Làm cách nào để sử dụng không gian tên PHP với tính năng tự động nạp?


104

Tôi gặp lỗi này khi cố gắng sử dụng tính năng tự động nạp và không gian tên:

Lỗi nghiêm trọng: Không tìm thấy lớp 'Class1' trong /usr/local/www/apache22/data/public/php5.3/test.php trên dòng 10

Bất cứ ai có thể cho tôi biết những gì tôi đang làm sai?

Đây là mã của tôi:

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

?>

Câu trả lời:


119

Class1 không thuộc phạm vi toàn cầu.

Xem bên dưới để biết ví dụ hoạt động:

<?php

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

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Chỉnh sửa (2009-12-14):

Chỉ để làm rõ, cách sử dụng "use ... as" của tôi là để đơn giản hóa ví dụ.

Phương án thay thế như sau:

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

hoặc là

use Person\Barnes\David\Class1;

// ...

$class = new Class1();

1
Bạn không cần phải sử dụng AS. Đó không phải là lý do tại sao giải pháp này hoạt động. Bạn có thể dễ dàng làm như sau: use Person\Barnes\David\Class1;(tương đương với use Person\Barnes\David\Class1 as Class1;).
cartbeforehorse

1
Cảm ơn, điều này hoạt động. Nhưng tôi không thể hiểu tại sao chúng ta chỉ có thể sử dụng $ class = new Class1 (); khi chúng ta đã xác định "sử dụng Person \ Barnes \ David;" trước đây?
user345602

4
@ user346665 bạn phải sử dụng use Person\Barnes\David\Class1;để thực hiện $class = new Class1();. Với use Person\Barnes\David;bạn phải làm $class = new David\Class1();. Bản thân usetừ khóa tương đương với use Person\Barnes\David\Class1 as Class1;hoặc use Person\Barnes\David as David;, tương ứng cho mỗi ví dụ.
Justin C

Đối với những người đọc vào năm 2018, sử dụng @ hoàng tử-billy-graham giải pháp với spl_autoload_register
Bruno de Oliveira

26

Như đã đề cập Pascal MARTIN, bạn nên thay thế '\' bằng DIRECTORY_SEPARATOR, ví dụ:

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

Ngoài ra, tôi khuyên bạn nên tổ chức lại cấu trúc thư mục, để làm cho mã dễ đọc hơn. Đây có thể là một giải pháp thay thế:

Cấu trúc thư mục:

ProjectRoot
 |- lib

Tập tin: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Tạo thư mục con cho mỗi vùng tên mà bạn đã xác định.

Tập tin: /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();
  • Tôi đã sử dụng gợi ý php 5 để khai báo trình tải tự động. Nếu bạn vẫn sử dụng PHP 4, hãy thay thế nó bằng cú pháp cũ: function __autoload ($ class)

18

__autoloadHàm của bạn sẽ nhận được tên lớp đầy đủ, bao gồm cả tên không gian tên.

Điều này có nghĩa là, trong trường hợp của bạn, __autoloadhàm sẽ nhận ' Person\Barnes\David\Class1' chứ không phải chỉ ' Class1'.

Vì vậy, bạn phải sửa đổi mã tự động tải của mình, để đối phó với loại tên "phức tạp hơn" đó; một giải pháp thường được sử dụng là sắp xếp các tệp của bạn bằng cách sử dụng một cấp độ thư mục cho mỗi "cấp độ" không gian tên và khi tự động tải, hãy thay thế ' \' trong tên không gian tên bằng DIRECTORY_SEPARATOR.


1
Đây không phải là những gì tôi tìm thấy. Khi tôi đã đặt câu lệnh die ($ class); trong hàm __autoload, nó in ra 'Class1 "', không phải 'Person \ Barnes \ David \ Class1'
David Barnes

Thật. Tham số $ class của autoload là tên lớp như được viết trong lệnh gọi hàm tạo.
tishma

1
Phản đối cho " __autoloadHàm của bạn sẽ nhận được tên lớp đầy đủ, bao gồm cả tên không gian tên" - điều này chỉ đúng nếu bạn đã xác định rõ ràng usevề lớp mà bạn đang cố gắng tham chiếu, chứ không phải nếu bạn chỉ used là không gian tên mà nó thuộc về. Sai lầm của OP là anh ta đã useđặt không gian tên có chứa một lớp và sau đó mong đợi hàm autoload của anh ta sẽ được chuyển một cách kỳ diệu đến toàn bộ classpath bằng cách nào đó. Câu trả lời này không thực sự giải quyết được sai lầm của OP.
Mark Amery,

15

Tôi làm điều gì đó như thế này: Xem ví dụ GitHub này

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
Đẹp và đơn giản. Nếu một nên tìm kiếm đó).
dennis

3

Tôi tìm thấy viên ngọc này từ 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

Tôi thấy rằng các hàm autoload chỉ nhận được tên lớp "đầy đủ" - với tất cả các không gian tên trước nó - trong hai trường hợp sau:

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

Tôi thấy rằng các hàm tự động tải KHÔNG nhận được tên lớp đầy đủ trong trường hợp sau:

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

CẬP NHẬT: [c] là một sai lầm và không phải là cách không gian tên hoạt động. Tôi có thể báo cáo rằng, thay vì [c], hai trường hợp sau cũng hoạt động tốt:

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

Hi vọng điêu nay co ich.


Một lưu ý nhỏ là usetừ khóa không hoạt động bình thường trong giao diện dòng lệnh tương tác PHP ( php --interactive);
Andrew Larsson

3

Tôi sử dụng hack đơn giản này trong một dòng:

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

1

đã gặp vấn đề tương tự và chỉ tìm thấy điều này:

Khi bạn tạo một cấu trúc thư mục con khớp với không gian tên của các lớp chứa, bạn thậm chí sẽ không bao giờ phải xác định một trình tải tự động.

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

Nó làm việc như một say mê

Thông tin thêm tại đây: http://www.php.net/manual/en/ Chức năng.spl-autoload-register.php#92514

CHỈNH SỬA: điều này gây ra sự cố trên Linux vì dấu gạch chéo ngược ... Xem tại đây để biết giải pháp làm việc của utëmosol

Namespace Autoload hoạt động trong windows, nhưng không hoạt động trên Linux


1

Sử dụng có một gotcha, mặc dù nó là phương pháp nhanh nhất cho đến nay, nó cũng yêu cầu tất cả các tên tệp của bạn là chữ thường.

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

Ví dụ:

Một tệp chứa lớp SomeSuperClass sẽ cần được đặt tên là somesuperclass.php, đây là một lỗi khi sử dụng hệ thống tệp phân biệt chữ hoa chữ thường như Linux, nếu tệp của bạn có tên SomeSuperClass.php nhưng không phải là vấn đề trong Windows.

Sử dụng __autoload trong mã của bạn có thể vẫn hoạt động với các phiên bản PHP hiện tại nhưng hy vọng tính năng này sẽ không còn được dùng nữa và cuối cùng sẽ bị loại bỏ trong tương lai.

Vì vậy, những tùy chọn còn lại:

Phiên bản này sẽ hoạt động với PHP 5.3 trở lên và cho phép đặt tên tệp là SomeSuperClass.php và somesuperclass.php. Nếu bạn đang sử dụng 5.3.2 trở lên, trình tải tự động này sẽ hoạt động nhanh hơn.

<?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
như một mặt lưu ý, str_replace ([ '_','\\'] '/', $className );là nhanh gấp hai lần so với hai str_replace
Itay Moav -Malimovka

Chừng nào nó không quan trọng nếu các tập tin php là thượng / hạ cased, các thư mục vẫn còn trường hợp sensisive
Mike

1

Gần đây tôi thấy câu trả lời của tanerkuc rất hữu ích! Chỉ muốn thêm rằng sử dụng strrpos()+ substr()nhanh hơn một chút so với explode()+ end():

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

1

Tôi sẽ ném hai xu cho những người mới bắt đầu tương đối hoặc không muốn thiết lập spl_autoload_register () đơn giản mà không có tất cả lý thuyết: Chỉ cần tạo một tệp php cho mỗi lớp, đặt tên tệp php đó giống với tên lớp của bạn và giữ các tệp lớp của bạn trong cùng thư mục với tệp php của bạn được đề cập, sau đó điều này sẽ hoạt động:

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

Googling các phần bên trong chức năng này sẽ trả lời cách hoạt động của nó. Tái bút: Tôi sử dụng Linux, và điều này hoạt động trên Linux. Người dùng Windows nên thử nghiệm nó trước.


1

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

Bạn sẽ muốn đặt các tệp lớp của mình vào một thư mục có tên Lớp, nằm trong cùng thư mục với điểm nhập vào ứng dụng PHP của bạn. Nếu các lớp sử dụng không gian tên, thì không gian tên sẽ được chuyển đổi thành cấu trúc thư mục. Không giống như nhiều trình tải tự động khác, dấu gạch dưới sẽ không được chuyển đổi thành cấu trúc thư mục (thật khó khi thực hiện PHP <5.3 không gian tên giả cùng với PHP> = 5.3 không gian tên thực).

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

Bạn sẽ muốn đặt mã sau vào tập lệnh PHP chính của mình (điểm nhập):

require_once("Classes/Autoloader.php");

Đây là một bố cục thư mục mẫu:

index.php
Classes/
  Autoloader.php
  ClassA.php - class ClassA {}
  ClassB.php - class ClassB {}
  Business/
    ClassC.php - namespace Business classC {}
    Deeper/
      ClassD.php - namespace BusinessDeeper 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
Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp thêm ngữ cảnh liên quan đến lý do và / hoặc cách mã này trả lời câu hỏi sẽ cải thiện giá trị lâu dài của nó.
Ethan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.