-1

In my custom plugin's php, trying to call the core wordpress function username_exists() throws a 500 error which I think is caused by that function not being defined.

The line in my code that is failing is:

$unres = username_exists($unt);

I have verified that it is not a problem caused by a null argument, as the following works as expected: $unres = $unt;

How do I fix this?

I tried all the solutions throughout the answers and comments at all of the following:

'username_exists' function not working in Wordpress plugin

Function username_exists() can't be accessed without logging in to wordpress

WordPress plugin admin page - using WordPress function in linked php file

https://wordpress.stackexchange.com/questions/20915/wordpress-plugin-admin-page-using-wordpress-function-in-linked-php-file

How do I call the basic WordPress header files?

wordpress plugin -> Call to undefined function wp_get_current_user()

I have successfully added the following php to include/require these files (but this did not help):

require realpath("../../../../../wp-includes/pluggable.php");
require realpath("../../../../../wp-includes/user.php");
require realpath("../../../../../wp-admin/includes/user.php");

If I include or require the following, it creates a fatal site error:

require realpath("../../../../../wp-load.php");

Is there now some other core file that I need to 'include' in my php by reference, other than those outlined above (for example, due to new WP versions since those questions were written)? (I'm on WP v5.5).

Do I need to contextualise the call? (e.g. reference namespace, call as public/global, etc?)

(Note: the site also uses the Ultimate Member plugin, not sure if that would override the core function name in any way?)

Thanks.

ed2
  • 1,457
  • 1
  • 9
  • 26
  • 2
    If you open the wp-includes/user.php file, is the function declare there within? This function got added in 4.9.0 – PatricNox Aug 24 '20 at 14:13
  • @PatricNox Yes, the function is in that file. (Running WP v5.5) – ed2 Aug 24 '20 at 14:16
  • 1
    _"Is there a new version of what to include"_ - If it is a WP function, then it would be if there is a new version of WP. You can't just swap out some files. That's a good way of breaking things. – M. Eriksson Aug 24 '20 at 14:17
  • what does the parentheses around the function do? never seen it like that before? is there a reason for them? – Stender Aug 24 '20 at 14:18
  • @MagnusEriksson Sorry, I will reword that sentence in the question for clarity. I meant "the previous similar questions did not help, so is there now a more up to date requirement of 'which core file to require in the custom php script'". – ed2 Aug 24 '20 at 14:20
  • @Stender They are to enclose the args passed to the function, following syntax from https://developer.wordpress.org/reference/functions/username_exists/ – ed2 Aug 24 '20 at 14:22
  • 2
    @Stender - They're not doing anything in that context. You can write `(foo())` or just `foo()`. It will result in the same thing so they aren't needed. – M. Eriksson Aug 24 '20 at 14:23
  • no - you have parentheses AROUND the function and then again around the arguments. there is no syntax in your link, that shows them around a function name :) – Stender Aug 24 '20 at 14:23
  • @MagnusEriksson, okay - so basically pointless in this example. I got it, thank you. – Stender Aug 24 '20 at 14:25
  • @Stender Oh, sorry, now I see what you mean. I get the same behavior with or without these. I will edit the question to omit them. – ed2 Aug 24 '20 at 14:26
  • @Stender And thanks - now 2 less chars in the never ending code golfing quest... – ed2 Aug 24 '20 at 14:27
  • When are you calling this? Is it right away in your plugin or in an action later on? – Chris Haas Aug 24 '20 at 18:03
  • @ChrisHaas It is not upon plugin init or load, it is in a PHP file that is called by the frontend JS. There is no issue with the other lines in that PHP file, if the line in question is commented out. Does this answer your question? – ed2 Aug 24 '20 at 18:53
  • 2
    Okay, that helps. I encourage you to not use raw PHP code and instead follow WordPress plugin patterns. If you want code that can be hit by JS just register a [REST route](https://developer.wordpress.org/rest-api/). But if you really want to go down the path you are going down (and I've done this before, too), then you do need to first boot WordPress using wp-load.php. You could also try using `SHORTINIT` for a small perf bonus but it usually isn't worth it. As for the error, turn on full error reporting in PHP and post that. – Chris Haas Aug 24 '20 at 20:32
  • Ok, thanks @ChrisHaas. I have a custom plugin for some PHP operations (including interfacing with 3rd party APIs), so was planning to include another PHP file within that plugin, that the JS can call. Is this what you call "follow Wordpress plugin patterns"? I would prefer to drill as few holes in the WP setup as possible- no point me being a hero or reinventing the wheel. I am open to any way that my JS can initiate the question "does this form field input represent an existing username?" and get an answer. If that can be done without my own PHP, even better! I will look into REST routes too. – ed2 Aug 25 '20 at 10:56
  • @ed2, I added an answer here elaborating further along with some code that should get you everything you need to run that specific function – Chris Haas Aug 25 '20 at 13:09

1 Answers1

1

Instead of running raw PHP code, it is really a best practice to run your code in the context of WordPress. WordPress has many APIs available and for a JavaScript-based call the REST is probably the best choice.

Below is really simple code that registers a REST route and tests the supplied parameter against the core username_exists function. I've included inline comments which should explain everything, but once you remove those and collapse some whitespace, you'll see it is only 20 lines or so of code.

// Always register routes in this action
add_action(
    'rest_api_init',

    // My IDE is happiest when I make my functions static, but that is not a requirement
    static function () {

        register_rest_route(

        // The shared namespace for all route in this plugin
            'ed2/v1',

            // The path with optional parameters for this specific route
            '/username/(?P<username>.*?)',
            [
                // We only accept GET, but could be POST or an array of methods
                'methods' => 'GET',

                // The function to run
                'callback' => static function (WP_REST_Request $request) {

                    // Our parameter was named above so we can fetch it here 
                    $username = $request->get_param('username');

                    // Return an array, the API will handle turning it into a JSON response
                    return [
                        'username' => $username,
                        'exists' => username_exists($username),
                    ];
                },
            ]
        );
    }
);

In your browser, you should now be able to go to http://example.com/wp-json/ed2/v1/username/test and it should return (assuming you don't have a username of test):

{"username":"test","exists":false}

If you change to a username that does exist, such as https://example.com/wp-json/ed2/v1/username/user@example.com you should get:

{"username":"user@example.com","exists":1}

To restate what I said at the beginning, I differentiate between PHP code calling WordPress functions which generally requires "waking WordPress up", and WordPress code calling PHP functions which means that WordPress is "already awake". You can run PHP code that boots WordPress by including wp-load.php, but you also shouldn't be surprised if certain things don't work. What those things are that could break really depends on your environment.

Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • Thanks @Chris Haas, I will try it and report back. It sounds like you say WP calling PHP beats PHP calling WP - is that right? This is all triggered by some page JS containing a variable holding a new username that it needs to check if already exists. Whether that is done through a JS function, wordpress PHP function or via custom PHP files, I will follow whatever is best. My default solution was to have the JS call a custom PHP file that is inside a custom plugin, and that PHP calls the inbuilt username_exists function. If that is not best practice I am keen to change to best practice. – ed2 Aug 26 '20 at 02:53
  • Regardless of whether code lives in a plugin folder or not, if WordPress doesn't call the code, I would say that you don't have a plugin (at least that specific block of code). It is splitting hairs to a degree, however all WordPress documentation and community support assumes that plugin code is invoked by WordPress and anything else is "you're on your own". In that regard, WP calling PHP "beats" the other way. Technically, the other way might be marginally faster but you probably really wouldn't notice the difference. – Chris Haas Aug 26 '20 at 13:28
  • Thanks. "It is splitting hairs to a degree", well, it's still important to me! I have no idea how to use my JS to say either "hey WP, does this proposed username already exist" or "hey WP, please call the following function/plugin/PHPfile to find out", but I presume the former is not possible(?) and the latter is best done by the above answer which I am yet to test. – ed2 Aug 26 '20 at 13:38
  • 1
    The above code is really for the former, actually. It is "WP, does this username exist?" using 100% official API and code, and is a higher level question. You could install a third-party plugin that replaced WP's authentication system and (in theory) that code would still work, too. Basically, as long as you surface your code through official WP APIs like REST, you are going down the safe path. Your code is still obviously PHP, it is just passed through WP's system first. If, however, you find yourself calling a specific PHP file from JS, that's where you are leaving WP's safety barrier. – Chris Haas Aug 26 '20 at 17:00
  • Got it, that clarifies it for me perfectly, thanks @Chris Haas. I will report back once I have tested your example. – ed2 Aug 26 '20 at 17:22
  • Thanks @ChrisHaas I have not forgotten to test this solution and am first diverging into remapping my existing custom functions (see https://stackoverflow.com/questions/64327204/where-to-register-rest-route-to-wordpress-plugin-endpoint and https://stackoverflow.com/questions/64274261/registering-a-wordpress-rest-route-and-endpoint-for-a-function-stored-in-a-custo) – ed2 Oct 13 '20 at 01:49
  • Hi @ChrisHaas that worked well. The result for "exists" is either "false" or an integer representing the UUID of the user. Thanks! – ed2 Oct 30 '20 at 02:11