Comment utiliser les espaces de noms PHP avec le chargement automatique?


104

J'obtiens cette erreur lorsque j'essaie d'utiliser le chargement automatique et les espaces de noms:

Erreur fatale: Classe 'Class1' introuvable dans /usr/local/www/apache22/data/public/php5.3/test.php à la ligne 10

Quelqu'un peut-il me dire ce que je fais mal?

Voici mon code:

Class1.php:

<?php

namespace Person\Barnes\David
{
    class Class1
    {
        public function __construct()
        {
            echo __CLASS__;
        }
    }
}

?>

test.php:

<?php

function __autoload($class)
{
    require $class . '.php';
}

use Person\Barnes\David;

$class = new Class1();

?>

Réponses:


119

Class1 n'est pas dans la portée globale.

Voir ci-dessous pour un exemple de travail:

<?php

function __autoload($class)
{
    $parts = explode('\\', $class);
    require end($parts) . '.php';
}

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Modifier (2009-12-14):

Juste pour clarifier, mon utilisation de "utiliser ... comme" était de simplifier l'exemple.

L'alternative était la suivante:

$class = new Person\Barnes\David\Class1();

ou

use Person\Barnes\David\Class1;

// ...

$class = new Class1();

1
Vous n'êtes pas obligé d'utiliser AS. Ce n'est pas pourquoi cette solution fonctionne. Vous pouvez tout aussi facilement faire: use Person\Barnes\David\Class1;(ce qui équivaut à use Person\Barnes\David\Class1 as Class1;).
cartbeforehorse

1
Merci, cela fonctionne. Mais je ne comprends pas pourquoi nous pouvons simplement utiliser $ class = new Class1 (); quand on a déjà défini "use Person \ Barnes \ David;" avant?
user345602

4
@ user346665 vous devez utiliser use Person\Barnes\David\Class1;pour faire $class = new Class1();. Avec use Person\Barnes\David;vous devez faire $class = new David\Class1();. Le usemot-clé en lui-même est l'équivalent de use Person\Barnes\David\Class1 as Class1;ou use Person\Barnes\David as David;, respectivement pour chaque exemple.
Justin C

Pour ceux qui liront en 2018, utilisez la solution @ prince-billy-graham avec spl_autoload_register
Bruno de Oliveira

26

Comme mentionné Pascal MARTIN, vous devez remplacer le '\' par DIRECTORY_SEPARATOR par exemple:

$filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
include($filename);

Je vous suggère également de réorganiser la structure du répertoire, pour rendre le code plus lisible. Cela pourrait être une alternative:

Structure du répertoire:

ProjectRoot
 |- lib

Fichier: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Créez le sous-répertoire pour chaque espace de noms défini.

Fichier: /ProjectRoot/test.php

define('BASE_PATH', realpath(dirname(__FILE__)));
function my_autoloader($class)
{
    $filename = BASE_PATH . '/lib/' . str_replace('\\', '/', $class) . '.php';
    include($filename);
}
spl_autoload_register('my_autoloader');

use Person\Barnes\David as MyPerson;
$class = new MyPerson\Class1();
  • J'ai utilisé la recommandation de php 5 pour la déclaration du chargeur automatique. Si vous utilisez toujours PHP 4, remplacez-le par l'ancienne syntaxe: function __autoload ($ class)

18

Votre __autoloadfonction recevra le nom de classe complet, y compris le nom de l'espace de noms.

Cela signifie que, dans votre cas, la __autoloadfonction recevra « Person\Barnes\David\Class1», et pas seulement « Class1».

Donc, vous devez modifier votre code de chargement automatique, pour gérer ce genre de nom "plus compliqué"; une solution souvent utilisée est d'organiser vos fichiers en utilisant un niveau de répertoire par "niveau" d'espaces de noms, et, lors du chargement automatique, de remplacer « \» dans le nom de l'espace de noms par DIRECTORY_SEPARATOR.


1
Ce n'est pas ce que j'ai trouvé. Quand j'ai mis la déclaration die ($ class); dans la fonction __autoload, il a imprimé 'Class1 "', pas 'Person \ Barnes \ David \ Class1'
David Barnes

Vrai. Le paramètre $ class de l'autoload est le nom de la classe tel qu'écrit dans l'appel du constructeur.
tishma

1
Downvote pour "Votre __autoloadfonction recevra le nom de classe complet, y compris le nom de l'espace de noms" - ceci n'est vrai que si vous avez explicitement used la classe que vous essayez de référencer, pas si vous avez simplement used l'espace de noms auquel elle appartient. L'erreur de l'OP était qu'il avait usecréé l'espace de noms contenant une classe et s'attendait alors à ce que sa fonction de chargement automatique passe comme par magie le chemin de classe complet. Cette réponse ne répond pas vraiment à l'erreur du PO.
Mark Amery

15

Je fais quelque chose comme ceci: voir cet exemple GitHub

spl_autoload_register('AutoLoader');

function AutoLoader($className)
{
    $file = str_replace('\\',DIRECTORY_SEPARATOR,$className);

    require_once 'classes' . DIRECTORY_SEPARATOR . $file . '.php'; 
    //Make your own path, Might need to use Magics like ___DIR___
}

3
Sympa et simple. Si on devrait chercher ça.)
dennis

3

J'ai trouvé ce bijou de Flysystem

spl_autoload_register(function($class) {
    $prefix = 'League\\Flysystem\\';

    if ( ! substr($class, 0, 17) === $prefix) {
        return;
    }

    $class = substr($class, strlen($prefix));
    $location = __DIR__ . 'path/to/flysystem/src/' . str_replace('\\', '/', $class) . '.php';

    if (is_file($location)) {
        require_once($location);
    }
});

3

Je vois que les fonctions de chargement automatique ne reçoivent que le nom de classe "complet" - avec tous les espaces de noms le précédant - dans les deux cas suivants:

[a] $a = new The\Full\Namespace\CoolClass();

[b] use The\Full\Namespace as SomeNamespace; (at the top of your source file) followed by $a = new SomeNamespace\CoolClass();

Je vois que les fonctions de chargement automatique NE reçoivent PAS le nom de classe complet dans le cas suivant:

[c] use The\Full\Namespace; (at the top of your source file) followed by $a = new CoolClass();

MISE À JOUR: [c] est une erreur et ce n'est pas la façon dont les espaces de noms fonctionnent de toute façon. Je peux signaler qu'au lieu de [c], les deux cas suivants fonctionnent également bien:

[d] use The\Full\Namespace; (at the top of your source file) followed by $a = new Namespace\CoolClass();

[e] use The\Full\Namespace\CoolClass; (at the top of your source file) followed by $a = new CoolClass();

J'espère que cela t'aides.


En remarque, le usemot-clé ne fonctionne pas correctement dans l'interface de ligne de commande interactive PHP ( php --interactive);
Andrew Larsson

3

J'utilise ce simple hack en une seule ligne:

spl_autoload_register(function($name){
        require_once 'lib/'.str_replace('\\','/',$name).'.php';
    });

1

eu le même problème et vient de trouver ceci:

Lorsque vous créez une structure de sous-dossiers correspondant aux espaces de noms des classes contenant, vous n'aurez même jamais à définir un chargeur automatique.

    spl_autoload_extensions(".php"); // comma-separated list
    spl_autoload_register();

Ça a marché comme sur des roulettes

Plus d'infos ici: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

EDIT: cela pose problème sous Linux à cause de la barre oblique inverse ... Voir ici pour une solution de travail par immeëmosol

Le chargement automatique des espaces de noms fonctionne sous Windows, mais pas sous Linux


1

L'utilisation a un piège, bien que ce soit de loin la méthode la plus rapide, elle s'attend également à ce que tous vos noms de fichiers soient en minuscules.

spl_autoload_extensions(".php");
spl_autoload_register();

Par exemple:

Un fichier contenant la classe SomeSuperClass devrait être nommé somesuperclass.php, c'est un piège lors de l'utilisation d'un système de fichiers sensible à la casse comme Linux, si votre fichier est nommé SomeSuperClass.php mais pas un problème sous Windows.

L'utilisation de __autoload dans votre code peut toujours fonctionner avec les versions actuelles de PHP, mais attendez-vous à ce que cette fonctionnalité devienne obsolète et finalement supprimée à l'avenir.

Alors, quelles sont les options restantes:

Cette version fonctionnera avec PHP 5.3 et supérieur et autorise les noms de fichiers SomeSuperClass.php et somesuperclass.php. Si vous utilisez 5.3.2 et versions ultérieures, cet autochargeur fonctionnera encore plus rapidement.

<?php

if ( function_exists ( 'stream_resolve_include_path' ) == false ) {
    function stream_resolve_include_path ( $filename ) {
        $paths = explode ( PATH_SEPARATOR, get_include_path () );
        foreach ( $paths as $path ) {
            $path = realpath ( $path . PATH_SEPARATOR . $filename );
            if ( $path ) {
                return $path;
            }
        }
        return false;
    }
}

spl_autoload_register ( function ( $className, $fileExtensions = null ) {
    $className = str_replace ( '_', '/', $className );
    $className = str_replace ( '\\', '/', $className );
    $file = stream_resolve_include_path ( $className . '.php' );
    if ( $file === false ) {
        $file = stream_resolve_include_path ( strtolower ( $className . '.php' ) );
    }
    if ( $file !== false ) {
        include $file;
        return true;
    }
    return false;
});

2
en str_replace ([ '_','\\'] '/', $className );
passant

Tant qu'il n'a pas d'importance si le fichier php est en majuscules / minuscules, les répertoires restent sensibles à la casse
Mike

1

J'ai récemment trouvé la réponse de tanerkuc très utile! Je voulais juste ajouter qu'utiliser strrpos()+ substr()est légèrement plus rapide que explode()+ end():

spl_autoload_register( function( $class ) {
    $pos = strrpos( $class, '\\' );
    include ( $pos === false ? $class : substr( $class, $pos + 1 ) ).'.php';
});

1

Je vais jeter mes deux cents pour les débutants relatifs ou ce qui ne veut pas d'une configuration simple spl_autoload_register () sans toute la théorie: créez simplement un fichier php pour chaque classe, nommez ce fichier php comme votre nom de classe et conservez vos fichiers de classe dans le même répertoire que votre fichier php en question, alors cela fonctionnera:

spl_autoload_register(function ($class_name) {
    require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . $class_name . '.php';
});

Googler les pièces à l'intérieur de cette fonction devrait expliquer comment cela fonctionne. PS: J'utilise Linux, et cela fonctionne sous Linux. Les utilisateurs de Windows devraient d'abord le tester.


1

https://thomashunter.name/blog/simple-php-namespace-friendly-autoloader-class/

Vous voudrez mettre vos fichiers de classe dans un dossier nommé Classes, qui se trouve dans le même répertoire que le point d'entrée de votre application PHP. Si les classes utilisent des espaces de noms, les espaces de noms seront convertis dans la structure de répertoires.

Contrairement à beaucoup d'autres chargeurs automatiques, les traits de soulignement ne seront pas convertis en structures de répertoires (il est difficile de faire des pseudo espaces de noms PHP <5.3 avec PHP> = 5.3 espaces de noms réels).

<?php
class Autoloader {
    static public function loader($className) {
        $filename = "Classes/" . str_replace("\\", '/', $className) . ".php";
        if (file_exists($filename)) {
            include($filename);
            if (class_exists($className)) {
                return TRUE;
            }
        }
        return FALSE;
    }
}
spl_autoload_register('Autoloader::loader');

Vous voudrez placer le code suivant dans votre script PHP principal (point d'entrée):

require_once("Classes/Autoloader.php");

Voici un exemple de disposition de répertoire:

index.php
Classes/
  Autoloader.php
  ClassA.php - class ClassA {}
  ClassB.php - class ClassB {}
  Business/
    ClassC.php - namespace Business; classC {}
    Deeper/
      ClassD.php - namespace Business\Deeper; classD {}

0
<?php
spl_autoload_register(function ($classname){
   // for security purpose
   //your class name should match the name of your class "file.php"
   $classname = str_replace("..", "", $classname);
   require_once __DIR__.DIRECTORY_SEPARATOR.("classes/$classname.class.php");
});
try {
  $new = new Class1();
} catch (Exception $e) {
   echo "error = ". $e->getMessage();
}
?>

1
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la raison et / ou la manière dont ce code répond à la question améliore sa valeur à long terme.
Ethan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.