PHP - spl_autoload and namespaces - doesn't work with capital letters - php

I've set up a really basic autoloader in my index.php to grab a namespaced class within hello.php. My development environment is Ubuntu 12.04.
Why am I trying to do this? I'm trying to stick to the PSR-1 and PSR-2 coding standard, that includes:
Class names MUST be declared in StudlyCaps
Namespaces are as /Vendor/Class (note: capitals)
The following is my structure and code that works before making the changes to capitals.
Folder Structure
- web
-- index.php
-- core
--- hello.php
Autoloader
Within index.php, I have my autoloader:
set_include_path(__DIR__);
spl_autoload_extensions('.php,.class.php');
spl_autoload_register();
Class File
Within the core folder, I have hello.php
namespace core;
class hello {
public function __construct() {
echo 'Constructed!';
}
}
Code that works
If I run $obj = new \core\hello(); in my index.php, I get back "Constructed!". Great!
That which doesn't work
Renaming my core folder to 'Core' - note the uppercase C, and also the namespace in hello.php to namespace Core;.
Now let's try again with $obj = new \Core\hello();
Fatal error: Class 'Core\hello' not found in ...
So please tell me, why am I not able to use capital letters to keep inline with the PSR standards? What am I doing wrong?

When you run your PHP code on a Linux platform, it's important to remember that Linux is case sensitive with filenames.
This affects autoloaders because they typically use the namespace and the class name when building the filename to load.
If the folder is named core, then the namespace must be core, with the same capitalisation. If you change it to Core in the namespace, then you must do the same to the folder name. (and as a result, all other core classes must be changed to Core at the same time).
On Windows, this doesn't happen because the Windows filesystem isn't case sensitive. This can cause confusion when code is tested and works on a local Windows-based dev system, and then breaks when it is copied to a Linux-based server.
[EDIT]
Okay, so I missed that you had changed the dirname as well. But nevertheless, I still think this is an issue of the filename/dirname case.
I note that you're calling spl_autoload_register() without any params. This means that the default spl_autoload() function will be used as the autoloader.
If you read the documentation for spl_autoload(), you'll note that it uses the lowercased version of the class and namespace.
In other words, using the default autoloader, your classes can be mixed case, but the folder structure and filenames must be all lower case.
So in fact, for you, you need to keep your filenames lower case.
I've personally experienced it the other way round, as per my original answer, where I had a fully lower case filename, and my mixed case class name was breaking when I moved from Windows dev box to Linux server. The reason my experience is different from yours is because I'm using a custom-written autoload function, which doesn't do an auto-lowercase conversion, so the case of my filenames has to match that of my classnames.

I think you have shown us some good ambiguity.Correct me if I am wrong.
According to the specification you have to use the lowercased name of the class (and namespace) being instantiated.(http://www.php.net/manual/en/function.spl-autoload.php)
But PSR tells us to use the capital letters. If you want to stick with PSR then we have to overwrite the default spl_autoload to our own.

For anyone else having issues with this, why not make use of ucfirst() or strtolower() ?
So the code below would try all lowercase and also try first letter uppercase files
e.g: somename.class.php or Somename.class.php
The is_readable() checks first as to not display the php error for file not found.
spl_autoload_register(function($name) {
if (is_readable(strtolower($name).'.class.php')) {
require_once(strtolower($name).'.class.php');
}
elseif (is_readable(ucfirst($name).'.class.php')) {
require_once(ucfirst($name).'.class.php');
}
});

Related

Php 7.4.3 Class not found

This is my project path configuration
./create.php
/Install/Install.php
create.php
<?php
use Install\Install;
echo "Starting";
$install = new Install();
This gives me the error
PHP Fatal error: Uncaught Error: Class 'Install\Install' not found in /project/create.php:6
Install.php
<?php
namespace Install;
class Install
{
//whatever
}
Can someone explain me what is happening there ?
Obviously I guess that using a require_once line with my filename would probably fix the issue...but I thought using namespace and use import could prevent me from doing that like we do in classic framework like symfony / magento ?
I've seen some post speaking about autoloading, but i'm a little bit lost. Haven't been able to find a clear explanation on the other stack topic neither.
PHP compiles code one file at a time. It doesn't have any native concept of a "project" or a "full program".
There are three concepts involved here, which complement rather than replacing each other:
Namespaces are just a way of naming things. They allow you to have two classes called Install and still tell the difference between them. The use statement just tells the compiler (within one file) which of those classes you want when you write Install. The PHP manual has a chapter on namespaces which goes into more detail on all of this.
Require and include are the only mechanisms that allow code in one file to reference code in another. At some point, you need to tell the compiler to load "Install.php".
Autoloading is a way for PHP to ask your code which file it should load, when you mention a class it hasn't seen the definition for yet. The first time a class name is encountered, any function registered with spl_autoload_register will be called with that class name, and then has a chance to run include/require to load the definition. There is a fairly brief overview of autoloading in the PHP manual.
So, in your example:
use Install\Install; just means "when I write Install, I really mean Install\Install"
new Install() is translated by the compiler to new Install\Install()
the class Install\Install hasn't been defined; if an autoload function has been registered, it will be called, with the string "Install\Install" as input
that autoload function can then look at that class name, and run require_once __DIR__ . '/some/path/Install.php';
You can write the autoload function yourself, or you can use an "off-the-shelf" implementation where you just have to configure the directory where your classes are, and then follow a convention for how to name them.
If you want to Use class from another file, you must include or require the file.
Use require('Install.php'); before use Install\Install;.
If you are planning to do a big project I would recommend to use PHP frameworks rather than coding from scratch.

how include all classes by preg_match and autoload [duplicate]

I've set up a really basic autoloader in my index.php to grab a namespaced class within hello.php. My development environment is Ubuntu 12.04.
Why am I trying to do this? I'm trying to stick to the PSR-1 and PSR-2 coding standard, that includes:
Class names MUST be declared in StudlyCaps
Namespaces are as /Vendor/Class (note: capitals)
The following is my structure and code that works before making the changes to capitals.
Folder Structure
- web
-- index.php
-- core
--- hello.php
Autoloader
Within index.php, I have my autoloader:
set_include_path(__DIR__);
spl_autoload_extensions('.php,.class.php');
spl_autoload_register();
Class File
Within the core folder, I have hello.php
namespace core;
class hello {
public function __construct() {
echo 'Constructed!';
}
}
Code that works
If I run $obj = new \core\hello(); in my index.php, I get back "Constructed!". Great!
That which doesn't work
Renaming my core folder to 'Core' - note the uppercase C, and also the namespace in hello.php to namespace Core;.
Now let's try again with $obj = new \Core\hello();
Fatal error: Class 'Core\hello' not found in ...
So please tell me, why am I not able to use capital letters to keep inline with the PSR standards? What am I doing wrong?
When you run your PHP code on a Linux platform, it's important to remember that Linux is case sensitive with filenames.
This affects autoloaders because they typically use the namespace and the class name when building the filename to load.
If the folder is named core, then the namespace must be core, with the same capitalisation. If you change it to Core in the namespace, then you must do the same to the folder name. (and as a result, all other core classes must be changed to Core at the same time).
On Windows, this doesn't happen because the Windows filesystem isn't case sensitive. This can cause confusion when code is tested and works on a local Windows-based dev system, and then breaks when it is copied to a Linux-based server.
[EDIT]
Okay, so I missed that you had changed the dirname as well. But nevertheless, I still think this is an issue of the filename/dirname case.
I note that you're calling spl_autoload_register() without any params. This means that the default spl_autoload() function will be used as the autoloader.
If you read the documentation for spl_autoload(), you'll note that it uses the lowercased version of the class and namespace.
In other words, using the default autoloader, your classes can be mixed case, but the folder structure and filenames must be all lower case.
So in fact, for you, you need to keep your filenames lower case.
I've personally experienced it the other way round, as per my original answer, where I had a fully lower case filename, and my mixed case class name was breaking when I moved from Windows dev box to Linux server. The reason my experience is different from yours is because I'm using a custom-written autoload function, which doesn't do an auto-lowercase conversion, so the case of my filenames has to match that of my classnames.
I think you have shown us some good ambiguity.Correct me if I am wrong.
According to the specification you have to use the lowercased name of the class (and namespace) being instantiated.(http://www.php.net/manual/en/function.spl-autoload.php)
But PSR tells us to use the capital letters. If you want to stick with PSR then we have to overwrite the default spl_autoload to our own.
For anyone else having issues with this, why not make use of ucfirst() or strtolower() ?
So the code below would try all lowercase and also try first letter uppercase files
e.g: somename.class.php or Somename.class.php
The is_readable() checks first as to not display the php error for file not found.
spl_autoload_register(function($name) {
if (is_readable(strtolower($name).'.class.php')) {
require_once(strtolower($name).'.class.php');
}
elseif (is_readable(ucfirst($name).'.class.php')) {
require_once(ucfirst($name).'.class.php');
}
});

Namespaces and Class names with underscores

composer.json contains the following
...
"autoload": {
...
"psr-0":{"Acme": "app/lib"}
},
at app/lib/Acme/Models/Product/Display.php I have the following:
<?php
namespace Acme\Models\Product;
use Eloquent;
use Db;
class Product_Display extends Eloquent
{
I'm lost on how to call that class given the underscore in the Class Name inside of a repository class:
<?php
namespace Acme\Repositories;
use Acme\Models\Product\Display as Product_Display;
...
Product_Display::where('page_id','=',$page_id)->first();
This gives me a Class 'Acme\Models\Product\Display' not found error.
I'm guessing the problem lies in the use Acme\Models\Product\Display as Product_Display, but I've tried several variations none of which seemed to cure the problem.
Did you composer dump-autoload? Do this by typing composer dump-autoload in the terminal from the root of your project.
Every time you make changes to your composer.json file you need to allow composer to reconstruct the autoload files. In the case of psr-0 this is all you need to do.
In the case of classmap autoloading, every time you add a file to a directory that is being autoloaded, you need to composer dump-autoload.
edit:
To fix: change use Acme\Models\Product\Display as Product_Display; to use Acme\Models\Product\Display. Change the class name from Product_Display to just Display.
I think I spotted the issue. You have use Acme\Models\Product\Display as Product_Display; but you need to use Acme\Models\Product\Product_Display; since the last part of the use statement is the name of the class. In this case your class actually is named Product_Display, not Display (which is the file name). You might also need to change the file name to match the class name (also for conventions sake you should do this).
You are using PSR-0 autoloading. This scheme converts every backslash and every underscore into a directory separator when building the path to the file.
So Acme\Whatever_Underscored is searched for in the path prefix/Acme/Whatever/Underscored.php.
With PSR-4, the rules have changed. First, it only works for namespaced classes, not the old Acme_Underscore_Endless_Classnames. Second, you don't have to have a set of otherwise emtpy directories if you don't want to. Third: Underscores are NOT converted to directory separators.
With PSR-4 rules, the Acme\Whatever_Underscored is searched for in the path prefix/Acme/Whatever_Underscored.php - or even prefix/Whatever_Underscored.php if configured so.
You got confused because you connected the class name in the code with the path to the file. PHP only cares about the class name in use imports. Always use the class name as it is defined in your class's file. Then the autoloader kicks in and tries to find a matching file - with the rules of either PSR-0 or PSR-4. If it cannot find the correct file (either the path does not point to the file correctly, or the file does not contain the class being searched for), you get an error.

Zend framework form helper

I'm trying to work out how below works...
Where does Jobs_Form_Report_Productivity() come from?
public function productivityAction(){
$this->view->headTitle('Number of Unique Jobs by Portfolio');
$this->view->form = new Jobs_Form_Report_Productivity();
if($this->view->form->isValid($_POST))
{
$report = new Model_Users();
$this->view->results = $report->reportProductivity(
$this->view->form->getStartDate('yyy-MM-dd'),
$this->view->form->getEndDate('yyy-MM-dd')
);
}
}
Is this some form of Model class?
ZF1 uses PEAR namespacing (note the underscores), each underscore becomes a directory separator and the last part of the classname is usually the name of the file typically something like 'Productivity.php', so Jobs_From_Report_Productivity should be located in Jobs/Form/Report in a file called 'Productivity.php'.
Since the classname you are looking for does not start with 'Application_', my suggestion is that it is a module namespace called 'jobs', so you are probably looking for 'application/modules/jobs/forms/report/Productivity.php,
otherwise it could be in the library directory as library/jobs/forms/report/Productivity.php.
Of course, its completey possible to do some weird and wonderful things with the locations that ZF1 uses to locate classes.
Zend Framework use Zend_Loader_Autoloader to load classes that are called but not found in the script scoope. When a class that isn't found is called the autoloader kicks in and find and include the class file in few simple steps.
1) Converts all the _ (underscore sign) into / (slash) in the class name.
Example: Zend_Loader_Autoloade becomes Zend/Loader/Autoloader
2) The suffix .php is appended to the end of the converted string and it becomes: Zend/Loader/Autoloader.php
3) Checks if the "Zend_" name space (name space as class prefix and not PHP5.3 namespace) is registered with the autoloader. If the name space is registered then the autoloader will issue: include Zend/Loader/Autoloader.php.
4) If the class is found it returns it and the script continues. If class not found based on the configuration throws and exception or error.
PRE-CONDITION: The file Zend/Loader/Autoloader.php should be in the PHP include path for the current script.
Therefore, Jobs_Form_Report_Productivity will correspond to Jobs/Form/Report/Productivity.php and it will be found in one (or more) of the include paths. Find the include paths with PHP's: get_include_path(). Most of the times (in ZF1) the include path(s) is/are set in the index.php file in the public directory of the Zend Framework application.
See: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md as the guys from PHP-FIG made this autoloading approach a standard "PSR-O"
Hope this helps :)

Capital letters in class name PHP

I have two classes in my system. One is called file and second is File.
On my localhost when i instantiate file i get file object, but my friend running the same script gets object of File like the capital letters were unrecognized and "file" was equal to "File".
Is that some configurable thing?
We are both running on Windows. I have WampServer, he has XAMPP.
PHP is case insensitive for the class naming. it means you can normally do $file = new file() even if the class is named File and vice-versa.
Are you by any chance relying on the auto loading of class files ? If this is the case, it is possible that depending on the computer, the interpreter don't always find the same file first. This will explain the problem.
I strongly suggest that you rename your classes. It's always a bad idea to rely on case to differentiate two different things and by convention, class names always start with a capital letter.
If you can't change the class names, I suggest to have a look at php namespaces.
Classnames in PHP are not case sensitive (that doesn't depend on the operating system)
class myclass {}
$x = new MYclaSS;
var_dump($x);
object(myclass)#1 (0) {
}
so as general advice: You shouldn't start and try to mix something there :)
Code like this should not work:
class ab {}
class AB {}
Fatal error: Cannot redeclare class AB in ... on line x
I guess you are using some kind of lazy loading for class files, may be you are programming in a PHP framework. The secret will lie in your __autoload function. Find it.
Check PHP manual for Autoloading.
The following code:
<?php
class file {
public $a;
}
class File {
public $a2;
}
$x = new file();
Gives an error: Cannot redeclare class File so again, the trick might be which file is included.
Behavior of your code displays that one of the classes isn't being loaded (otherwise you'll see class redeclare error). It is probably the auto loader that first loads the file class and then when it finds definition to File it simply assumes that that it has already loaded the class (due to case insensitive behavior of PHP).

Categories