Don't use the code in this post in your applications.
I was reading a comment of someone that liked php before version 5, because it had no object-oriented features.
That made me think about how code gets discovered in php files. And the first thing I thought of was Composer.
With a simple directive in the composer.json file you can make all classes discoverable in a directory.
{
"autoload": {
"psr-4": {"Acme\\": "src/"}
}
}
For functions it is a different story. You can only add one file at a time.
{
"autoload": {
"files": ["src/MyLibrary/functions.php"]
}
}
It seems like functions are second class citizens for Composer.
Why does Composer do it like that?
The PHP autoload functions are build with class loading as the target.
// src/a.php
function a() {
echo 'Test';
}
// index.php
spl_autoload_register(function($callable){
$file = 'src' . DIRECTORY_SEPARATOR . $callable . '.php';
if{file_exists($file)) {
require_once $file;
}
echo "$callable is not found in the src directory.";
});
a();
This will result in a undefined function error. The echo string will not be displayed.
It is also not a good practice to call functions in an object-oriented application.
What if I want a functions and classes autoloader?
// index.php
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator('src')) as $file) {
if ($file->isFile()) {
require_once $file->getPathname();
}
}
Test\a(); // src/Test/a.php
new Test\B(); // src/Test/B.php
This is a brute-force method to load every file.
A more fine-grained way would be to use a function that iterates the files that are provided.
// index.php
function loadCode(...$files) {
foreach ($files as $name) {
$parts = explode('\\', $name);
$fileName = array_pop($parts);
$namespace = count($parts) > 0 ? implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR : '';
$file = 'src' . DIRECTORY_SEPARATOR . $namespace . $fileName . '.php';
if(file_exists($file)) {
require_once $file;
}
}
}
// any file that is called by index.php
loadCode(Test\a::class);
Test\a();
The most confusing part in the code will be Test\a::class
. I used that hack to not use a string as a filename. Most code editors will autocomplete the path because of the class standards.
What can cause problems?
Having a class and a function with the same name is not possible in the same directory, because the file-system of an OS is not going to like that. Even if you use capitalisation, like a.php and A.php
There is no standard to distinguish between third party code and your own code. This can be solved if I use the vendor standard.
// index.php
function loadCode(...$files) {
foreach ($files as $name) {
$parts = explode('\\', $name);
$fileName = array_pop($parts);
$namespace = count($parts) > 0 ? implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR : '';
foreach(['src', 'vendor'] as $baseDirectory) {
$file = $baseDirectory . DIRECTORY_SEPARATOR . $namespace . $fileName . '.php';
if(file_exists($file)) {
require_once $file;
}
}
}
}
Conclusion
Would I ever use this? NO, no one should use this.
Go has this mixing of functions and classes build-in using modules. Because what looks like a class in Go, a struct, is not really a class according to the object-oriented definition.
So if the mixing is something that is a requirement Go is a better option.
Top comments (0)