Re: Making PEAR2 Portable

From: Date: Tue, 11 Sep 2007 22:02:02 +0000
Subject: Re: Making PEAR2 Portable
References: 1 2 3  Groups: php.pear.dev 
Request: Send a blank email to pear-dev+get-48004@lists.php.net to get a copy of this message
David Morse wrote: > Greg: > > Thanks for your response. Thanks also for your seemingly infinite > patience for explaining and defending the PEAR2 proposal. After > rereading your reply, and some of this semi-infinite thread, I think I > finally get the point: Removing "require_once" from the source code > separates the problem of handling file dependencies from the code. As > a result, it becomes possible to construct multiple ways of handling > dependencies for different contexts, without modifying PEAR2 class > source files. I'm convinced that this separation is simply a matter of > good design. Right, this is one of the goals. > > Okay, got it. I hadn't understood that autoload was irreparably slow. > > Autoload implementation > ----------------------- > >>> Perhaps I'm missing something, but don't see the point of having >>> the PEAR2_Autoload() function try to define the magic __autoload() >>> function internally, as done in the current svn implementation. >>> If the PEAR2_Autoload function will normally be called only from >>> within an __autoload() function, then an __autoload() function must >>> necessarily be defined before PEAR2_Autoload() is called. >> >> >> The point is to provide something with which absolute beginners can add >> 1 line of code to their apps and simply start using PEAR2 packages. >> > > Sorry, but I'm still missing something. It seems to me that the > minimal addition to user code is actually two lines: > > include 'pathto/PEAR2/Autoload.php' > function __autoload($class) { return PEAR2_Autoload($class); } PEAR2/Autoload.php (which is by the way in its own svn module now http://svn.pear.php.net/wsvn/PEARSVN/Autoload/trunk/src/Autoload.php?op=file) creates __autoload or spl_autoload_registers PEAR2_Autoload: <?php |// set up __autoload if (function_exists('spl_autoload_register')) { if (!($_____t = spl_autoload_functions()) || !in_array('PEAR2_Autoload', spl_autoload_functions())) { spl_autoload_register('PEAR2_Autoload'); if (function_exists('__autoload') && ($_____t === false)) { // __autoload() was being used, but now would be ignored, add // it to the autoload stack spl_autoload_register('__autoload'); } } unset($_____t); } else { function __autoload($class) { return PEAR2_Autoload($class); } } ?> Next, it sets up include_path and cleans up after itself: <?php ||// set up include_path if it doesn't register our current location $____paths = explode(PATH_SEPARATOR, get_include_path()); $____found = false; foreach ($____paths as $____path) { if ($____path == dirname(dirname(__FILE__))) { $____found = true; break; } } if (!$____found) { set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(__FILE__))); } unset($____paths); unset($____path); unset($____found); | ------------------------------------------------------------------------ ?> > A two line addition is fine, and preferable to my proposal to put the > second line in a separate file. My confusion arises from the fact > that the only way the PEAR2:: Autoload function will ever be called is > if it is called internally by a user-defined __autoload function, or > registered by spl_autoload. That's why it seemed to me that is is > pointless to try to redefine __autoload within PEAR2::Autoload, or > have the PEAR2::Autoload function register itself with spl_autoload -- > the fact that the PEAR2::Autoload function was called means that the > php interpreter already knows to use it as the autoload function. This > has to be set up correctly outside the function. What am I getting wrong? The setup is done inside PEAR2/Autoload.php to keep things simple for folks. >>> The current PEAR2_Autoload makes no assumptions about include_path, but >>> in fact sets it up if it isn't there, perhaps you're looking at an >>> outdated version? > > It still seems to me that the current autoload implementation > depends on include_path being set correctly, in the following sense: > The path that it uses to try to find a class file is simply > $filename = str_replace('_', '/', $class) . '.php'. This path > will > allow the file to be opened only if the include path contains the > PEAR2 root directory. If the attempt to open this file fails, it looks > like the function throws an exception, and thus will never gets to the > code where it tries to fix the include path. Ah - here is an important distinction to understand. PHP does not "execute" all of the code at the same time. First, it parses the file to detect the structure and create an opcode array. Then, it runs the file, so in actuality, this is the execution order when you include PEAR2_Autoload as in this example: file.php: <?php include '/path/to/PEAR2/Autoload.php'; ?> 1) parse the include 2) parse /path/toPEAR2/Autoload.php 3) detect PEAR2_Autoload function 4) run the PHP code in file.php 5) run the PHP code in /path/to/PEAR2/Autoload.php 6) setup autoload 7) setup include_path 8) return to file.php Note that the execution changes if you use run-time include: file2.php: <?php include dirname(__FILE__) . '/PEAR2/Autoload.php'; function __autoload($class) { return false; } ?> Now the execution is: 1) parse file2.php 2) notice __autoload() 3) run the PHP code in file2.php 4) resolve dirname(__FILE__) . '/PEAR2/Autoload.php' to /path/to/PEAR2/Autoload.php 5) parse /path/to/PEAR2/Autoload.php 6) detect PEAR2_Autoload function 7) run the PHP code in /path/to/PEAR2/Autoload.php 8) setup autoload (notice that __autoload exists, use spl_autoload_register) 9) setup include_path 10) return to file2.php > Loading Scripts > --------------- > > Regarding the other proposed method, in which the PEAR developer > writes a loading script, I'd like to clarify what a standard > as-distributed script would look like. You'd like these scripts to > work correctly when a user loads multiple PEAR packages, which may > have overlapping dependencies. The easiest way to resolve the problem > of overlapping dependencies would be to have the script for loading a > package be a series of 'require_once' statements that load all of the > files required by that package. Is this what you have in mind? If so, > I agree that this would still be an improvement over the current > situation. The dreaded 'require_once' statements would have been moved > out of the class source files, and could be written to avoid checks > that would be redundant even for the use of a single package. They > would also be easier to locate and analyze, allowing those with a need > for speed to easily combine scripts for several packages and remove > redundant includes. Is this the concept? > Or is the current proposal to provide only a standard autoload > implementation and a list of file dependencies for each package, and > let users write their own loading scripts from scratch? The current proposal is to provide only a standard autoload implementation and a list of files within a package, and a list of *package* dependencies for each package. Users write their own loading scripts. However, this need not happen from scratch, for instance, this short script could be put at the top of the application in order to create the loader file based on actual execution: <?php function get_included_thingies() { $included = ''; foreach (get_included_files() as $i => $file) { if (!$i) continue; $included .= 'include \'' . $file . "';\n"; } file_put_contents('/tmp/loaderscript.php', $included); } register_shutdown_function('get_included_thingies'); ?> Also useful would be Gopal's inclued extension, which can do a similar thing, but this is not yet publicly available. Greg

« previous php.pear.dev (#48004) next »