I am getting more and more confused as I read through various SO posts and the PHP Manual on how to properly set up the "USE" for PHPSpreadsheet. I installed the files manually (not through Composer) and all the posts/references I've read seem to imply that I should set it up this way:
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
My folder structure does not follow this format and is set up this way (for example):
/phpscripts/phpspreadsheet/IOFactory.php
I'm now wondering if the folder structure is not related to how the "USE" is configured/utilized but I have not found any posts that explain this for my level of understanding. I see a few references to the use of "REQUIRE" that points to the exact location of the specific files but that is just adding to my confusion. Could someone elaborate on how this should be properly set up?
The thing that composer does for you is to provide what is called an autoloader, which basicly loads the files which contain the classes that your code needs at runtime.
Registering a autoloader is done through the function spl_autoload_register where you provide it a callable that should be run when your code requests a class that is not known to PHP.
So when using a composer package without composer you would need to make your own autoloader for that specific library.
As you noticed the FQCN(fully qualified class name) aka. (namespace + class name) does not always correspond to the location of the file containing that class. The namespace of a class is actually defined in the file containing the class(typically in the top of the file).
PSR-4 is a standard that:
describes a specification for autoloading classes from file paths.
and if you look in the PhpSpreadsheet composer.json you can see that they follow that standard so that makes it easier to implement our own autoload now that you have decided not to use composer.
here is my attempt at loading the PhpSpreadsheet without composer
use PhpOffice\PhpSpreadsheet\IOFactory;
spl_autoload_register(function ($class) {
$path = 'phpscripts/phpspreadsheet'; // Replace this with the path to where PhpSpreadsheet is stored.
//Split the FQCN into parts
$fqcnParts = explode('\\', trim($class, " \t\n\r\0\x0B\\"));
//check that we have atleast 3 parts and that the vendor namespaces matches our package
if (count($fqcnParts) < 3 || $fqcnParts[0] != 'PhpOffice' || $fqcnParts[1] != 'PhpSpreadsheet') {
return;
}
// read the comments in this next parts from the inside -> out
$filePath =
rtrim($path, '/') . '/' . // prefix with our path to the library
ltrim(
implode( // implode the remaining parts into a string split by /
'/',
array_slice($fqcnParts, 2) // Remove the vendor namespaces
),
'/'
) . '.php'; // suffix the file extension
if (file_exists($filePath)) {
include $filePath;
}
});
var_dump(class_exists(IOFactory::class)); // this line will try to autoload the IOFactory class
Hope this helps you otherwise feel free to hit me up :-)
Related
Uisng spl_autoload_register for auto loading classes on my code. Example:
<?php
spl_autoload_register(function ($class_name) {
include 'my_class1.php';
include 'my_class2.php';
//...
include 'my_classN.php';
});
$obj = new MyClass1();
?>
What if I used just class MyClass1 in my code, does autoload loads all files or just my_class1.php?
Thanks in advance.
EDIT: DO NOT use above code. Now I using #Alex Howansky's code with PSR-4 autoloading specifications.
NOTE: Autoload requires to use namespaces if classes located in subdirectory relatively to basedir (see examples).
With this code, the first time you refer to any class, it will load every class. This will work, but almost certainly isn't what you want. In an autoloader, you usually just want to load only the one source file containing the one class referenced by $class_name. Something like this:
spl_autoload_register(function ($class_name) {
$filename = '/path/to/classes/' . $class_name . '.php';
require_once $filename;
});
This obviously becomes very difficult if your source file names don't match your class names or you otherwise can't determine the source file names based on the class names. This is why you should use PSR-4 naming conventions.
Just as an addition, you can use the PHP function get_declared_classes() which will list all defined classes in the current script.
I have this function below that autoloads classes before registering them as new classes.
function __autoload($controller){
$ce = explode('\\', $controller);
require ROOT . '/app/base/classes/' .
end($ce) . '/class.' .
end($ce) . '.php';
}
How can I ignore this autoloader for one class? The reason behind this is because I installed a package and the class file is another directory to what my class files are in...
Try to require your package file containing the class you need, before calling new OneClass().
For example, if you have [ROOT]/app/base/classes/OneClass/class.OneClass.php alongside with [ROOT]/app/custom/packages/OneClass.php, you may:
require ROOT . '/app/custom/packages/OneClass.php';
$class = new OneClass(); // will be an instance of /app/custom/packages/OneClass.php
But the best solution is using namespaces, as described in PSR-0 and PSR-4 recommendations.
And when you use namespaces, and you have correct directory/file structure/names, and you have correctly described namespaces inside your classes:
$my_class = new \app\base\OneClass();
// The same class name without any conflicts
$class_from_package = new \app\custom\packages\OneClass();
I think it's a more useful approach. You will have some logical directories/files structure which will reflect namespace structure. And if you want load the same class name from other folder, you just will be able to do it by using fully qualified class name with correspond namespace.
It can be a bit weird, to use namespaces, if you did not use them before. You need to support correlations between file path and namespace definition all the time (when moving class file to another folder, for example), but after some time of using it, you will pay no more attention to namespaces than you are paying to tying your shoes before going outdoor.
You can do something like this:
function __autoload($controller){
$ce = explode('\\', $controller);
$ignore=array('class_1', 'class_2');
if (!in_array(end($ce), $ignore)) {
require ROOT . '/app/base/classes/' .
end($ce) . '/class.' .
end($ce) . '.php';
}
}
According to the top comment on the PHP page spl_autoload_register( ) :
Good news for PHP 5.3 users with namespaced classes:
When you create a subfolder structure matching the namespaces of the >containing classes, you will never even have to define an autoloader.
<?php
spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();
?>
However, when I have the following structure:
* classes/someclass.php
* index.php
Where someclass.php contains the following:
<?php
class someclass {
function __construct( ) {
echo 'It works!';
}
}
?>
and index.php contains:
<?php
spl_autoload_extensions(".php");
spl_autoload_register();
new classes\someclass;
?>
Then I get the following error:
Fatal error: spl_autoload(): Class classes\someclass could not be
loaded
Am I getting this wrong? How can I make this work?
From the comments
This doesn't work either for the class:
<?php
namespace classes;
class someclass {
function __construct( ) {
echo 'It works!';
}
}
?>
In your someclass.php file you must define the namespace at the begginning.
<?php
namespace classes;
TLDR; It works, but:
namespace classes; is missing in classes/someclass.php
set_include_path(__DIR__); is missing in index.php
(spl_autoload_extensions(".php") is not necessary)
Include Path
The SPL autoload implementation is include path based. Using a dot as include path is relative to the working directory (!) which is independent from script file location. __DIR__ names the exact directory that is needed if the classes folder lies next to the index.php file as in the scenario.
Directory Separator mapping
Next the autoloader implementation does map the class namespace separator properly on Unix systems. In case that got lost in the o/p, the PHP source code clearly has this.
Case Sensitivity of File Names
What the source code also shows is that file-names to load are made lowercase. That is, if your file system is case sensitive, the file and directory names must be in lower case.
Refernces:
https://lxr.room11.org/xref/php-src%407.1/ext/spl/php_spl.c#spl_autoload
https://lxr.room11.org/xref/php-src%407.1/ext/spl/php_spl.c#307
PHP - most lightweight psr-0 compliant autoloader
The user, SedaSoft, who had posted the Good news comment you which you refer, had subsequently posted another comment, due to a revision of ideas:
What I said here previously is only true on Windows. The built-in
default autoloader that is registered when you call
spl_autoload_register() without any arguments simply adds the
qualified class name plus the registered file extension (.php) to each
of the include paths and tries to include that file.
Example (on Windows):
include paths:
- "."
- "d:/projects/phplib"
qualified class name to load:
network\http\rest\Resource
Here's what happens:
PHP tries to load
'.\\network\\http\\rest\\Resource.php'
-> file not found
PHP tries to load
'd:/projects/phplib\\network\\http\\rest\\Resource.php'
-> file found and included
Note the slashes and backslashes in the file path. On Windows this
works perfectly, but on a Linux machine, the backslashes won't work
and additionally the file names are case-sensitive.
That's why on Linux the quick-and-easy way would be to convert these
qualified class names to slashes and to lowercase and pass them to the
built-in autoloader like so:
<?php
spl_autoload_register(
function ($pClassName) {
spl_autoload(strtolower(str_replace("\\", "/", $pClassName)));
}
);
?>
But this means, you have to save all your classes with lowercase file
names. Otherwise, if you omit the strtolower call, you have to use the
class names exactly as specified by the file name, which can be
annoying for class names that are defined with non-straightforward
case like e. g. XMLHttpRequest.
I prefer the lowercase approach, because it is easier to use and the
file name conversion can be done automatically on deploying.
Note that this commenter had actually posted an answer here on SO today, containing the above links (along with a short explantation), but it was subsequently deleted, whilst in the review queue - presumably due to its brevity. I have reinstated the commenter's answer, along with the content of the link. Their answer was as follows:
I wrote that comment on php.net some time ago when I was working on a
Windows system. Later, I had to partially revoke what I wrote in that
comment on the very same page in another comment, which also contains
a possible solution to the problem that is the easiest I could think
of (apart from using Composer).
Here is a screenshot of the original answer:
Do not roll your own autoloading, but use composer instead.
Create a composer.json in the root of your project:
{
"autoload": {
"psr-4": {
"classes\\": "classes/"
}
}
}
Install composer, then run
composer dump-autoload
In your index.php, require the autoloader:
require __DIR__ . '/vendor/autoload.php';
For reference, see
https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx
https://getcomposer.org/doc/01-basic-usage.md#autoloading
http://www.php-fig.org/psr/psr-0/
http://www.php-fig.org/psr/psr-4/
Do not use the spl_autoload_extensions () function if the files to load only have the php extension.
In my case, I create a class called autoload .. something similar to this:
<?php
class Autoload
{
private static $_extensions = array(
0 => '.inc',
1 => '.lib.php',
2 => '.class.php',
);
public function __construct()
{
self::set_include_path();
spl_autoload_extensions(implode(',', self::$_extensions));
spl_autoload_register(__CLASS__, 'load_class');
}
public static function set_include_paths()
{
set_include_path(implode(PATH_SEPARATOR, array(
realpath('classes');
realpath('system');
...
get_include_path();,
)));
}
public function load_class($class)
{
if (!empty($class)) {
spl_autoload($class);
}
return false;
}
}
The use of ?> At the end of the file is not necessary.
I have built a PHP video upload library aimed to be used in different frameworks .
## HERE ##.
But i want to make it optimized like , i dodnt want to include or make\
require or require_once call in class and also i want the configuration class to be available to all class i have at run time ... without explicitly calling or making require once.
How can i do that , most php project use bootstrap class /file to do that , and some body pls help me.
I would suggest to make a way to have an auto_load.php file and this file would contain the files required to include, and then you can include this auto_load.php into the entry point file / class to your library to load all the necessary files for your library to work. It is the same idea as how composer work and it is efficient.
You can take a look into psr-4 standards in loading classes.
http://www.php-fig.org/psr/psr-1/
require_once is the same as require except PHP will check if the file has already been included, and if so, not include (require) it again, refer to http://php.net/manual/en/function.require-once.php
You can create an autoload function . This way you will only load the required library.
A basic autoload function looks like this:
define('PATH_LIBRARY', '/some/path/lib/');
function myautoload($class_name){
// you can do string manipulation to build the filename, for example
$filename = strtolower($class_name);
$filename = str_replace('_', '/', $filename);
$filename = ucwords($filename);
$filepath = PATH_LIBRARY.$filename.'.php';
if (file_exists($filepath))
{
require_once($path);
return true;
}
}
spl_autoload_register('myautoload');
Edit that code has to be added at the beginning of your code (usually in a file included at the top of your index.php ), so all instruction after will benefit of it. You can improve it by checking different directory (for example if the class name starts with "controller", or "model", change the path to the appropriate directory)
I'm actually trying to create a MVC framework for my own, however I'm having troubles with the Autoload. It's not a problem actually, but I'd like to ask the gurus, how are they using the spl_autoload_register function when there are different directories.
Lets say we have the following directories:
Controllers
Libs
Models
Each folder contains different classes, like:
Controllers:
Main.php
File.php
About.php
Libs:
Main.php
Front_controller.php
Models:
Index.php
File.php
Login.php
You can notice that some file names might be found with the same name in different directories. Okay, so this is what I've tried so far:
spl_autoload_register(function ($class) {
$pathContorllers = 'Controllers/' . $class . '.php';
$pathLibs = 'Libs/' . $class . '.php';
$pathModels = 'Models/' . $class . '.php';
if (file_exists($pathContorllers)) {
require_once $pathContorllers;
} elseif (file_exists($pathLibs)) {
require_once $pathLibs;
} elseif (file_exists($pathModels )) {
require_once $pathModels ;
}
});
It is working well, however I'm sure that there is another way to make everything simpler. Can anyone suggest me how can I make this code better or simpler / what are gurus using in this situation?
For the purpose of keeping individuals who may come across this answer from obtaining out-of-date information I have updated it in regards to the latest PSR autoloading standards. The original answer has been maintained for historical purposes and for those who are only interested in the PSR-0 autoloader.
Updated Answer
The PHP-FIG has officially deprecated the PSR-0 standard in favor of the alternative autoloader, PSR-4. Although the two are similar in some aspects they are also very different in others. (E.g.: the handling of underscores in class names.)
You may be thinking to yourself -- "I use PSR-0 now and it works fine." The truth of the matter is that PSR-0 will still work fine for certain projects. This is especially true when backwards compatibility with a package that doesn't use namespaces is concerned. PSR-0 is still a decent autoloading principle, but it has its own shortcomings.
Of course, if there is one thing that is a constant with programming, it is that code eventually changes and programming techniques continue to evolve. You can do yourself a favor today by preparing yourself for tomorrow. Therefore, if you are just starting a project or are trying to port a project to a newer version of PHP that can use namespaces, you should seriously consider using the PSR-4 autoloader.
It is also worth noting that if you are developing a project that does not use namespaces then PSR-4 does not apply to you. In this case PSR-0 or your own custom autoloader applies.
Original Answer
If you want to go with namespaces in your classes, then the PSR-0 route is a pretty good way to autoload. Basically your namespace represents you directory structure and classes can be loaded based on a convention.
If the PSR-0 method doesn't meet all your needs (or doesn't play nice with existing code) you can still add more functions with spl_autoload_register and PHP will go through them one by one in an attempt to load classes.
Example usage:
First thing is first, if you aren't familiar with namespaces in PHP then you will benefit from checking out the PHP manual on the subject. They can be a bit confusing at first, but their benefits are worth the initial confusion.
So I said that PSR-0 works by associating your namespaces with your directory structure. Let's use your directories for an example. You have in your root folder (wherever it may be) the following:
Project directory: <- Let's call this directory "MyProject"
Controllers:
Main.php
File.php
About.php
Libs:
Main.php
Front_controller.php
Models:
Index.php
File.php
Login.php
index.php <- Let's say this is your entry point file, this is where you will be autoloading stuff from.
Now let's take a look at your controller Main.php. Two things to keep in mind is that the class name needs to be the name of the file and the namespace for that class is the directory path to that file. So Main.php should look something like this:
<?php
namespace MyProject\Controllers;
class Main {
//Field vars, contructor, methods, etc. all go here.
}
?>
You would do the same thing for your your Login model
<?php
namespace MyProject\Models;
class Login {
//Field vars, contructor, methods, etc. all go here.
}
?>
Now in your index.php file (out in the root directory - MyProject) you would make your call to the spl_autoload_register and give it the PSR-0 autoloader.
spl_autoload_register( function ($className) {
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
});
//Now you can make your call to your objects without a bunch of include/require statements
$main = new \MyProject\Controllers\Main(); //Instantiates your 'Main' controller
$login = new \MyProject\Models\Login(); //Instantiates your 'Login' model
Hopefully that helps make better sense of it, and again, if you don't want to use namespaces you can always just keep adding closures into the SPL autoload stack. You can have 10 different autoloaders in there if you want and PHP will go through them one by one (in the order you defined them) using each function to try and load a class. However, a couple convention based autoloaders is a bit cleaner and more of a preferred method. Also keep in mind that the autoloader translates both namespace separators \ and underscores _ as a directory separator. So your Front_controller.php would not autoload as you would expect.
The code below will help. But I'll advice you check on Namespaces.
spl_autoload_register ( function ($class) {
$sources = array("Controllers/$class.php", "Lib/$class.php ", "Models/$class.php " );
foreach ($sources as $source) {
if (file_exists($source)) {
require_once $source;
}
}
});