How to autoload PHP 5.3 classes? - php

I am trying to autoload my PHP 5.3 namespaced classes eg.
/JM
Auth.php
User.php
Db/Entity.php
/ ...
I did
namespace KM;
class Autoloader {
public static function registerAutolaod() {
ini_set('include_path', dirname(__FILE__) . PATH_SEPARATOR . ini_get('include_path'));
spl_autoload_register(function ($classname) {
$file = preg_replace('/\\\/', DIRECTORY_SEPARATOR, $classname) . '.php';
echo $file . '<br />';
include ($file);
});
}
}
Problem is sometimes I get classname as User sometimes KM\User how can I fix this?

The $classname you receive in your callback function will be assumed to be local. That's because you have defined your __autoloader within a namespace.
To always get the absolute / fully qualified namespace and class name, declare your autoloader in the global scope. http://www.php.net/manual/en/language.oop5.autoload.php#87985

Related

How to open a .json file in a certain namespace

It should be easy to load a file using a namespace, but I cannot seem to generate the url to it. The file is located in the same directory as MyClass, where it is called from.
<?php namespace Mynamespace\Subnamespace;
class MyClass {
public function getFile() {
$fileLocation = 'myfile.json'; // file is located next to this class in Mynamespace\Subnamespace\file.json
return file_get_contents( $fileLocation );
}
}
The closest solution I found is using the method getFileName() in a ReflectionClass of MyClass. But that returns the full url including the class MyClass.php file. And using a regExp on that seems overkill, since there is probably an easier solution.
If a namespace can somehow be converted into a valid url, that should do the trick.
How should file.json be retreived?
You can't use namespaces for JSON files. But since it is in the same directory as the class you should be able to use __DIR__
<?php namespace Mynamespace\Subnamespace;
class MyClass {
public function getFile() {
$fileLocation = 'myfile.json';
return file_get_contents( __DIR__ . PATH_SEPARATOR . $fileLocation );
}
}
The easiest way to do this is with get_class
<?php namespace Mynamespace\Subnamespace;
class MyClass {
public function getFile() {
// Will have backslashes so str_replace if this is a Unix environment
$path = str_replace('\\', '/', get_class($this));
$fileLocation = $path . '/myfile.json'; // Mynamespace/Subnamespace/myfile.json
return file_get_contents( $fileLocation );
}
}
If I understand correctly, just this:
$fileLocation = __NAMESPACE__ . '\myfile.json';
See PHP: namespace keyword and __NAMESPACE__ constant.
But since you state "file is located next to this class in Mynamespace\Subnamespace\file.json" then you can just use the full path to the current file:
$fileLocation = __DIR__ . '\myfile.json'; // or dirname(__FILE__)
See PHP: Magic Constants.

php - autoload not working with static method

I using spl_autoload_register to autoload class like
My Structure
index.php
Module\Autoloader.php
Module\MyClass.php
Test\test.php
in index.php file
require_once ("Module\Autoloader.php");
use Module\MyClass;
include 'Test\test.php';
in Module\Autoloader.php file
class Autoloader {
static public function loader($className) {
$filename = __DIR__."/" . str_replace("\\", '/', $className) . ".php";
echo $filename.'<br>';
if (file_exists($filename)) {
include($filename);
}
}
}
spl_autoload_register('Autoloader::loader');
in Module\MyClass.php file
namespace Module;
class MyClass {
public static function run() {
echo 'run';
}
}
in Test\test.php file
MyClass::run();
But it has error
Fatal error: Uncaught Error: Class 'MyClass' not found in ..\Test\test.php
How to fix that thank
your issue is that you prepend __DIR__
__DIR__ is based on where the file from which it gets called resides:
__DIR__
The directory of the file. If used inside an include, the directory of the included file is returned. This is equivalent to dirname(__FILE__). This directory name does not have a trailing slash unless it is the root directory.
http://php.net/manual/en/language.constants.predefined.php
So because your autoloader routine resides in ./Module/
__DIR__ will not return / when called from index.php but Module, making your finished classpath Module/Module/MyClass.php which obviously can't be found.
Either use another means of prepending the directory, like a predetermined list, use the first part of the namespace (so just ditch the __DIR__) or move the classes to location relative to directory in which your include file resides.
Your autoloader is inside the Module dir so it will apppend an extra "Module" when you try to append "DIR" to the class full name. The file location will be something like this:
../Module/Module/MyClass.php
Try to move your autoloader the same dir as index.php or change it as the following:
<?php
class Autoloader {
static public function loader($className) {
$filename = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
str_replace("\\", DIRECTORY_SEPARATOR, $className) . ".php";
if (file_exists($filename)) {
include($filename);
} else {
echo "$filename not found!\n";
}
}
}
spl_autoload_register('Autoloader::loader');

Oriented Object PHP autoloader issue

I encounterd a little problem with my classes : they simply do not load through my autoloader.
I get this message error :
Warning:
require(C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php):
failed to open stream: No such file or directory in
C:\wamp64\www\blog\app\Autoloader.php on line 23
Fatal error: require(): Failed opening required
'C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php'
(include_path='.;C:\php\pear') in
C:\wamp64\www\blog\app\Autoloader.php on line 23
Factory class :
use Core\config;
use Core\Database\MysqlDatabase;
class App {
public $title = "My super site";
private $db_instance;
private static $_instance;
public static function getInstance()
{
if (is_null(self::$_instance))
{
self::$_instance = new App();
}
return self::$_instance;
}
public static function load()
{
session_start();
require ROOT . '/app/Autoloader.php';
App\Autoloader::register();
require ROOT .'/core/Autoloader.php';
Core\Autoloader::register();
}
public function getTable($name)
{
$class_name = '\\App\\Table\\' . ucfirst($name) .'Table';
return new $class_name($this->getDb());
}
public function getDb()
{
$config = Config::getInstance(ROOT . '/config/config.php');
if (is_null($this->db_instance)) {
$this->db_instance = new MysqlDatabase($config->get('db_name'), $config->get('db_user'), $config->get('db_pass'), $config->get('db_host'));
}
return $this->db_instance;
}
}
Namespace App autoloader class :
<?php
namespace App;
class Autoloader {
static function register()
{
spl_autoload_register(array(__CLASS__, 'autoload')); // __CLASS__ load the current class
}
static function autoload($class)
{
if (strpos($class, __NAMESPACE__ .'\\') === 0) {
$class = str_replace(__NAMESPACE__ . '\\', '', $class); // _NAMESPACE_ load the current name_space
$class = str_replace('\\', '/', $class);
require __DIR__ . 'Autoloader.php/' . $class . '.php'; // __DIR__ = the parent folder. Here "app"
}
}
}
Namespace Core autoloader class :
<?php
namespace Core;
class Autoloader {
static function register()
{
spl_autoload_register(array(__CLASS__, 'autoload')); // __CLASS__ load the current class
}
static function autoload($class)
{
if (strpos($class, __NAMESPACE__ .'\\') === 0) {
$class = str_replace(__NAMESPACE__ . '\\', '', $class); // _NAMESPACE_ load the current name_space
$class = str_replace('\\', '/', $class);
require __DIR__ . 'Autoloader.php/' . $class . '.php'; // __DIR__ = the parent folder. Here "app"
}
}
}
Empty PostTable
namespace App\Table;
use Core\Table\Table;
class PostsTable extends Table
{
}
Index page :
define('ROOT', dirname(__DIR__));
require ROOT . '/app/App.php';
App::load();
$app = App::getInstance();
$posts = $app->getTable('Posts');
var_dump($posts->all());
How to make it works please?
AS I said in the comments check this path
require(C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php)
Doesn't look right to me
require(C:\wamp64\www\blog\ [appAutoloader.php] /Table/PostsTable.php)
What's that bit doing there....
Also namespace of App is not app for the folder its App because this may work on Windows but you will find it does not work on Linux. Because Linux paths are case sensitive, and windows are not.
Further this makes little to no sense
require __DIR__ . 'Autoloader.php/' . $class . '.php'; // __DIR__ = the parent folder. Here "app"
Require 2 files? Paths don't work that way, not that I am aware of at least.
On top of that your implementation ignores the _ Typically underlines will be part of the class name but are replaced by directory, this allows a shorter namespace. So for example instead of having a namespace like this
Namespace \APP\Table;
class PostsTable ..
You could have a class in the same place Like so
Namespace \APP;
class Table_PostsTable ..
With a shorter namespace but still located in the App/Table/PostsTable.php file. However, that's just how I read the spec for PSR autoloaders.
PRO TIP
Take this path C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php open the file browser on you desktop and see if it pulls up the file by pasting it into the navigation bar. It wont, but you can be sure your path is wrong by eliminating the code.

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 namespace autoload

Trying to understand how namespaces and autoload works on PHP
Server.php located at core/server.php
namespace core\server
{
class Main
{
public function getTopic()
{
$get_params = $_GET;
if (empty($get_params)) $get_params = ['subtopic' => 'test'];
return $get_params;
}
}
}
and Index.php
spl_autoload_register();
use core\server as subtopic;
$test = new subtopic\Main();
var_dump($test);
It cant load the class core/server/Main
Autoload doesn't work that way.
First I will explain how autoloaders works.
spl_autoload_register() is a function to register a function you have in your code to server as an autoloader, the standard function would be:
define('APP_PATH','/path/to/your/dir');
function auto_load($class)
{
if(file_exists(APP_PATH.$class.'.php'))
{
include_once APP_PATH.$class.'.php';
}
}
spl_autoload_register('auto_load');
The constant APP_PATH would be the path to your directory where your code lives.
As you noticed it the param that is passed to spl_autoload_register is the name of my function, this register the function so when a class is instanciated it runs that function.
Now an effective way to use autoloaders and namespaces would be the following:
file - /autoloader.php
define('APP_PATH','/path/to/your/dir');
define('DS', DIRECTORY_SEPARATOR);
function auto_load($class)
{
$class = str_replace('\\', DS, $class);
if(file_exists(APP_PATH.$class.'.php'))
{
include_once APP_PATH.$class.'.php';
}
}
spl_autoload_register('auto_load');
file - /index.php
include 'autoloader.php';
$tree = new Libs\Tree();
$apple_tree = new Libs\Tree\AppleTree();
file - /Libs/Tree.php
namespace Libs;
class Tree
{
public function __construct()
{
echo 'Builded '.__CLASS__;
}
}
file - /Libs/Tree/AppleTree.php
namespace Libs\Tree;
class AppleTree
{
public function __construct()
{
echo 'Builded '.__CLASS__;
}
}
I'm using namespaces and autoload to load my functions in a nicely way, you can use namespace to describe in what dir your class resides and uses the autoloader magic to load it without any problems.
Note: I used the constant 'DS', because in *nix it uses '/' and in Windows it uses '\', with DIRECTORY_SEPARATOR we don't have to worry where the code is going to run, because it will be "path-compatible"

Categories