PHP autoloaders and namespaces - php

I'm slightly confused by autoloaders and namespaces in PHP.
My classes are organised within an assets file,
-- Assets
-- user
-- User.php
-- database
-- Database.php
This is just a simple version, there are more files in each folder (i.e. user, database).
There is no need to worry about conflicting vendor names as there is only one vendor in this project (i.e. me) but for example, using the PSR0 autoloader:
function autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($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;
}
Do I need to declare a namespace in each of my class files and initilize a class by using new user\User();. Is it not possible to initilize it by using new user_User;
Many thanks

The PSR-0 autoloader convention doesn't require you to use namespaces that map to your directory structure. You can also use underscores in the class name for basically the same purpose.
Like the \ namespace separator, any underscores in the class name are converted to DIRECTORY_SEPARATOR when looking up the file to load. So, a class user_User and \user\User would both result in the autoloader looking for the same file: user/User.php.

Related

How to use the PSR-4 autoload in my /Classes/ folder?

I've tried multiple PSR-4 loaders, but they either don't work or I can't access the classes from another folder.
My current folder structure:
-Classes
--Config.php
--Session.php
--Frontend (folder)
---Login.php
PSR-4 Autoloader:
I tried to load all classes using the PSR-4 autoload register. I modified it slightly to my folder structure. I've given all classes the namespace Classes, but the ones in the Frotend folder has the namespace Classes\Frontend.
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Classes\\';
// base directory for the namespace prefix
$base_dir = __DIR__ . '/Classes/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});
I'm not sure if this has to do with the autoloader, but I also want to call the class from any file wherever it's located. So if I have a file in
/Frontend/templates/login-page.php
I want to be able to call the class "Classes\Frontend\Login".
Is this possible and how would I do that?
There are mainly two ways to get it working: The first option is to use an absolute server path (starting with a '/'), to set the base directory for your classes in your autoload function:
spl_autoload_register(function ($class) {
$prefix = 'Classes\\';
$base_dir = '/var/www/html/my_project/src/'; // your classes folder
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
if (file_exists($file)) {
require $file;
}
});
The better solution is, as #FĂ©lix suggested, to stick with the __DIR__ constant to keep things relative to your project folder. Absolute paths are brittle between deployments on different servers. __DIR__ refers to the directory of the file it is used in; in this case it is where you register the autoload function. Starting from this directory, you can navigate to the classes base directory, for example $base_dir = __DIR__ . '/../../src/;
Don't forget to namespace your classes:
namespace Classes;
class Foo
{
public function test()
{
echo 'Hurray';
}
}
Then use classes like this:
$foo = new Classes\Foo();
$foo->test();

Namespaces and Class loading in php

I'm new to namespaces in PHP and trying to make use of them to load classes.
Whenever I run my code I get class Cheese cannot be found on line x
PHPstorm recognizes the class via the namespaces and enables its methods.
I have the following files / directory structure.
/Project
/App
Mouse.php
/Test
MouseTest.php
Mouse.php
namespace App\Mouse;
class Cheese
{
}
MouseTest.php
namespace Test\MouseTest;
use \App\Mouse\Cheese as Cheese;
class CheeseTest
{
function test() {
$cheese = new Cheese();
$cheese->eat();
}
}
if you use composer or any autoloader that follow psr-0, then file name must same with class name, change Mouse.php to Cheese.php
This looks like a similar structure to the PSR0 standard found here: http://www.php-fig.org/psr/psr-0/
This is an example autoloader following that structure:
function autoload($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;
}
spl_autoload_register('autoload');
The spl_autoload_register call registers the autoload function so when a class is instantiated, it will use your autoload function to retrieve the class definition.
PHP namespaces don't load classes automatically, you have to include their files.
To make it automatic (like you want) you have to make an autoloader which will load the classes depending on the namespaces that you want to use.
The best way to make this, is using composer: http://code.tutsplus.com/tutorials/easy-package-management-with-composer--net-25530
but I recommend to search "php auloader" in google

PHP spl_autoload_register namespaces interfere with file includes

I don't know if I'm missing something but using namespaces seems to break my application. It won't find the parent class because it includes the namespace in the class name when the autoload register gets called
spl_autoload_register(function($classname){
if (preg_match('/[a-zA-Z]+Controller$/', $classname)) {
echo __DIR__ . '/controllers/' . $classname.".php" . "<br />";
require __DIR__ . '/controllers/' . $classname.".php";
}
});
//echo produces:
/var/www/web/controllers/DefaultController.php
/var/www/web/controllers/Project\Controllers\BaseController.php
Project\Controllers is the namespace used in both default and base controller.
Default extends base controller.
Why is spl autoload doing this?
Structure:
web/controllers:
BaseController.php
DefaultController.php
BaseController:
namespace Project\Controllers;
class BaseController
{
private $config;
public function __construct($config)
{
$this->config = $config;
}
}
DefaultController:
<?php
namespace Project\Controllers;
class DefaultController extends BaseController
{
}
The main problems seems to be that your autoloader is not aware of the root namespace which it belongs to. So really your autoloader should be made aware of or be part of the namespace so in this case Project.
So the first thing your autoload needs to do is remove the known root namespace (unless of course it is used as part of the file structure which in this case it isn't)
<?php namespace Project;
// Within the namespace
$classname = ltrim(str_replace(__NAMESPACE__ . '\\', '', $classname), '\\');
// Outside of the namespace
$classname = ltrim(str_replace('Project\\', '', $classname), '\\');
Now for the class name and the actual file location.
// find the last backslash
if($last = strripos($classname, '\\')) {
$namespace = substr($classname, 0, $last); // Controllers
$classname = substr($classname, $last+1); // DefaultController
}
Now you can built the actual file location to the PHP class file
$filepath = '/var/www/'; // Root
if(isset($namespace)) {
// Add the controllers bit onto the root
$filepath .= str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
// add the class name (DefaultController) to the file path
$filepath .= str_replace('\\', DIRECTORY_SEPARATOR, $classname) . '.php';
This should result in with the following filepaths which can be checked if exists then included if they do.
/var/www/Controllers/BaseController.php
/var/www/Controllers/DefaultController.php

Php namespaces with functions

I have a directory include. In this directory, I have only one dir: HQF. In this directory, I have many files where I always declare them belonging to the HQF namespace like this:
<?php
namespace HQF;
class MyClass
{
}
And the file is called "MyClass.php". I'm trying to stick "a bit" to PSR0.
Everything works fine, and I've made my autoloader like this:
function __autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DS, $namespace).DS;
}
$fileName .= str_replace('_', DS, $className).'.php';
$fileName = 'include/'.$fileName;
require $fileName;
}
So when I need a class I just have to do: $m = new \HQF\MyClass(); and it works like a charm, including automagically the file "include/HQF/MyClass.php"
I have a problem with "pure" functions.
I've made a file called "include/HQF/mb_utils.php" where I want to put my mb_xxx function. NB: no classes, only functions.
I've tried all the following things without success:
$test=\HQF\mb_ucname('calling HQF/mb_ucname...');
$test=\mb_ucname('calling HQF/mb_ucname...');
use \HQF\mb_utils; and then the two tests above;
None of them work. What am I missing?
I'm relying on Mark Baker's comment.
So here's my workaround: I've made a file named Mb.php and in it, a class named "Mb" (for 'multibytes') and I've put all my mutibytes functions like this:
<?php
namespace HQF;
class Mb
{
static public function ucfirst(&$string, $e ='utf-8')
{
/* blabla */
}
static public function ucname($string, $e ='utf-8')
{
/* blabla */
}
}
And then I call them like this:
$mystring_to_change = \HQF\Mb::ucname($original_name);
This is the only way I've found, I dont know if it's the best one... any suggestion welcome.

Register namespaced class in joomla 2.5

I'm trying to implement a library which was written for PSR-0 compliant autoloading in my joomla project. However joomla 2.5 does not support PSR-0 autoloading so I figured that I would have to write my own implementation.
Since I want to use my classes across my entire joomla installation I decided to write a plugin which registers the classes onAfterInitialise() but this is where it goes wrong: it overrides joomla's autoloading methods.
The library i'm trying to implement is located at libraries/Zoho/CRM/ZohoClient.php with several subfolders. Everything is namespaced with accordance to PSR-0 so starting with: \Zoho\CRM.
How do I register the classes in such a way that I can use them globally across joomla? This is my implementation:
public function onAfterInitialise()
{
spl_autoload_register(function ($className) {
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($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;
});
}
This obviously wont work because it overrides the joomla autoloading function. I'm running php5.3 (5.4 in 3 months)

Categories