Bonjour tout le monde, :)

j'ai besoin d'une âme charitable car je sature un peu avec les namespace. Je me suis mis au namespace (encore une belle connerie), mais je comprend rien, j'ai beau lire/regarder des tuto's rien n'y fais ça ne veux pas rentrer. Donc je viens vers vous pour avoir des explications. :)

Mon architecture

J'ai fais une petite architecture pour tester tout ça, la voici :

  • Eloyas/ qui correspond au nom du projet et qui est donc par conséquent la racine du "site"
    • index.php : qui ce situe à la racine donc : Eloyas/index.php
    • app : un dossier, Eloyas/app/
    • Autoloader.php, qui ce situe à la racine du dossier app, Eloyas/app/Autoloader.php
    • tester, un dossier, Eloyas/app/tester/
      • Test.php, qui ce trouve à la racine du dossier tester, Eloyas/app/tester/Test.php
Voilà mon architecture. Passons maintenant à la question.
Ma question

Voici mes trois fichiers respectivement: index, Autoloader, Test

<?php

use \App\Autoloader;
use \App\Tester\Test;

session_start();

require_once'app/Autoloader.php';
Autoloader::register();

$test = new Test();
<?php
namespace App;

class Autoloader{

    public static function register(){
        spl_autoload_register(array(__CLASS__, 'autoload'));
    }

    public static function autoload($class){
        $class = str_replace('\\', '/', $class);
        $class = str_replace(__NAMESPACE__, strtolower(__NAMESPACE__), $class);
        require $class.'.php';
    }

}
<?php

namespace App\Tester;

class Test{
    public function __construct(){

    }
}

Dans mon fichier index.php (premier code), je suis obliger de faire un use de ma ma classe Test, pourquoi ?

Suis-je obligé d'utilisé use chaque fois ?

Si je regarde mes namespaces, ma classe Test à la même racine que mon autoloader, je ne pourrais pas faire ceci :

<?php

use \App\Autoloader;

session_start();

require_once'app/Autoloader.php';
Autoloader::register();

$test = new Test(); //PHP est con au point de ne pas comprendre que je veux ma classe Test qui est dans le namespace App/Tester/...

Voilà. D'avance merci de m'éclairé :)

Cordialement.

PS : Comment je peux activer la coloration syntaxite en markdown ?

Edit : Dans le rendu du markdown à droite, la coloration ne ce fais pas, elle ce fais lors de l'envoi. :)

11 réponses


connected
Réponse acceptée

Salut,

avant tout tu as déjà bien avancé :)

Pour te simplifier la tâche je pense que tu devrais suivre la convetion suivante : le namespace doit correspondre au chemin d'accès vers le fichier de la classe à charger.

Je m'explique, tu veux charger le fichier de la classe Test avec le namespace App\Tester. Il te faudrait donc charger le fichier physiquement sous /app/tester/Test.php. Hors dans ton cas, ton répertoire tester se trouve à la racine, son namespace devrait donc plutôt être Tester\Test.

Ce qu'il faudrait faire c'est :

/index.php :

<?php

namespace App;

// use App\Autoloader; inutile car tu charges manuellement avec un require_once plus bas
use Tester\Test; // cheimin vers notre classe Test

define('DS', DIRECTORY_SEPARATOR); // meilleur portabilité sur les différents systeme.
define('ROOT', dirname(__FILE__).DS); // pour se simplifier la vie

session_start();

require_once'app/Autoloader.php';
Autoloader::register();

$test = new Test();

/app/Autoloader.php :

<?php
namespace App;

class Autoloader{

    public static function register(){
        spl_autoload_register(array(__CLASS__, 'autoload'));
    }

    public static function autoload($class){
        // var_dump($class); => App\Tester\Test

        // on explose notre variable $class par \
        $parts = preg_split('#\\\#', $class);

        // on extrait le dernier element 
        $className = array_pop($parts);

        // on créé le chemin vers la classe
        // on utilise DS car plus propre et meilleure portabilité entre les différents systèmes (windows/linux) 

        $path = implode(DS, $parts);
        $file = $className.'.php';

        $filepath = ROOT.strtolower($path).DS.$file;

        // var_dump($filepath); => C:\xampp\htdocs\Labs\Eloyas\app\tester\Test.php
        //
        require $filepath;
    }

}

/tester/Test.php :

<?php

namespace Tester;  // le namespace n'est pas App\Tester puisqu'on se trouve à la racine

class Test{
    public function __construct(){

    }
}

Je ne sais pas si c'est clair, j'ai mis les commentaires dans le code :)

connected
Réponse acceptée

Pour éviter la boucle :

<?php
namespace App;

class Autoloader{

    public static function register(){
        spl_autoload_register(array(__CLASS__, 'autoload'));
    }

    public static function autoload($class){
      $nameSpace = explode('\\', $class);
      $nameSpace = array_map('strtolower', $nameSpace);
      $i = count($nameSpace) - 1;
      $nameSpace[$i] = ucfirst($nameSpace[$i]);      
      $class = implode('/', $nameSpace);
      require $class.'.php';
    }

}

Merci de ta réponse. Mais ma class Test ne ce trouve pas à la racine, elle ce trouve dans : /Eloyas/app/tester/Test.php

Le USE est OBLIGATOIRE... Si je ne le mets pas il n'arrive pas à trouver ma class Autoloader... Hier j'en parlais sur le chan justement je trouvais pas ça logique...

Cordialement.

Pour répondre aux questions que j'ai zappé hihi :
Tu n'est pas obligé d'utiliser use, tu peux faire l'appel à ta classe comme ceci :

<?php
$test = new \Tester\Test();
?>

Si tu as plusieurs appel à la classe ce sera plus simple de faire use que de préfixer à chaque fois avec le namespace.

Je préfère uilisé USE, ça saute directement au yeux. :)

Au temps pour moi donc il suffit juste de faire

/index.php :

<?php

namespace App;

// use App\Autoloader; inutile car tu charges manuellement avec un require_once plus bas
use App\Tester\Test; // cheimin vers notre classe Test

define('DS', DIRECTORY_SEPARATOR); // meilleur portabilité sur les différents systeme.
define('ROOT', dirname(__FILE__).DS); // pour se simplifier la vie

session_start();

require_once'app/Autoloader.php';
Autoloader::register();

$test = new Test();

/app/tester/Test.php:

<?php

namespace App\Tester; 

class Test{
    public function __construct(){

    }
}

Et tu laisses l'autoloader de ma première réponse.

C'est exactement ce que j'ai fais pour que ça marche, je voulais savoir si j'était obliger de mettre le USE, si PHP n'étais pas assez intélligent pour le faire tout seul x')

Le USE autoloader et obligatoire sinon il me retourne : Fatal error: Class 'Autoloader' not found in /home/eloyas/public_html/Eloyas/index.php on line 8

Cordialement. :)

Non tu es obligé de lui dire, il n'est pas assez intelligent :S

Pour l'erreur tu parles avec ton index.php ?

Oui sur mon index, quand j'apelle mon autoload, si j'utilise pas USE pour lui spécifier le namespace alors il trouve pas la class...

C'est normal tu ne spécifie pas de namespace au début de ton index :

<?php

namespace App;

use App\Tester\Test; // tu peux enlever le \ au tout début car on spécifie plus au qu'on est dans le namespace App

session_start();

require_once'app/Autoloader.php';
Autoloader::register();

$test = new Test();

J'ai trouvé mon erreur enfait en débuguant ma variable class dans mon autoloader. Mes namespace prenne tous une majuscule, mais pas mes dossiers résultat, il cherchait ma classe dans App/Tester/Test alors que c'était dans : app/tester/Test

Donc voici mon nouvelle autoloader :

<?php
namespace App;

class Autoloader{

    public static function register(){
        spl_autoload_register(array(__CLASS__, 'autoload'));
    }

    public static function autoload($class){
        $nameSpace = explode('\\', $class);
        foreach($nameSpace as $key =>  $value){
            if(end(array_keys($nameSpace)) !== $key){
                $nameSpace[$key] = strtolower($value);
            }
        }
        $class = implode('/', $nameSpace);

        require $class.'.php';
    }

}

Edit : Normal le fichier index est a la racine et l'autoloader est dans app, donc c'est normal. Je commence a bien comprendre la relation entre les deux. Merci à toi :)

Des avis sur mon autoload ?