I think this cannot be done do to the fact the PHP is a stateless language. But I will ask anyway...
I've created a home-grown MVC framework. The classes are all namespaced. On initial load I instantiate my custom Autoload class. A method of this class scans registered class directories (stored in the application config) and creates an array of fully qualified class names to paths. The array is stored as a protected member of this class. This member array is used by the spl_autoload() implementation. The Autoloader object is cached and reused on each http request.
This is fine and well. However, it irks me that every time an http request comes in to the domain my bootstrap routine has to register the autoload method to the __autoload stack. I would love to register it once and leave it alone.
I think the closest you can get to this would be opcode caching, where the parsing is done and machine language is built. Beyond that, you're right in that being a stateless language means it has to be built every time. I've never delved into it myself (so I'm not sure it would answer your question), but there is also pre-compiled PHP.
Related
Is it possible to define a separate PSR-0/4 path for classes/interfaces that can only be loaded by the package itself (not other packages that include it)?
The idea being it would stop other packages from mistakenly using classes that should be private to the package.
I realise that they can always include the file manually and the class loader is global. The goal is not to prevent them from using the class, but rather make it clear that they shouldn't be accessing it directly now or if the API of the package changes.
Use case:
Let say there is a Person class in the package (already released and used). When a new version of the package arrives we no longer want them to invoke new Person, but instead use a PersonFactory (as it has to setup some other stuff with the person that the caller does not need to worry about).
Yes, you could document this in the change log. However an IDE and static analysers would not be able to report on this. The bug would only be discovered when the improperly initialised Person is given to a provider and the program crashes or throws an exception at runtime.
Based on Alexander's response, this seems the most reasonable:
https://repl.it/repls/GrowlingInconsequentialFanworms
I can't see how this would be possible, because the autoloader loads them into their defined namespaces on their first usage. Once in a namespace, anything can use it by accessing that namespace.
PHP namespace to not have any way of limiting what other namespaces can access them, so your answer is probably no.
What you could do though is put your private classes in a namespace that tells developers they are private:
use yourpackage\private\SomeClass;
It won't stop them, but it could make it clear they should not do that.
I don't think it's possible. spl_autoload_register() takes only $class as argument. You can't register autoload only for a part of application. Moreover there is no way to know which class invokes or creates object of the other classes. Even if you wrote your own autoloader you won't get all information that you need.
If you look for securing your code look at PHP extensions.
Am not sure if this is what you talking about but the php keywords private and protected should help a bit when the classes and interfaces are in a namespace
I'm new to php and inherited a website project with hundreds of pages, all procedural (when I do a text search of the files, there isn't even a function definition anywhere). I'm coming from the c# and Java worlds. I'm looking for a way to incrementally add OOP. (They want me to update the front end and I am trying to convince them of fixing the backend at the same time and they don't want to use a framework (dammit)).
While looking into autoloader... Well, here's my understanding. It's a method of registering folders where classes are stored and when you instantiate a class, trait, etc. it searches the folder based on the class/filename/namespace and loads the appropriate definitions.
I have a few questions:
Does autoloader search the folder and load the appropriate definitions on every page lifecycle (or does it cache them)?
Pre-loading:
Is there a way to use autoloader, or some alternative, to pre-load ALL class definitions into memory and make them available across all sessions?
If so, when updating class files, how would I tell this mechanism to reload everything to memory when I make changes to class files?
UPDATE TO QUESTIONS:
Thank you both for your answers and it helps a little, but... I do have a bad habit of posing the wrong question(s) on StackOverflow.
The thing I want to avoid is slowing down pages by adding classes. So let's say I add a library and register the paths with autoloader. A page instanciates a class with multiple dependencies. Let's say that the dependency graph includes 15 files. For each request lifecycle, the server loads the page and 15 other files just on that one page.
Since I am coming from compiled languages, I feel a little strange not loading these classes into memory. All the classes together should not be over say 5MB.
(Or maybe I should just create a RAM Disk and copy all the files in there on boot and just have a symlink?)
Auto loaders in PHP are lazy. When PHP encounters a the use of a class it doesn't know about, it will ask the registered autoloader (or chain of autoloaders) to go find it. It's the autoloader's job to figure out where to get the file the class is defined in and include it. Having some sort of convention for naming your classes and organizing your class files is key to having a useful autoloader, and several conventions have arisen in the PHP community, such as PSR-4.
Does autoloader search the folder and load the appropriate definitions on every page lifecycle (or does it cache them)?
The autoloader(s) is(are) called on every request, but only when the need to autoload a class arises.
Pre-loading: Is there a way to use autoloader, or some alternative, to pre-load ALL class definitions into memory and make them available across all sessions?
I don't believe so, but as the number of classes grow, this becomes more and more wasteful.
Welcome to the wonderful[citation needed] world of legacy PHP, I highly recommend you check out Modernizing Legacy Applications In PHP. It's like a strategy guide for getting from Mordor back to the Shire.
I think you may misunderstand the purpose of autoloading. It is simply instructions on what to do when your code calls for a class that PHP doesn't recognize. That's it. The autoloader just calls requires /path/to/classfile so that PHP will see the class.
Does autoloader search the folder and load the appropriate definitions
on every page lifecycle (or does it cache them)?
There is no caching across requests, so if you make a change to file, the next http request will incorporate those changes. It's just as if you changed any other instruction in your script, for example change echo 1 to echo 2
Pre-loading: Is there a way to use autoloader, or some alternative, to
pre-load ALL class definitions into memory and make them available
across all sessions?
There is no need for this. A well written autoloader has instructions for where to find any class, so loading all possible classes ahead of time is wasteful. If you're still running into undefined classes errors, you need to either improve the autoloader or place the class files in accordance with the current autoloader instructions.
If you really want to preload all your classes, use the auto_prepend_file setting in php.ini. The docs say
Specifies the name of a file that is automatically parsed before the
main file
Set it to an initialization script. In that script have something like:
//put all your class files in this folder
$dir = '/path/to/classes/folder';
$handle = opendir($dir);
//require all PHP files from classes folder
while (false !== ($item = readdir($handle))){
$path = $dir.'/'.$item;
if(is_file($path) && pathinfo($path,PATHINFO_EXTENSION)==='php')
require_once $path;
}
This is simplified. There is significant risk in just including all files in any directory into your script so I would not do this. You would also need to adjust this if you want to include files in subdirectories.
Basically, don't do this. Just have a good autoloader.
No one posted what I was looking for but it seems the best route is the OptCache that's prebuilt into php 5.5 and above (my client is using 5.3 so I didn't know about it).
https://github.com/zendtech/ZendOptimizerPlus
The Zend OPcache
The Zend OPcache provides faster PHP execution through opcode caching
and optimization. It improves PHP performance by storing precompiled
script bytecode in the shared memory. This eliminates the stages of
reading code from the disk and compiling it on future access. In
addition, it applies a few bytecode optimization patterns that make
code execution faster.
Firstly, this is a bit of a long one, so thank you for reading.
My issue is similar to this one:
Class not found in the same file
I have a custom-built framework originally written in 2008 for PHP 5 and it's been upgraded over the years to work with PHP 5.3. I've been looking at 5.4 compatibility and have hit a serious issue.
The ORM layer automatically generates classes for each DB table. These classes all sit in one file per table and our autoloader loads that file when required.
For example, a 'customer' table in the 'public' schema (postgresql) would have the following classes:
PublicCustomer, PublicCustomerDBReader, PublicCustomerDBWriter.
Now this may not be the ideal set up, but it is what we currently have.
In PHP 5.3, if PublicCustomer was required, the file would be included, parsed and all of the above classes would become available. So if, for example, a static method is called on PublicCustomer, and that method calls something in PublicCustomerDBReader, that would work fine, since that class is in the same file.
In PHP 5.4, it looks like some optimisations have been done in the core. In the above scenario:
A static method gets called in PublicCustomer.
The autoloader finds and loads the correct file.
The PHP parser only parses up to where it needs; the PublicCustomer class. It has not parsed or instantiated the PublicCustomerDBReader class. I can confirm this by testing if the class exists and by seeing if the parser reaches the end of the file when it gets included, when the method is called (it doesn't).
The method in PublicCustomer then tries to call a method in PublicCustomerDBReader. This fails, since our autoloader has already required the file once.
It seems to me that I have two solutions:
Separate these classes out so that there is one file for each (this will produce a huge number of files)
Redesign the ORM layer so that multiple classes are not required.
Have I understood the issue above properly?
Does anyone know if an optimisation or change was made in PHP 5.4 that would cause this behaviour?
Are there any other potential solutions to the problem that I have not considered?
Place the reader/writer classes at the head of the file. Also you might consider filing a bug report, since the parser should only halt on errors.
Referring to the PHP Framework Interop Group and their autoloading standards PSR-0 and PSR-4 each class must have its own file with a name ending in .php. Even if you have thousands of classes, this should't be a problem for the file system.
With more than one class in a file you have to consider following aspects:
For each class the autoloader must decide which file to load. If you have multiple classes in a file, each used class causes a load. Class files should be loaded once and not more, because classes cannot be redeclared. I do not recommend it, but you could handle this with your own autoloader, which remembers loaded files or tests for loaded classes.
Classes placed within the same file and using each other is problematic. The problem is described in Derived class defined later in the same file “does not exist”? and answered by Jon.
So I have the following dilemma. I want to have a common library that all my ZF2 applications will use. This library will contain all the business logic for my website. Each application will consume different parts of the library to properly display/perform whatever actions are necessary. Now so far I've managed to create a library. Lets call it Foo. Foo has a Module.php which does the basic autoloading required to load the entire library.
Now here is where I start to have problems. I want to take advantage of dependency injection, the service manager, etc from ZF2 inside Foo. The problem is I only have the one Module.php that loads Foo. This means as my library grows so will Module.php since as far as I can tell I can't have sub modules. Is there any way around this issue?
Essentially I want every app to just include Foo and Foo to have several Module.php so that at least the dependency stuff can be handled on a module by module basis.
You're probably swimming against the current to try and do sub-modules -- and you probably don't need to.
If you've written your module nicely, loading it won't be a very expensive operation. Remember, the whole point of the service manager is that all those services are lazily created. So if the calling code never asks for a particular service in a particular request, that service's classfile is never autoloaded, the object is never instantiated, etc. So you may be fine staying with a big, monolithic, module.
The one place that things might get a little tricky is if you're leaning heavily on the EventManager, and your module is attaching a bunch listeners. But you can probably get around that by setting up some module configuration directives, and then just conditionally attach listeners.
Having said that, it probably makes sense to try to split your module up. So you could have FooBar and FooBaz modules.
If you really, really, want sub-modules, you can dig into the ModuleManager and try to figure it out. I went a little ways down that road once -- and then got distracted. In my case, I was dealing with shipping physical items. I wanted a "Fulfillment" module that could be configured to load a bunch of similar shipping modules (Fulfillment\Courier\USPSModule, Fulfillment\Courier\FedExModule, etc), so that my main Fulfillment module could iterate over all loaded submodules, without specific knowledge about any of them. If I recall correctly, the best way to do it was to essentially mirror what ZF2 does, but inside my Fulfillment\Module class. However, I can't think of many situations where you'd want to do that, unless you want a set of similar submodules that all implement the same interface, and want them to be consumed by a super-module that has no specific knowledge of them. I also looked at this because was thinking about runtime enabling/disabling of those submodules by end-users (sort of like a plugin system).
If you're not doing that, I'd say stick to FooBarModule, FooBazModule, etc, so far as it makes sense. And remember even if your module contains a ton of code, the ServiceManager will only autoload, parse, and instantiate classes that are needed to satisfy the dependencies of any given request.
From php.net:
In PHP 5, this is no longer necessary. You may define an __autoload() function which is automatically called in case you are trying to use a class/interface which hasn't been defined yet. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.
Now I am wanting to know, is it bad practice to solely use __autoload to load the appropriate classes on a dynamic site?
The way my site is setup is to include files into the index.php file, for example http://www.site.com/index.php?p=PAGE-I-WANT-TO-LOAD
So if I am on the forums section or the blogs section of my site, I want only appropriate classes and functions to be loaded, so I use autoload but I never include a file manually, should I be using __autoload as a last resort or is what I am doing fine even on a high traffic system?
Bad? No. __autoload() is one of my favorite additions to PHP 5. It removes the responsibility (and annoyance) of manually having to include/require the class files necessary to your application. That being said, it's up to you as the developer to ensure that only the 'appropriate classes' are loaded. This is easily done with a structured naming scheme and directory structure. There are plenty examples online of how to properly use __autoload(), do a Google search and you'll find plenty of information.
Autoload is a good way to load only what classes is needed.
In PHP 5 >= 5.1.2, most of the problems with the old __autoload() dissapeared, thanks to spl_autoload_register().
Now I am wanting to know, is it bad practice to solely use __autoload to load the appropriate classes on a dynamic site?
Not at all. You can rely on autoload, all you need to do is to devise a good naming convention and implement an efficient autoloader.
There is one major issue to consider. Autoloading and Zend Guard do not play well together, because Zend Guard tends to rename things, which will mean that the naming convention you decided to use will most likely not be the same. If you will be using Zend Guard (or any other obfuscator for that matter) you will most likely be forced to include all the files by hand.
Here is a quote from the Zend Guard user guide:
Autoloading classes will not work since the filename on the disk would not
match the obfuscated class name.
The only danger to __autoload() is if you define a poor autoloading function. Generally, all you're going to get in terms of a performance hit is a few disk seeks as PHP looks for the right files that contain your classes. The upside is getting rid of all those annoying include() calls.
If you're worried about performance at this level, then you should already be using an opcode cache such as APC.