Bagaimana cara menggunakan namespace PHP dengan autoload?

104

Saya mendapatkan kesalahan ini ketika mencoba menggunakan muat otomatis dan ruang nama:

Kesalahan fatal: Kelas 'Class1' tidak ditemukan di /usr/local/www/apache22/data/public/php5.3/test.php pada baris 10

Adakah yang bisa memberi tahu saya apa yang saya lakukan salah?

Ini kode saya:

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

?>
David Barnes
sumber

Jawaban:

119

Kelas1 tidak dalam lingkup global.

Lihat di bawah untuk contoh kerja:

<?php

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

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Edit (2009-12-14):

Hanya untuk memperjelas, penggunaan "gunakan ... sebagai" saya adalah untuk menyederhanakan contoh.

Alternatifnya adalah sebagai berikut:

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

atau

use Person\Barnes\David\Class1;

// ...

$class = new Class1();
tanerkay.dll
sumber
1
Anda tidak harus menggunakan AS. Itu tidak mengapa solusi ini berhasil. Anda dapat dengan mudah melakukan: use Person\Barnes\David\Class1;(yang setara dengan use Person\Barnes\David\Class1 as Class1;).
cartbeforehorse
1
Terima kasih, ini berhasil. Tapi saya tidak mengerti mengapa kita bisa menggunakan $ class = new Class1 (); ketika kita telah mendefinisikan "gunakan Orang \ Barnes \ David;" sebelumnya?
pengguna345602
4
@ user346665 harus Anda gunakan use Person\Barnes\David\Class1;untuk melakukannya $class = new Class1();. Dengan use Person\Barnes\David;Anda harus melakukannya $class = new David\Class1();. Kata usekunci itu sendiri sama dengan use Person\Barnes\David\Class1 as Class1;atau use Person\Barnes\David as David;, masing-masing untuk setiap contoh.
Justin C
Untuk mereka yang membaca di 2018, gunakan solusi @ prince-billy-graham dengan spl_autoload_register
Bruno de Oliveira
26

Seperti yang disebutkan Pascal MARTIN, Anda harus mengganti '\' dengan DIRECTORY_SEPARATOR misalnya:

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

Saya juga menyarankan Anda untuk mengatur ulang struktur direktori, untuk membuat kode lebih mudah dibaca. Ini bisa menjadi alternatif:

Struktur direktori:

ProjectRoot
 |- lib

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

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Buat sub direktori untuk setiap namespace yang Anda tentukan.

Mengajukan: /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();
  • Saya menggunakan rekomendasi php 5 untuk deklarasi autoloader. Jika Anda masih menggunakan PHP 4, gantilah dengan sintaks lama: function __autoload ($ class)
Kostanos
sumber
18

__autoloadFungsi Anda akan menerima nama kelas lengkap, termasuk nama namespace.

Artinya, dalam kasus Anda, __autoloadfungsi tersebut akan menerima ' Person\Barnes\David\Class1', dan tidak hanya ' Class1'.

Jadi, Anda harus memodifikasi kode pemuatan otomatis Anda, untuk menangani nama yang "lebih rumit" semacam itu; solusi yang sering digunakan adalah mengatur file Anda menggunakan satu tingkat direktori per "level" ruang nama, dan, saat memuat otomatis, ganti ' \' di nama ruang nama dengan DIRECTORY_SEPARATOR.

Pascal MARTIN
sumber
1
Ini bukan yang saya temukan. Ketika saya meletakkan pernyataan die ($ class); dalam fungsi __autoload, itu mencetak 'Class1 "', bukan 'Person \ Barnes \ David \ Class1'
David Barnes
Benar. $ class parameter autoload adalah nama kelas seperti yang tertulis dalam pemanggilan konstruktor.
tishma
1
Suara negatif untuk " __autoloadFungsi Anda akan menerima nama-kelas lengkap, termasuk nama ruang nama" - ini hanya berlaku jika Anda telah secara eksplisit used kelas yang Anda coba rujuk, bukan jika Anda hanya used ruang nama yang dimilikinya. Kesalahan OP adalah bahwa dia akan usemenggunakan namespace yang berisi kelas dan kemudian mengharapkan fungsi muat otomatisnya secara ajaib melewati jalur kelas penuh. Jawaban ini tidak benar-benar mengatasi kesalahan OP.
Mark Amery
15

Saya melakukan sesuatu seperti ini: Lihat Contoh GitHub ini

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___
}
tika
sumber
3
Bagus dan sederhana. Jika seseorang harus mencari itu.)
dennis
3

Saya menemukan permata ini dari 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);
    }
});
boksiora
sumber
3

Saya melihat bahwa fungsi muat otomatis hanya menerima nama kelas "lengkap" - dengan semua ruang nama sebelumnya - dalam dua kasus berikut:

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

Saya melihat bahwa fungsi muat otomatis TIDAK menerima nama kelas lengkap dalam kasus berikut:

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

UPDATE: [c] adalah kesalahan dan bukan cara kerja namespace. Saya dapat melaporkan bahwa, alih-alih [c], dua kasus berikut juga berfungsi dengan baik:

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

Semoga ini membantu.

Daniel Rhodes
sumber
Sebagai catatan tambahan, usekata kunci tidak bekerja dengan baik dalam antarmuka baris perintah interaktif PHP ( php --interactive);
Andrew Larsson
3

Saya menggunakan peretasan sederhana ini dalam satu baris:

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

memiliki masalah yang sama dan baru saja menemukan ini:

Saat Anda membuat struktur subfolder yang cocok dengan ruang nama kelas yang memuatnya, Anda bahkan tidak perlu mendefinisikan pemuat otomatis.

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

Ini bekerja seperti pesona

Info lebih lanjut di sini: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

EDIT: ini menyebabkan masalah di Linux karena garis miring terbalik ... Lihat di sini untuk solusi kerja oleh immeëmosol

Namespace Autoload berfungsi di bawah windows, tetapi tidak di Linux

JohnWolf
sumber
1

Menggunakan memiliki gotcha, meskipun sejauh ini merupakan metode tercepat, ia juga mengharapkan semua nama file Anda menjadi huruf kecil.

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

Sebagai contoh:

File yang berisi kelas SomeSuperClass perlu diberi nama somesuperclass.php, ini adalah gotcha saat menggunakan sistem file yang peka huruf besar kecil seperti Linux, jika file Anda bernama SomeSuperClass.php tetapi bukan masalah di Windows.

Penggunaan __autoload dalam kode Anda mungkin masih berfungsi dengan versi PHP saat ini, tetapi fitur ini diharapkan tidak digunakan lagi dan akhirnya dihapus di masa mendatang.

Jadi pilihan apa yang tersisa:

Versi ini akan bekerja dengan PHP 5.3 ke atas dan memungkinkan nama file SomeSuperClass.php dan somesuperclass.php. Jika Anda menggunakan 5.3.2 dan yang lebih baru, pemuat otomatis ini akan bekerja lebih cepat.

<?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;
});
Michael Bush
sumber
2
sebagai catatan tambahan, str_replace ([ '_','\\'] '/', $className );dua kali lebih cepat dari dua str_replace
Itay Moav -Malimovka
Selama tidak masalah jika file php dalam huruf besar / kecil, direktori masih tetap case sensitif
Mike
1

Saya baru-baru ini menemukan jawaban tanerkuc sangat membantu! Hanya ingin menambahkan bahwa menggunakan strrpos()+ substr()sedikit lebih cepat dari explode()+ end():

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

Saya akan memberikan dua sen saya untuk pemula relatif atau yang tidak menginginkan pengaturan spl_autoload_register () sederhana tanpa semua teori: Cukup buat satu file php untuk setiap kelas, beri nama file php itu sama dengan nama kelas Anda, dan simpan file kelas Anda di direktori yang sama dengan file php Anda yang dimaksud, maka ini akan berfungsi:

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

Googling bagian di dalam fungsi ini harus menjawab cara kerjanya. PS: Saya menggunakan Linux, dan ini berfungsi di Linux. Orang-orang Windows harus mengujinya terlebih dahulu.


sumber
1

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

Anda akan ingin meletakkan file kelas Anda ke dalam folder bernama Classes, yang ada di direktori yang sama dengan titik masuk ke aplikasi PHP Anda. Jika kelas menggunakan ruang nama, ruang nama akan diubah menjadi struktur direktori.

Tidak seperti banyak pemuat otomatis lainnya, garis bawah tidak akan diubah menjadi struktur direktori (sulit untuk melakukan PHP <5.3 pseudo namespaces bersama dengan PHP> = 5.3 ruang nama asli).

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

Anda akan ingin menempatkan kode berikut ke dalam skrip PHP utama Anda (titik masuk):

require_once("Classes/Autoloader.php");

Berikut adalah contoh tata letak direktori:

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 {}
Yuvraj Singh Shekhawat
sumber
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();
}
?>
Nevil Saul Blemuss
sumber
1
Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana kode ini menjawab pertanyaan tersebut meningkatkan nilai jangka panjangnya.
Ethan