Loading PHP classes by just putting files into PSR-4 directory - php

In my current lib, I have classes with static code outside the class definition, that I would like to execute when browsing a index.php file that has autoloading (with properly putting the class files into a PSR-4 folder structure, and calling composer install before).
It's not such a bad deal. For instance, in my custom Error.php class I could for instance call set_error_handler function outside the class so warnings could be catchable. And putting this file in a PSR-4 autoloading could ease the pain for not having to call any Error.php code in index.php to enable this catching. Every source that just uses my namespace and autoloads my lib would have that for granted.
I tried to include use \MyNamespace\Error; in the index.php file, but the code in Error.php, outside the Error class definition, isn't automatically executed.
The code outside the class is only executed when I call a class method inside my index.php file (the one that has the autoloading).
Can this be done ? Thanks for your time.

use \MyNamespace\Error; does not trigger autoloading, it just allows you to use shorter class name in code - new Error() instead of new \MyNamespace\Error(). If you want to include Error.php file, you need to use this class. Probably the safest way would to use class_exist():
class_exists(Error::class);
But honestly you should rethink your design, implicit registering error handler in file with class declaration is against PSR-1 and may be really annoying in big project.
Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both
https://www.php-fig.org/psr/psr-1/#23-side-effects
It would be less magical if you create separate method for registering error handler and call it explicitly in index.php:
Error::registerErrorHandler();

Related

PHP: spl_autoload_register attempting to load files from wrong directory

I am trying to implement autoloading of my classes. I separate my classes into two different folders:
/classes
/external
In my external folder, I keep everything third-party that I am using within my application. It may include classes, functions, etc. I like to keep that separate from my own code.
I want to include everything in the external directory manually. I only want files in the classes directory to be autoloaded. Here is what I have:
Files:
/classes/cache.class.php
/classes/db.class.php
/classes.mail.class.php
/external/hooks.php
And to autoload:
spl_autoload_register(function ($class_name) {
include dirname(__FILE__).'/classes/'.$class_name.'.class.php';
});
$Cache = new cache();
$Db = new db();
$Mail = new mail();
Somehow, this is attempting to load any class that exists in the external directory. I know this because in hooks.php I have:
class Hooks { ... } // Notice the capital H
And I am getting the error:
"Warning: include(/path/to/classes/Hooks.class.php): failed to open stream: No such file or directory"
How can I get spl_autoload_register to ignore all classes that exist outside of the classes directory?
The autoloader has no way of knowing in which folder a class is defined. The general flow is:
See an undefined class being used in the code being executed
Call all the functions registered with the spl_autoloader, and pass them the undefined class name
Since you didn't have any check for if the file exists, the autoloader function you registered is erroring out whenever you fail to include a non-autoloaded class before it's used. As #Forbs mentioned in his answer, just add a check in your autoloader function to see if the file exists before including the source file.
It's worth noting that Composer does this job for you very well already - unless you have some specific reason for wanting to use your own autoloader, I'd suggest just setting up composer for your project.
https://getcomposer.org/doc/00-intro.md
add this
if (file_exists(dirname(__FILE__).'/classes/.$class_name.'.class.php'))
right before your include

How does CakePHP connect files in different directories?

This is probably very trivial but because I'm new I just don't get it.
I can see the notion of App::uses at the beginning of every file but how does the file know where App is?
There are no includes anywhere and to my understanding there is one autoloader somewhere in lib, does that mean that one autoloader in one file is responsible for loading all the classes (if instantiated)?
I tried reading this part of the manual but still failed to understand how it works.
Also read some material on the spl_autoload_register function itself but no avail.
I'd really appreciate it if someone could help me understand how the files communicate with eachother.
Look at App::load(), it's doc block explains it:
Method to handle the automatic class loading. It will look for each
class' package defined using App::uses() and with this information it
will resolve the package name to a full path to load the class from.
File name for each class should follow the class name. For instance,
if a class is name MyCustomClass the file name should be
MyCustomClass.php.
And look at the frameworks core file lib/Cake/basics.php:
spl_autoload_register(array('App', 'load'));
It registers that method as auto loader.

How to include external PHP classes in Joomla

I have few PHP classes and files that I want to include in Joomla so that I can call those classes. I tried doing require_once config.php to include a PHP file which also includes all the classes that I would want to use in Joomla.
But each time I execute the page I receive the error below:
Fatal error: Class 'SomeClassName' not found
is there any other way to include external PHP classes or files in Joomla?
Thanks in advance!
Please use Joomla autoloader. Is better.
<?php
// Register an adhoc class.
JLoader::register('AdhocClass', '/the/path/adhoc.php');
// Register a custom class to override as core class.
// This must be done before the core class is loaded.
JLoader::register('JDatabase', '/custom/path/database_driver.php', true);
Edit:
Load classes with autoload instead of require/include have a better performance, because PHP will only read (require access to the disk) and compile (require memory and CPU usage) if you really use your class.
To do the same with require/include you have to be sure to only use if will really use the class.
Source:
http://developer.joomla.org/manual/ch01s04.html
require_once should work just fine within Joomla. Make sure the class you want to use is really loaded within your file, and the file is properly referenced in the require_once. Something is going wrong there and it has nothing to do with Joomla itself :-)

PHP's "use" keyword not properly including

I am currently refactoring a project which has been halfheartedly ported to Yii. There are some classes in the components folder which are included in a controller with the PHP "use"-keyword. This gives me a "include(protected/components/classes/SClass.php): failed to open stream: No such file or directory" error though.
What's really strange about it is, that changing the name (used by "use") to a non-existent file gives me a fatal error. Any ideas?
The use keyword by itself in PHP does not do any including of other files. It merely tells PHP that the namespace defined in the use statement may be referenced by code further down in the current PHP file.
However, what is likely happening here is that your system has an autoload function defined. If there is an autoload function, PHP will call this function whenever it encounters a class name that it doesn't recognise. The autoload function searches for the class file to load and includes it if it can find it. This is probably where your errors are occurring.
In the first case, this is the sequence of events:
the use statement is referencing a valid namespace, but this is ignored until a class in that namespace is referenced in the code.
When the class is referenced, PHP says "I don't know this class yet, lets autoload it".
The autoloader function is run, and builds a path to include. This usually comes from the namespace and classname of the class, but can be whatever the autoload function is written to expect.
In this case, it sounds like the autoloader is building the path and running include() on that path, but the class doesn't exist where the autoloader expects. Hence the "file not found" error.
In the second case, where you change the use statement:
The use statement now references a different namespace, but you probably haven't changed the code later in the program that actually references the class.
The program reaches the code that calls the class, but it doesn't recognise the namespace because it doesn't match the use statement any more, so instant fatal error.
You might want to check the file ownership and permissions, and check for PHP security as is mentioned HERE

Where to include class specific Exception Class

I am wondering what is the best practice for including class specific exception files.
Say we have an init exception that extends base exception that extends Exception. Should these exceptions be included in global application scope or at the top of the class files they can throw from with include_once?
I know both methods work, but for testing it seems having the file included at the top would be easier as the dependency would be included with the class file.
They should only be included with the module that they are attached to. It doesn't make much sense to throw an exception for a module that isn't loaded. So when you load the module, load the exceptions as well. This can done in the module init code or simply including the file on top of the main module file.

Categories