Namespaces en autoloading
Ik ben al een hele tijd bezig om namespaces en autloading te laten werken.
PHP vindt op een of andere manier steeds niet de goede klasse.
Ik heb bijvoorbeeld deze code, als index.php in een private map.
Deze index.php doe ik includen op de index.php in de public map. (bootstrap manier)
/private/index.php
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
// private/index.php
use Routing\RouteCollection;
use Routing\Route;
echo "Index.php aangeroepen";
require_once("config.php");
include BASE_PATH . "/Library/Routing/RouteCollection.php";
function __autoload($path) {
$path = str_replace("\\", DIR_SEPERATOR, $path);
// Debug
echo "<br>base path: " . BASE_PATH;
echo "<br>path: " . $path;
echo "<br>absolute path: " . BASE_PATH . "/Library/" . $path . ".php";
require_once(BASE_PATH . "/Library/" . $path . ".php");
}
$routes = new RouteCollection();
$routes->add(new Route("\/", ""));
$routes->add(new Route()); // nieuwe route maken, enz.
$rm = new RouteMatcher();
$rm->setCollection($routes);
$rm->match($_SERVER['REQUEST_URI']);
?>
// private/index.php
use Routing\RouteCollection;
use Routing\Route;
echo "Index.php aangeroepen";
require_once("config.php");
include BASE_PATH . "/Library/Routing/RouteCollection.php";
function __autoload($path) {
$path = str_replace("\\", DIR_SEPERATOR, $path);
// Debug
echo "<br>base path: " . BASE_PATH;
echo "<br>path: " . $path;
echo "<br>absolute path: " . BASE_PATH . "/Library/" . $path . ".php";
require_once(BASE_PATH . "/Library/" . $path . ".php");
}
$routes = new RouteCollection();
$routes->add(new Route("\/", ""));
$routes->add(new Route()); // nieuwe route maken, enz.
$rm = new RouteMatcher();
$rm->setCollection($routes);
$rm->match($_SERVER['REQUEST_URI']);
?>
De mapstructuur is als volgt:
private
- map: Library
-- map: Routing
--- RouteCollection.php
--- Route.php
- index.php
public
- index.php
RouteCollection.php heeft namespace: Routing\RouteCollection
Route.php heeft namespace: Routing\Route
Bij het autoloaden gaat het steeds mis, de klassen kunnen niet gevonden worden. Maar ik begrijp eigenlijk ook niet echt hoe namespaces werken. En al helemaal niet hoe je het use of use ... as keyword gebruikt.
Kan iemand me hierover wat uitleggen? Ik heb al veel op internet gelezen, maar het lukt nog steeds niet echt.
Hier zijn nog de errors die PHP gaf
Code (php)
1
2
3
4
5
6
2
3
4
5
6
[22-Feb-2014 10:16:50] PHP Fatal error: Class 'Routing\RouteCollection' not found in /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php on line 22
[22-Feb-2014 10:16:50] PHP Stack trace:
[22-Feb-2014 10:16:50] PHP 1. {main}() /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php:0
[22-Feb-2014 10:17:00] PHP Fatal error: Class 'Routing\RouteCollection' not found in /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php on line 22
[22-Feb-2014 10:17:00] PHP Stack trace:
[22-Feb-2014 10:17:00] PHP 1. {main}() /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php:0
[22-Feb-2014 10:16:50] PHP Stack trace:
[22-Feb-2014 10:16:50] PHP 1. {main}() /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php:0
[22-Feb-2014 10:17:00] PHP Fatal error: Class 'Routing\RouteCollection' not found in /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php on line 22
[22-Feb-2014 10:17:00] PHP Stack trace:
[22-Feb-2014 10:17:00] PHP 1. {main}() /var/lib/stickshift/52e369455973ca150500011d/app-root/data/538262/private/index.php:0
Zou je dat dan niet eerst eens gaan proberen te begrijpen? Als je gaat autorijden zonder rijbewijs schiet het ook niet echt op toch?
Sowieso vraag ik me af waarom je een router in de bootstrap plaatst, en waarom je niet eerst een kernel/core class gaat maken waarin je dat soort zaken afhandelt. De routing hoort niet thuis in de bootstrap.
Misschien moet je even uitleggen wat je bedoelt met "het lukt niet echt. Google staat vol met uitleg over namespaces: https://www.google.nl/?q=php+namespaces#q=php+namespaces
Quote:
Bij het autoloaden gaat het steeds mis, de klassen kunnen niet gevonden worden. Maar ik begrijp eigenlijk ook niet echt hoe namespaces werken. En al helemaal niet hoe je het use of use ... as keyword gebruikt.
Kan iemand me hierover wat uitleggen? Ik heb al veel op internet gelezen, maar het lukt nog steeds niet echt.
Kan iemand me hierover wat uitleggen? Ik heb al veel op internet gelezen, maar het lukt nog steeds niet echt.
Een namespace is een nieuwe scope. Dus alles in 1 namespace hoort bij die namespace, variabelen, functies, classes, etc. In een andere namespace kun je niet zomaar bij deze namespace. Een namespace definieer je met het namespace keyword. Voorbeeldje:
Nu hebben we de PHP namespace Acme\Routing\Loader en de class YamlRoutingLoader in die namespace.
[side]
Merk op dat je hier dus ook meteen de aangerade structuur ziet van een namespace: VendorNaam\ComponentNaam(\SubComponentNaam)+
Merk op dat je hier dus ook meteen de aangerade structuur ziet van een namespace: VendorNaam\ComponentNaam(\SubComponentNaam)+
Normaal gesproken zit je in de globale namespace. Stel we zitten in de globale namespace en proberen nu deze class aan te maken, dan zal dat niet lukken:
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
require_once 'path/to/YamlRoutingLoader.php'; // autoloader komt straks pas :)
$routingLoader = new YamlRoutingLoader(); //> CLASS NOT FOUND
?>
require_once 'path/to/YamlRoutingLoader.php'; // autoloader komt straks pas :)
$routingLoader = new YamlRoutingLoader(); //> CLASS NOT FOUND
?>
Dit komt omdat we, zoals je net geleerd hebt, niet zomaar elementen van een andere namespace kunt krijgen. Om dat te willen moet je de volledige namespace + classnaam gebruiken:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
require_once '...'; // nog steeds geen autoloader...
// beginnen met een slash om zeker te zijn dat we vanuit de globale namespace
// starten, een absolute namespace dus
$routingLoader = new \Acme\Routing\Loader\YamlRoutingLoader();
?>
require_once '...'; // nog steeds geen autoloader...
// beginnen met een slash om zeker te zijn dat we vanuit de globale namespace
// starten, een absolute namespace dus
$routingLoader = new \Acme\Routing\Loader\YamlRoutingLoader();
?>
Dit werkt! Maar stel dat we hem nu 2x willen aanmaken, dan moeten we dit 2x gaan herhalen. Dat betekend dat we dit niet makkelijk kunnen veranderen. Hiervoor hebben we het use statement uitgevonden, daarmee kun je een namespace (+ eventuele klasse) "aliasen". Als je geen specifieke alias naam opgeeft wordt de laatste element van een namespace gebruikt:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
require_once '...'; // autoloader, waar ben je?
// use statements zijn altijd absoluut, eerste slash mag niet meer
use Acme\Routing\Loader\YamlRoutingLoader;
$routingLoader = new YamlRoutingLoader();
// hé, dit is een alias voor Acme\Routing\Loader\YamlRoutingLoader en
// raad eens? Die bestaat!
// of
use Acme\Routing\Loader; // nu aliasen we net de klasse, maar een namespace
$routingLoader = new Loader\YamlRoutingLoader();
// hé, Loader is een alias voor Acme\Routing\Loader en dit wordt dus
// Acme\Routing\Loader\YamlRoutingLoader, die bestaat!
// of
use Acme\Routing\Loader as RoutingLoader; // een specifieke alias naam
$routingLoader = new RoutingLoader\YamlRoutingLoader();
// het moet niet gekker worden, RoutingLoader is een alias voor Acme\Routing\Loader
// en Acme\Routing\Loader\YamlRoutingLoader bestaat nog steeds!
?>
require_once '...'; // autoloader, waar ben je?
// use statements zijn altijd absoluut, eerste slash mag niet meer
use Acme\Routing\Loader\YamlRoutingLoader;
$routingLoader = new YamlRoutingLoader();
// hé, dit is een alias voor Acme\Routing\Loader\YamlRoutingLoader en
// raad eens? Die bestaat!
// of
use Acme\Routing\Loader; // nu aliasen we net de klasse, maar een namespace
$routingLoader = new Loader\YamlRoutingLoader();
// hé, Loader is een alias voor Acme\Routing\Loader en dit wordt dus
// Acme\Routing\Loader\YamlRoutingLoader, die bestaat!
// of
use Acme\Routing\Loader as RoutingLoader; // een specifieke alias naam
$routingLoader = new RoutingLoader\YamlRoutingLoader();
// het moet niet gekker worden, RoutingLoader is een alias voor Acme\Routing\Loader
// en Acme\Routing\Loader\YamlRoutingLoader bestaat nog steeds!
?>
Ik hoop dat je dit principe een beetje snapt :)
Dan nu het autoloading verhaal, dit staat volledig los van het namespace verhaal. De enige overeenkomst is dat de autoloader de namespaces gebruikt om een klasse bestand te vinden, maar de namespaces zijn niet uitgevonden om de autoloader mogelijk te kunnen maken.
Nu kun je kiezen uit 2 heel bekende standaarden:
- PSR-0 - Dit betekend dat je bijv. instelt dat de Acme namespace gevonden kan worden in de /Library/Acme map en dat het elk afzonderlijk namespace element een map is. De klasse Acme\Routing\Loader\YamlRoutingLoader kan dan dus worden gevonden in /Library/Acme/Routing/Loader/YamlRoutingLoader.php
- PSR-4 - Dit is eigenlijk gelijk aan PSR-0, met 1 groot verschil: de namespace die je instelt wordt niet opgenomen in de mappenstructuur. Dus stel ik definieer dat Acme\Routing gevonden kan worden in de /Library/acme-routing map, dan moet Acme\Routing\Loader\YamlRoutingLoader zich dus in /Library/acme-routing/Loader/YamlRoutingLoader.php bevinden.
Deze autoloader kun je zelf maken, of je kunt hem van de PSR standaarden jatten, of je gebruikt een andere autoloader, bijv. die van Symfony2, Zend Framework of Composer.
Gewijzigd op 22/02/2014 17:19:45 door Wouter J
Bedankt voor de uitleg. Daar heb ik wat aan.