Python __name__ == __main__ in PHP

Tweet

One of those nice features of Python, that marks it as a language designed for developers (as opposed to one designed for tool vendors) is the ability to conditionally run some logic if you’re executing a script directly but ignore it if the script is being imported (“included”) by another script.

For example;


class User:
def setName(self, name):
self.name = name
def getName(self):
return self.name

# Only execute this we're "main"...
if __name__ == "__main__":
u = User()
u.setName("Harry")
print u.getName()

The code after the ‘if __name__ == “__main__”:’ is only executed is I run this script directly. If I import the User class from another script it is ignored.

Makes a very handy tool for testing (or better yet unit testing) a Python script. So much so I want it in PHP.

One trick to do so is like this;


class User {
var $name;
function setName($name) {
$this->name = $name;
}
function getName() {
return $this->name;
}
}

if ( realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']) ) {
$u = & new User();
$u->setName("Harry");
print $u->getName();
}
?>

This relies on the predefined constant __FILE__ which gets populated with the name of script where __FILE__ is accessed (i.e. it works irrespective of includes). The value of __FILE__ should be unique to the script, as far as I know.

There’s two problems here though. First if you use this approach alot, there’s going to tons of calls to realpath(), meaning a performance hit. Also $_SERVER['SCRIPT_FILENAME'] returns the name of the PHP executable when using PHP via CGI and otherwise probably can’t be relied upon on web servers other than Apache.

Another approach would be to conditionally define a constant using the value of __FILE__ e.g.;


class User {
// ....
}

if ( !defined('__MAIN__') ) {
define ('__MAIN__',__FILE__);
}
if ( __FILE__ == __MAIN__ ) {
$u = & new User();
$u->setName("Harry");
print $u->getName();
}

If the constant __MAIN__ hasn’t already been defined (by a script which included this one), it gets assigned the value of __FILE__ and the conditional code block is executed.

This approach is probably most trustworthy in that it should work on any PHP install but requires additional work and if you forget to define __MAIN__ somewhere you may end up with code being executed that you weren’t expecting.

Note that the following;


@define ('__MAIN__',__FILE__);

The @ operator, used to suppress the error notice if __MAIN__ was already defined, is slower than using is_defined().

The third option, which I’m not sure I 100% trust to work correctly everywhere, is to use get_included_files() like;


class User {
// ...
}

if ( __FILE__ == array_shift(get_included_files()) ) {
$u = & new User();
$u->setName("Harry");
print $u->getName();
}

This is based on the assumptions that the file where execution began will always be the first element in the list returned by get_included_files() (which, as far as I know, it should be) and that it will match the value of __FILE__. So long as this script wasn’t included (or required) by any other, the code inside the if condition is executed.

Of course the overall approach comes cost of parsing the code inside the condition, which can only be avoided using a OPCODE cache like the Zend Accelerator but it makes a useful tool when you’re building modular code and want to develop and test “modules” in your app without running the entire application.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://gosu.pl cagrET

    There is an another solution:


    if (basename(__FILE__) == basename($_SERVER['PHP_SELF'])) {
    // ...
    }

    It only requires that the included file has an unique name.

  • http://gosu.pl cagrET

    And another one:


    if (count(debug_backtrace()) == 0) {
    echo 'SELF';
    }

    But I didn’t do any perfomance tests with debug_backtrace() so don’t know if it is not to slow.

  • http://www.phppatterns.com HarryF


    if (count(debug_backtrace()) == 0) {

    Nice one! Think that wins the lateral thought of the day ;)

    Running it through XDebug, compared to the alterative;


    if ( __FILE__ == array_shift(get_included_files()) ) {

    The results looks like;


    Total Time Avg. Time #Calls Function Name
    0.1426506042 0.0000014265 100000 count
    0.1460983753 0.0000014610 100000 debug_backtrace

    vs.


    Total Time Avg. Time #Calls Function Name
    0.1719875336 0.0000017199 100000 array_shift
    0.1729249954 0.0000017292 100000 get_included_files

    Looks like debug_backtrace is the winner.

  • gerzson

    I think is_defined() should be a typo only, but I report it.
    The name of the desired function is defined().

  • http://www.ajohnstone.com Andrew-J2000

    heh, Harry, we think alike.

    I emailed, dmitry (The developer of the Perl extension) earlier, about implementing Python and I just found your post :p. Also a comment on zend refered to this as ‘bloatware’, which didn’t quite make sense considering its not bundled with PHP. http://www.zend.com/zend/comments/show_comment.php?article=php5-perl&id=4887&pid=4751&days=10000&f_id=php5-perl&mode=&kind=php5

  • http://www.ajohnstone.com Andrew-J2000

    What are your comments about the perl/python extension being ‘bloatware’?

  • http://www.phppatterns.com HarryF

    What are your comments about the perl/python extension being ‘bloatware’?

    Well the comment “Completly useless… just bloatware” doesn’t really give anything to argue against, accept to say “No it isn’t”. Seems more like random spam.

    To date I’ve never played with the Python or Perl extensions.

    Where the subject of “bloat” might need examining is when it comes to the performance overhead of loading / executing external Perl/Python code.

    But there is most definately a value in being able to execute Perl / Python code in PHP. Just head to CPAN and you have your answer. Python also has some excellent libraries.

    Also that this has got Dmitry’s name on it suggests to me it’s a serious project, Dmitry being the original author of Turck MMCache and now the SOAP extension.

  • Pingback: SitePoint Blogs » PHP Server API Differences