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
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.
This is what I have at hand:
//Person.php
namespace Entity;
class Person{
}
User file:
//User.php
use Entity\Person;
$person = new Person;
Here, it fails if I don't include the Person.php file. If I include it, the everything works fine. Do I absolutely require to include the file even when using namespaces? If at all we need to include/require files, then how can namespaces be effectively used? Also, can we maintain folder structure by nesting namespaces?
The answer to your question is "yes and no".
Indeed the code implementing class Person has to be included, otherwise the class is not defined and cannot be used. Where should the definition come from, when the code is not included? The php interpreter cannot guess the classes implementation. That is the same in all programming languages, by the way.
However there is something called Autoloading in php. It allows to automatically include certain files. The mechanism is based on a mapping of class names to file names. So in the end it boils down to php searching through a folder structure to find a file whos name suggests that it implements a class currently required in the code it executes.
But don't get this wrong: that still means the file has to be included. The only difference is: the including is done automatically, so without you specifying an explicit include or require statement.
Yes, you need to include every file.
A very good example can be found here on effective usage of namespaces.
With PSR-0 autoloading, the namespace has to be the same as the folder in which the class is, file the filename has to be the same as the classname. This gives you very simple and effective autoloading with composer for example.
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.
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');
}
});
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 :)