¿Cómo uso los espacios de nombres PHP con carga automática?


104

Recibo este error cuando intento usar la carga automática y los espacios de nombres:

Error fatal: la clase 'Class1' no se encuentra en /usr/local/www/apache22/data/public/php5.3/test.php en la línea 10

¿Alguien puede decirme qué estoy haciendo mal?

Aquí está mi código:

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();

?>

Respuestas:


119

Class1 no está en el ámbito global.

Vea a continuación un ejemplo de trabajo:

<?php

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

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Editar (14/12/2009):

Solo para aclarar, mi uso de "use ... as" fue para simplificar el ejemplo.

La alternativa fue la siguiente:

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

o

use Person\Barnes\David\Class1;

// ...

$class = new Class1();

1
No tienes que usar AS. No es por eso que esta solución funciona. También podría hacer: use Person\Barnes\David\Class1;(que es equivalente a use Person\Barnes\David\Class1 as Class1;).
cartbeforehorse

1
Gracias, esto funciona. Pero no puedo entender por qué podemos usar $ class = new Class1 (); cuando ya hemos definido "use Person \ Barnes \ David;" antes?
user345602

4
@ user346665 debe usar use Person\Barnes\David\Class1;para poder hacerlo $class = new Class1();. Con use Person\Barnes\David;debe hacer $class = new David\Class1();. La usepalabra clave en sí misma es el equivalente de use Person\Barnes\David\Class1 as Class1;o use Person\Barnes\David as David;, respectivamente, para cada ejemplo.
Justin C

Para aquellos que lean en 2018, use la solución @ prince-billy-graham con spl_autoload_register
Bruno de Oliveira

26

Como mencionó Pascal MARTIN, debe reemplazar el '\' con DIRECTORY_SEPARATOR, por ejemplo:

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

También le sugiero que reorganice la estructura del directorio para que el código sea más legible. Esta podría ser una alternativa:

Estructura de directorios:

ProjectRoot
 |- lib

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

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Cree el subdirectorio para cada espacio de nombres que esté definido.

Expediente: /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();
  • Usé la recomendación de php 5 para la declaración del cargador automático. Si todavía está con PHP 4, reemplácelo con la sintaxis anterior: función __autoload ($ class)

18

Su __autoloadfunción recibirá el nombre de clase completo, incluido el nombre del espacio de nombres.

Esto significa que, en su caso, la __autoloadfunción recibirá ' Person\Barnes\David\Class1', y no solo ' Class1'.

Entonces, tienes que modificar tu código de carga automática, para lidiar con ese tipo de nombre "más complicado"; Una solución que se usa con frecuencia es organizar sus archivos usando un nivel de directorio por "nivel" de espacios de nombres y, cuando se cargue automáticamente, reemplazar ' \' en el nombre del espacio de nombres por DIRECTORY_SEPARATOR.


1
Esto no es lo que encontré. Cuando puse la sentencia die ($ class); en la función __autoload, imprimió 'Class1 "', no 'Person \ Barnes \ David \ Class1'
David Barnes

Cierto. El parámetro $ class de autoload es el nombre de la clase tal como está escrito en la llamada al constructor.
tishma

1
Voto en contra de "Su __autoloadfunción recibirá el nombre de clase completo, incluido el nombre del espacio de nombres" ; esto solo es cierto si ha used explícitamente la clase a la que está tratando de hacer referencia, no si simplemente ha used el espacio de nombres al que pertenece. El error del OP fue que había usecreado el espacio de nombres que contenía una clase y luego esperaba que su función de carga automática pasara mágicamente el classpath completo de alguna manera. Esta respuesta realmente no aborda el error del OP.
Mark Amery

15

Hago algo como esto: Ver este ejemplo de 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
Agradable y sencillo. Si uno debería estar buscando eso.)
dennis

3

Encontré esta joya 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

Veo que las funciones de carga automática solo reciben el nombre de clase "completo", con todos los espacios de nombres precediéndolo, en los dos casos siguientes:

[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();

Veo que las funciones de carga automática NO reciben el nombre de clase completo en el siguiente caso:

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

ACTUALIZACIÓN: [c] es un error y no es así como funcionan los espacios de nombres de todos modos. Puedo informar que, en lugar de [c], los siguientes dos casos también funcionan 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();

Espero que esto ayude.


Como nota al margen, la usepalabra clave no funciona correctamente en la interfaz de línea de comandos interactiva de PHP ( php --interactive);
Andrew Larsson

3

Utilizo este simple truco en una línea:

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

1

tenía el mismo problema y acaba de encontrar esto:

Cuando crea una estructura de subcarpetas que coincida con los espacios de nombres de las clases que las contienen, nunca tendrá que definir un autocargador.

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

Funcionó a las mil maravillas

Más información aquí: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

EDITAR: esto causa un problema en Linux debido a la barra invertida ... Vea aquí la solución de trabajo de immeëmosol

Autoload del espacio de nombres funciona en Windows, pero no en Linux


1

El uso tiene un problema, aunque es, con mucho, el método más rápido, también espera que todos los nombres de archivo estén en minúsculas.

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

Por ejemplo:

Un archivo que contenga la clase SomeSuperClass debería llamarse somesuperclass.php, esto es un problema cuando se usa un sistema de archivos sensible a mayúsculas y minúsculas como Linux, si su archivo se llama SomeSuperClass.php pero no es un problema en Windows.

El uso de __autoload en su código puede seguir funcionando con las versiones actuales de PHP, pero se espera que esta característica quede obsoleta y finalmente se elimine en el futuro.

Entonces, qué opciones quedan:

Esta versión funcionará con PHP 5.3 y superior y permite los nombres de archivo SomeSuperClass.php y somesuperclass.php. Si usa 5.3.2 y superior, este autocargador funcionará aún más rápido.

<?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
como nota al margen, str_replace ([ '_','\\'] '/', $className );es dos veces más rápido que dos str_replace
Itay Moav -Malimovka

Siempre que no importe si el archivo php está en mayúsculas / minúsculas, los directorios siguen siendo sensibles a las mayúsculas y minúsculas
Mike

1

¡Recientemente encontré la respuesta de tanerkuc muy útil! Solo quería agregar que usar strrpos()+ substr()es un poco más rápido que explode()+ end():

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

1

Daré mis dos centavos por principiantes relativos o lo que sea que desee una configuración simple de spl_autoload_register () sin toda la teoría: simplemente cree un archivo php para cada clase, nombre ese archivo php igual que el nombre de su clase y mantenga sus archivos de clase en el mismo directorio que su archivo php en cuestión, esto funcionará:

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

Buscar en Google las piezas dentro de esta función debería responder cómo funciona. PD: uso Linux y esto funciona en Linux. La gente de Windows debería probarlo primero.


1

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

Querrá poner sus archivos de clase en una carpeta llamada Classes, que está en el mismo directorio que el punto de entrada a su aplicación PHP. Si las clases usan espacios de nombres, los espacios de nombres se convertirán a la estructura de directorio.

A diferencia de muchos otros cargadores automáticos, los guiones bajos no se convertirán en estructuras de directorio (es complicado hacer PHP <5.3 pseudo espacios de nombres junto con PHP> = 5.3 espacios de nombres reales).

<?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');

Querrá colocar el siguiente código en su script PHP principal (punto de entrada):

require_once("Classes/Autoloader.php");

Aquí hay un ejemplo de diseño de directorio:

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
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo.
Ethan
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.