Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm using a great little open-source product called Restler to provide a RESTful API to a Wordpress application. In order for this to work I want to be able to load the Wordpress environment within a set of PHP classes that Restler will run when a request comes in.

The idea of loading the WP environment -- without the load of the UI elements -- is achieved by a script I have called AddWordpress.php:

<?php
error_reporting(E_ALL);
$site_name='yoursite';
$site_domain='www.yoursite.com';

/**
* Construct a fake $_SERVER global to get WordPress to load a specific site.
* This avoids alot of messing about with switch_to_blog() and all its pitfalls.
*/
$_SERVER=array(
'HTTP_HOST'=>$site_domain,
'REQUEST_METHOD'=>'GET',
'REQUEST_URI'=>"/{$site_name}/",
'SERVER_NAME'=>$site_domain,
);

// Remove all our bespoke variables as they'll be in scope as globals and could affect WordPress
unset($site_name,$site_domain);

// Pretend that we're executing an AJAX process. This should help WordPress not load all of the things.
define('DOING_AJAX',true);

// Stop WordPress doing any of its normal output handling.
define('WP_USE_THEMES',false);

// turn on debugging
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);

// Load WordPress - intentionally using an absolute URL due to issues with relative paths on the CLI.
include "/[path-to-root-www]/wp-load.php";

This works like a charm. I can now run scripts like this from the command line (php test.php where below script is test.php):

<?php
include "AddWordpress.php";
$terms = get_terms("actions");
echo json_encode($terms);

The problem I'm having is that if I want to include the "AddWordpress.php" not at the top of the file but inside of a method call -- I know this sounds like a terribly bad idea but there are good reasons for it ... please believe me -- then all shit breaks loose and my day turns badly very quickly. More specifically, the test file now looks like this:

<?php
function do_that_wordpress_thing() {
    include "AddWordpress.php";
}
do_that_wordpress_thing();
$terms = get_terms("actions");
echo json_encode($terms);

and the errors I get are as follows:

Notice:  is_404 was called incorrectly. Conditional query tags do not work before the query is run. Before then, they always return false. Please see Debugging in WordPress for more information. (This message was added in version 3.1.) in /[path-to-www-root]/wp-includes/functions.php on line 2944
Notice:  is_home was called incorrectly. Conditional query tags do not work before the query is run. Before then, they always return false. Please see Debugging in WordPress for more information. (This message was added in version 3.1.) in /[path-to-www-root]/wp-includes/functions.php on line 2944
Notice:  is_search was called incorrectly. Conditional query tags do not work before the query is run. Before then, they always return false. Please see Debugging in WordPress for more information. (This message was added in version 3.1.) in /[path-to-www-root]/wp-includes/functions.php on line 2944
Notice:  is_archive was called incorrectly. Conditional query tags do not work before the query is run. Before then, they always return false. Please see Debugging in WordPress for more information. (This message was added in version 3.1.) in /[path-to-www-root]/wp-includes/functions.php on line 2944

... etc ...

* UPDATE * On thing I picked up on PHP.net is this snippet on the include function def:

If the include occurs inside a function within the calling file, then all of the code contained in the called file will behave as though it had been defined inside that function. So, it will follow the variable scope of that function. An exception to this rule are magic constants which are evaluated by the parser before the include occurs.

I think this must have an important role in why this isn't working but I'm still hopelessly lost. Can anyone explain why this could be happening? Any help would be VERY much appreciated.

share|improve this question
2  
+1 interesting question. Some question upfront: Are you shure it's a RESTful service and not just a web facing raw data interface? Other notes: Have you looked at how other plugins are doing it? Have you considered doing a plugin to wrap things up and easier integrate custom with WP stuff? Why don't you use the XML RPC interface? –  kaiser Jan 28 '13 at 14:03
    
I think you probably have to load all the globals, it's messy, either manually in code or using extract($GLOBALS); –  Wyck Jan 28 '13 at 16:07
    
By the order of the notices they come from WP::handle_404() which is called from WP::main() which is something you probably don't want to run at all. –  Mark Kaplun Jan 28 '13 at 16:52
    
@MarkKaplun, I've run a stacktrace on the errors and they terminate in the query.php function in wp-includes and are called by my theme's library. That said, I think the problem isn't solving this call but understanding why the variation in state exists between the import statement when it's outside of a function call versus inside it. –  ken Jan 28 '13 at 17:48
1  
I'm not 100% sure, last night I created a build file that interfaces with WordPress from outside WordPress and the only options to get it working was to include all the globals, you can see a poc here: gist.github.com/5e92ed2c9a6aca8c33db also more info alexking.org/blog/2012/05/27/carrington-core-and-globals, and have a look at how the wp-cli works, it basically loads it own wp-settings: github.com/wp-cli/wp-cli –  Wyck Jan 28 '13 at 19:26

1 Answer 1

up vote 2 down vote accepted

The credit for this answer really goes to @Wyck who correctly identified the source of the problem. Thank you very much. The reason for the problem is:

There is a variance between an import statement in the mainline code versus within a function. In WP or in in any of the plugins if variables aren't explicitly set to global then they become locally scoped to the function they are being imported into.

It's a shame there's no import directive to explicitly direct variables to a particular namespace but in absence of this you can simply claim all the global variables as being "global" in the function. Using the same demonstration file from my original problem statement, this now works:

<?php
function do_that_wordpress_thing() {
    global $domain, $path, $base, $admin_page_hooks, $ajax_results, $all_links, $allowedposttags, $allowedtags, $authordata, $bgcolor, $cache_categories, $cache_lastcommentmodified, $cache_lastpostdate, $cache_lastpostmodified, $cache_userdata, $category_cache, $class, $comment, $comment_cache, $comment_count_cache, $commentdata, $current_user, $day, $debug, $descriptions, $error, $feeds, $id, $is_apache, $is_IIS, $is_macIE, $is_winIE, $l10n, $locale, $link, $m, $map, $max_num_pages, $menu, $mode, $month, $month_abbrev, $monthnum, $more, $multipage, $names, $newday, $numpages, $page, $page_cache, $paged, $pagenow, $pages, $parent_file, $preview, $previousday, $previousweekday, $plugin_page, $post, $post_cache, $post_default_category, $post_default_title, $post_meta_cache, $postc, $postdata, $posts, $posts_per_page, $previousday, $request, $result, $richedit, $single, $submenu, $table_prefix, $targets, $timedifference, $timestart, $timeend, $updated_timestamp, $urls, $user_ID, $user_email, $user_identity, $user_level, $user_login, $user_pass_md5, $user_url, $weekday, $weekday_abbrev, $weekday_initial, $withcomments, $wp, $wp_broken_themes, $wp_db_version, $wp_did_header, $wp_did_template_redirect, $wp_file_description, $wp_filter, $wp_importers, $wp_plugins, $wp_taxonomies, $wp_the_query, $wp_themes, $wp_object_cache, $wp_query, $wp_queries, $wp_rewrite, $wp_roles, $wp_similiesreplace, $wp_smiliessearch, $wp_version, $wpcommentspopupfile, $wpcommentsjavascript, $wpdb;
    global $load_sections; // for pagelines theme
    include "AddWordpress.php";
}
do_that_wordpress_thing();
$terms = get_terms("actions"); // I have a custom taxonomy named "actions"
echo json_encode($terms);

It's also worth noting that if use autoloaders in your plugin; you should make them namespace compliant. For instance, instead of:

spl_autoload_register (  'autoloader_function_name' );

use this:

spl_autoload_register (  __NAMESPACE__ . '\\autoloader_function_name' );
share|improve this answer
    
I am going to look a little more into namespacing and see if there isn't a more global scoping directive that could be used. –  ken Jan 28 '13 at 20:27
    
Keep in mind I just ripped those globals to test it out, there are a lot more in WordPress and not sure how updated this list is, it's quite confusing how they are generally used and which ones need to be included. I ripped WP through PHPDocumnter 2 for Global namespaces and come up with this, wycks.github.com/WordDoc/namespaces/global.html. –  Wyck Jan 29 '13 at 6:24
    
Yes this is the problem with the whitelisting approach. I'm still secretly hoping to find an innovative way to default it to the global namespace but until that day this list is a good start and if you have any further exceptions you can hopefully pick them off pretty quickly. For me it was just adding one that my theme needed and it seems to work in all the use-cases I need. –  ken Jan 29 '13 at 7:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.