ฉันจะใช้เนมสเปซ PHP กับโหลดอัตโนมัติได้อย่างไร


104

ฉันได้รับข้อผิดพลาดนี้เมื่อพยายามใช้โหลดอัตโนมัติและเนมสเปซ:

ข้อผิดพลาดร้ายแรง:ไม่พบคลาส 'Class1' ใน/usr/local/www/apache22/data/public/php5.3/test.phpในบรรทัดที่ 10

ใครช่วยบอกทีว่าฉันทำอะไรผิด?

นี่คือรหัสของฉัน:

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

?>

คำตอบ:


119

Class1 ไม่อยู่ในขอบเขตส่วนกลาง

ดูตัวอย่างการทำงานด้านล่าง:

<?php

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

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

แก้ไข (2009-12-14):

เพื่อชี้แจงว่าการใช้ "use ... as" ของฉันเป็นการทำให้ตัวอย่างง่ายขึ้น

ทางเลือกมีดังต่อไปนี้:

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

หรือ

use Person\Barnes\David\Class1;

// ...

$class = new Class1();

1
คุณไม่ต้องใช้AS. นั่นไม่ใช่เหตุผลที่วิธีนี้ใช้ได้ผล คุณสามารถทำได้อย่างง่ายดาย: use Person\Barnes\David\Class1;(ซึ่งเทียบเท่ากับuse Person\Barnes\David\Class1 as Class1;)
รถเข็นก่อน

1
ขอบคุณมันใช้งานได้ แต่ฉันไม่เข้าใจว่าทำไมเราสามารถใช้ $ class = new Class1 (); เมื่อเราได้กำหนด "use Person \ Barnes \ David;" ไว้ก่อนหน้านี้แล้ว?
user345602

4
@ user346665 คุณต้องใช้ในการดำเนินการuse Person\Barnes\David\Class1; $class = new Class1();ด้วยความที่คุณต้องทำuse Person\Barnes\David; คำหลักโดยตัวเองเป็นเทียบเท่าหรือตามลำดับสำหรับแต่ละตัวอย่าง $class = new David\Class1();useuse Person\Barnes\David\Class1 as Class1;use Person\Barnes\David as David;
Justin C

สำหรับผู้ที่อ่านในปี 2018 ให้ใช้โซลูชัน @ prince-billy-graham กับ spl_autoload_register
Bruno de Oliveira

27

ตามที่กล่าวไว้ Pascal MARTIN คุณควรแทนที่ '\' ด้วย DIRECTORY_SEPARATOR เช่น:

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

นอกจากนี้ฉันขอแนะนำให้คุณจัดโครงสร้าง dirrectory ใหม่เพื่อให้โค้ดอ่านง่ายขึ้น นี่อาจเป็นอีกทางเลือกหนึ่ง:

โครงสร้างไดเรกทอรี:

ProjectRoot
 |- lib

ไฟล์: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • สร้างไดเร็กทอรีย่อยสำหรับแต่ละเนมสเปซที่คุณกำหนดไว้

ไฟล์: /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();
  • ฉันใช้คำแนะนำ php 5 สำหรับการประกาศตัวโหลดอัตโนมัติ หากคุณยังคงใช้ PHP 4 อยู่ให้แทนที่ด้วยไวยากรณ์เก่า: function __autoload ($ class)

18

__autoloadฟังก์ชันของคุณจะได้รับชื่อคลาสแบบเต็มรวมถึงชื่อเนมสเปซ

ซึ่งหมายความว่าในกรณีของคุณ__autoloadฟังก์ชันจะได้รับ ' Person\Barnes\David\Class1' และไม่เพียง แต่ ' Class1'

ดังนั้นคุณต้องแก้ไขรหัสโหลดอัตโนมัติเพื่อจัดการกับชื่อที่ "ซับซ้อนกว่า" แบบนั้น วิธีการแก้ปัญหาที่ใช้บ่อยคือการจัดระเบียบไฟล์ของคุณโดยใช้ระดับหนึ่งในไดเรกทอรีต่อ "ระดับ" ของ namespaces และเมื่อ autoloading แทนที่ ' \' ในชื่อ namespace DIRECTORY_SEPARATORโดย


1
นี่ไม่ใช่สิ่งที่ฉันพบ เมื่อฉันใส่คำสั่งตาย ($ class); ในฟังก์ชัน __autoload พิมพ์ 'Class1' 'ไม่ใช่' Person \ Barnes \ David \ Class1 '
David Barnes

จริง. พารามิเตอร์ $ class ของ autoload คือชื่อคลาสตามที่เขียนในการเรียกตัวสร้าง
tishma

1
โหวตลงคะแนนสำหรับ" __autoloadฟังก์ชันของคุณจะได้รับชื่อคลาสแบบเต็มรวมถึงชื่อเนมสเปซ"ซึ่งจะเป็นจริงก็ต่อเมื่อคุณมีuseคลาสที่คุณพยายามจะอ้างอิงอย่างชัดเจนไม่ใช่ในกรณีที่คุณเป็นเพียงuseเนมสเปซที่เป็นของ ข้อผิดพลาดของ OP คือเขาต้องการuseเนมสเปซที่มีคลาสและจากนั้นก็คาดหวังว่าฟังก์ชันโหลดอัตโนมัติของเขาจะผ่านคลาสพา ธ แบบเต็มได้อย่างน่าอัศจรรย์ คำตอบนี้ไม่ได้กล่าวถึงความผิดพลาดของ OP
Mark Amery

15

ฉันทำสิ่งนี้: ดูตัวอย่าง 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
สวยและเรียบง่าย ถ้าใครจะมองหาสิ่งนั้น)
dennis

3

ฉันพบอัญมณีนี้จาก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

ฉันเห็นว่าฟังก์ชันโหลดอัตโนมัติรับเฉพาะชื่อคลาส "เต็ม" - โดยมีเนมสเปซทั้งหมดนำหน้า - ในสองกรณีต่อไปนี้:

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

ฉันเห็นว่าฟังก์ชันโหลดอัตโนมัติไม่ได้รับชื่อคลาสแบบเต็มในกรณีต่อไปนี้:

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

อัปเดต: [c] เป็นข้อผิดพลาดและไม่ใช่วิธีการทำงานของเนมสเปซ ฉันรายงานได้ว่าแทนที่จะเป็น [c] สองกรณีต่อไปนี้ก็ใช้ได้ดีเช่นกัน:

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

หวังว่านี่จะช่วยได้


ตามหมายเหตุด้านข้างuseคำหลักทำงานไม่ถูกต้องในอินเทอร์เฟซบรรทัดคำสั่งแบบโต้ตอบ PHP ( php --interactive);
Andrew Larsson


1

มีปัญหาเดียวกันและเพิ่งพบสิ่งนี้:

เมื่อคุณสร้างโครงสร้างโฟลเดอร์ย่อยที่ตรงกับเนมสเปซของคลาสที่มีคุณจะไม่ต้องกำหนดตัวโหลดอัตโนมัติด้วยซ้ำ

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

มันทำงานได้อย่างมีเสน่ห์

ข้อมูลเพิ่มเติมที่นี่: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

แก้ไข: สิ่งนี้ทำให้เกิดปัญหาบน Linux เนื่องจากแบ็กสแลช ... ดูวิธีแก้ปัญหาการทำงานที่นี่โดยimmeëmosol

Namespace Autoload ทำงานภายใต้ windows แต่ใช้ไม่ได้บน Linux


1

การใช้ gotcha ในขณะที่เป็นวิธีที่เร็วที่สุด แต่ก็คาดหวังว่าชื่อไฟล์ทั้งหมดของคุณจะเป็นตัวพิมพ์เล็ก

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

ตัวอย่างเช่น:

ไฟล์ที่มีคลาส SomeSuperClass จะต้องมีชื่อว่า somesuperclass.php ซึ่งเป็น gotcha เมื่อใช้ระบบไฟล์ที่มีความสำคัญกับตัวพิมพ์เล็กและใหญ่เช่น Linux หากไฟล์ของคุณชื่อ SomeSuperClass.php แต่ไม่ใช่ปัญหาใน Windows

การใช้ __autoload ในโค้ดของคุณอาจยังใช้งานได้กับ PHP เวอร์ชันปัจจุบัน แต่คาดว่าฟีเจอร์นี้จะเลิกใช้งานและสุดท้ายจะถูกลบออกในอนาคต

เหลือตัวเลือกอะไรบ้าง:

เวอร์ชันนี้จะทำงานกับ PHP 5.3 ขึ้นไปและอนุญาตให้ใช้ชื่อไฟล์ SomeSuperClass.php และ somesuperclass.php หากคุณใช้ 5.3.2 ขึ้นไปตัวโหลดอัตโนมัตินี้จะทำงานได้เร็วขึ้น

<?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
ในฐานะโน้ตด้านข้างstr_replace ([ '_','\\'] '/', $className );เร็วกว่าสอง str_replace สองเท่า
Itay Moav -Malimovka

ตราบเท่าที่ไม่สำคัญว่าไฟล์ php จะอยู่บน / ล่างหรือไม่ไดเรกทอรียังคงมีความสำคัญอยู่
Mike

1

ฉันเพิ่งพบว่าคำตอบของ tanerkuc มีประโยชน์มาก! แค่อยากจะเพิ่มว่าการใช้strrpos()+ substr()นั้นเร็วกว่าexplode()+ เล็กน้อยend():

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

1

ฉันจะทุ่มสองเซ็นต์ให้กับผู้เริ่มต้นสัมพัทธ์หรือสิ่งที่ไม่ต้องการการตั้งค่า spl_autoload_register () แบบง่ายๆโดยไม่มีทฤษฎีทั้งหมด: เพียงสร้างไฟล์ php หนึ่งไฟล์สำหรับแต่ละคลาสตั้งชื่อไฟล์ php นั้นให้เหมือนกับชื่อคลาสของคุณและเก็บไฟล์คลาสของคุณไว้ ในไดเร็กทอรีเดียวกับไฟล์ php ของคุณที่เป็นปัญหาสิ่งนี้จะใช้ได้:

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

Googling ชิ้นส่วนภายในฟังก์ชันนี้ควรตอบโจทย์ว่ามันทำงานอย่างไร PS: ฉันใช้ Linux และใช้งานได้กับ Linux ผู้ใช้ Windows ควรทดสอบก่อน


1

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

คุณจะต้องใส่ไฟล์คลาสของคุณลงในโฟลเดอร์ชื่อClassesซึ่งอยู่ในไดเร็กทอรีเดียวกับจุดเริ่มต้นในแอปพลิเคชัน PHP ของคุณ หากคลาสใช้เนมสเปซเนมสเปซจะถูกแปลงเป็นโครงสร้างไดเร็กทอรี

ซึ่งแตกต่างจากตัวโหลดอัตโนมัติอื่น ๆ จำนวนมากขีดล่างจะไม่ถูกแปลงเป็นโครงสร้างไดเร็กทอรี (การทำเนมสเปซหลอก PHP <5.3 พร้อมกับ PHP> = 5.3 เนมสเปซจริงเป็นเรื่องยาก)

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

คุณจะต้องวางโค้ดต่อไปนี้ลงในสคริปต์ PHP หลักของคุณ (จุดเริ่มต้น):

require_once("Classes/Autoloader.php");

นี่คือตัวอย่างเค้าโครงไดเรกทอรี:

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
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีที่รหัสนี้ตอบคำถามช่วยเพิ่มคุณค่าในระยะยาว
Ethan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.