autoloader
Nu doe ik dit:
$xml_reader = new library_xml_reader();
Hoe ziet dit er dan uit als ik namespaces gebruik? En blijft de class-naam wel gewoon "library_xml_reader", of verandert daar ook iets aan?
Met namespaces ziet het zo eruit
Zonder namespaces zo
Allebei als filename library/xml/reader.php
Ik hoop dat je deze vragen nog kunt beantwoorden:
1) Boven de class zet jij "namespace Library\Xml;" Dit is dus eigenlijk gewoon de directory waar de class in staat?
2) Als ik namespaces gebruik, hoe roep ik dan een class aan? $reader = new Library\Xml\Reader ?
3) Wat is nu precies het voordeel van namespaces ten opzichte van underscores?
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
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
<?php
namespace A {
class X { ... }
class Y { ... }
class Z { ... }
var_dump(X::class, Y::class, Z::class);
// output:
// string(3) "A\X"
// string(3) "A\Y"
// string(3) "A\Z"
}
namespace B {
class X { ... }
class Y { ... }
class Z { ... }
var_dump(X::class, Y::ckass, Z::class);
// output:
// string(3) "B\X"
// string(3) "B\Y"
// string(3) "B\Z"
}
// Buiten de namespace A zullen we dit moeten doen om de zelfde output te krijgen.
// var_dump(A\X::class, A\Y::class, A\Z::class);
// var_dump(B\X::class, B\Y::class, B\Z::class);
?>
namespace A {
class X { ... }
class Y { ... }
class Z { ... }
var_dump(X::class, Y::class, Z::class);
// output:
// string(3) "A\X"
// string(3) "A\Y"
// string(3) "A\Z"
}
namespace B {
class X { ... }
class Y { ... }
class Z { ... }
var_dump(X::class, Y::ckass, Z::class);
// output:
// string(3) "B\X"
// string(3) "B\Y"
// string(3) "B\Z"
}
// Buiten de namespace A zullen we dit moeten doen om de zelfde output te krijgen.
// var_dump(A\X::class, A\Y::class, A\Z::class);
// var_dump(B\X::class, B\Y::class, B\Z::class);
?>
2) Dat is optie een, optie twee is use Library\Xml\Reader as Reader; $reader = new Reader;
wat ook nog kan is use Library\Xml; $reader = new Xml\Reader;
3) Het voordeel is wanneer binnen een namespace werkt of je het 'use' gebruikt om een namespace te importeren.
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$internal = new MyFrameworkName\MVC\Request\Client\Internal;
$client = new MyFrameworkName\MVC\Request\Client($internal);
?>
$internal = new MyFrameworkName\MVC\Request\Client\Internal;
$client = new MyFrameworkName\MVC\Request\Client($internal);
?>
versus
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
use MyframeworkName\MVC\Request\Client;
$interal = new Client\Internal;
$client = new Client($internal);
?>
use MyframeworkName\MVC\Request\Client;
$interal = new Client\Internal;
$client = new Client($internal);
?>
Gewijzigd op 05/10/2013 19:22:22 door Dos Moonen
2) Ja, zo roep je hem aan. Of mooier, met het gebruik van aliasing:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
use Library\Xml\Reader as XmlReader; // klasse Reader van namespace Library\Xml heeft nu de alias XmlReader gekregen in dit document
$reader = new XmlReader();
?>
use Library\Xml\Reader as XmlReader; // klasse Reader van namespace Library\Xml heeft nu de alias XmlReader gekregen in dit document
$reader = new XmlReader();
?>
3) De voordelen:
- Namespace scope. Variabelen en klassen bestaan alleen in die namespace en niet in anderen. Dat betekend dat je geen problemen hebt met 2 dezelfde classnamen.
- Simpel aan te passen. Stel de klasse wordt ineens Wj\Xml\Reader, dan hoeven we maar 1 use statement aan te passen en alle klassen zijn geupdate!
- Minder schrijfwerk. Geef toe, 3x Library_Xml_Reader schrijven of 1 keer Libary\Xml\Reader schrijven en vervolgens alleen Reader, wat is sneller?
- Zie ook http://net.tutsplus.com/tutorials/php/namespacing-in-php-2/
Edit:
Nee, nogmaals. Het noemen van xml_reader is al namespacing, aangezien je al de klasse reader in de xml namespace 'stopt'. De berichten op pagina 1 zijn een beetje verwarrend, aangezien de mensen daar praatte over namespacing als ze bedoelde het maken van een root namespace: Library
Vanaf PHP5.3 bevat PHP ook nog namespace support en dat is wat we in pagina 2 en wat in de algehele PHP wereld wordt bedoelt als men over namespacing praat.
Quote:
Zo bedoel je? (en is dit dan wat je noemt namespacing?)
Nee, nogmaals. Het noemen van xml_reader is al namespacing, aangezien je al de klasse reader in de xml namespace 'stopt'. De berichten op pagina 1 zijn een beetje verwarrend, aangezien de mensen daar praatte over namespacing als ze bedoelde het maken van een root namespace: Library
Vanaf PHP5.3 bevat PHP ook nog namespace support en dat is wat we in pagina 2 en wat in de algehele PHP wereld wordt bedoelt als men over namespacing praat.
Gewijzigd op 05/10/2013 19:26:23 door Wouter J
Even een paar dingen...
NOLot schrijft boven een class "namespace Library\Xml;" terwijl DOS "namespace A {" schrijft. Het verschil wat ik zie is de accolade. Ik werk met losse classes. Dus 1 class per bestand. Begrijp ik goed dat je dan de versie van NOLot gebruikt (zonder accolades) en dat je accolades gebruikt wanneer je er meerdere classes tussenzet?
Dus als ik het goed begrijp moet ik boven iedere class de namespace aangeven (die in principe overeenkomt met de directory). En als ik dan een class nodig heb, dan moet ik eerst use [namespace] doen, en vervolgens kan ik dan pas de class gebruiken.
Oké. Maar hoe pas ik dit verhaal dan weer in een autoloader toe? Want een autoloader krijgt alleen de class-naam binnen. Hoe weet die dan weer om welke namespace het gaat?
En om het nog leuker te maken, als je een service-container gebruikt, dan moet je dus ook aangeven wat de namespace is. Dan kun je geen use gebruiken, dus wordt het alsnog library\xml\reader neem ik aan?
Pfff... lastig!
Ozzie PHP op 05/10/2013 19:48:21:
En om het nog leuker te maken, als je een service-container gebruikt, dan moet je dus ook aangeven wat de namespace is. Dan kun je geen use gebruiken, dus wordt het alsnog library\xml\reader neem ik aan?
Pfff... lastig!
Pfff... lastig!
Hoe kun je nou uberhaupt gaan nadenken over het gebruiken van een service-container als je nog niet eens begrijpt wat namespaces zijn en doen???
Als je maar 1 class per file schrijft kan je gewoon namespace X; doen, dan krijgt elke class in je file die namespace.
Ozzie PHP op 05/10/2013 19:48:21:
Dus als ik het goed begrijp moet ik boven iedere class de namespace aangeven (die in principe overeenkomt met de directory). En als ik dan een class nodig heb, dan moet ik eerst use [namespace] doen, en vervolgens kan ik dan pas de class gebruiken.
Heb je dit al geprobeerd te testen? Open je editor en test!
Ozzie PHP op 05/10/2013 19:48:21:
Oké. Maar hoe pas ik dit verhaal dan weer in een autoloader toe? Want een autoloader krijgt alleen de class-naam binnen. Hoe weet die dan weer om welke namespace het gaat?
Open je editor, en schrijf dit
Probeer dan een paar classes aan te roepen, en kijk wat voor waarden je krijgt
Beide notaties kunnen, je kunt ze niet mixen in 1 bestand. In het algemeen gebruik je altijd de versie zonder accolades, behalve als je meerdere namespaces in 1 bestand zet, dan gebruik je de versie met accolades. In dat geval is alleen alles binnen de accolades in die namespace, in het eerste geval (zonder accolades) is alles eronder in die namespace.
>> Dus als ik het goed begrijp moet ik boven iedere class de namespace aangeven (die in principe overeenkomt met de directory). En als ik dan een class nodig heb, dan moet ik eerst use [namespace] doen, en vervolgens kan ik dan pas de class gebruiken.
Ja, dat begrijp je goed. Al kun je ook gewoon direct new \Library\Xml\Reader() doen (merk de eerste \ op, dit zorgt ervoor dat we vanuit de global (root) namespace werken en niet vanuit de huidige namespace).
>> Oké. Maar hoe pas ik dit verhaal dan weer in een autoloader toe? Want een autoloader krijgt alleen de class-naam binnen. Hoe weet die dan weer om welke namespace het gaat?
PHP developers zijn niet dom, de autoloader krijg de FQCN (fully qualified class name) mee, oftewel het volledige namespace + de naam van de class.
>> Dan kun je geen use gebruiken, dus wordt het alsnog library\xml\reader neem ik aan?
Exact!
NOLot - op 05/10/2013 19:55:39:
Hoe kun je nou uberhaupt gaan nadenken over het gebruiken van een service-container als je nog niet eens begrijpt wat namespaces zijn en doen???
Ik heb al eerder een service-container gemaakt hoor.
@Wouter. Thanks!
"Al kun je ook gewoon direct new \Library\Xml\Reader() doen (merk de eerste \ op, dit zorgt ervoor dat we vanuit de global (root) namespace werken en niet vanuit de huidige namespace)."
Wat me opvalt, is dat ongeacht of ik zeg $foo = new \library\foo of $foo = new library\foo dat de class-naam in de autoloader "library\foo" is, dus telkens zonder slash.
En wat ik ook raar vind... stel ik zet class Foo in de root van de library, en boven de class zet ik "namespace library;". Nu is het volgende het geval:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
use library;
$foo = new Foo();
// werkt niet
use library\foo;
$foo = new Foo();
// werkt wel
?>
use library;
$foo = new Foo();
// werkt niet
use library\foo;
$foo = new Foo();
// werkt wel
?>
Waarom is het niet voldoende om "use library" te zeggen? En waarom moet ik daar de class-naam foo aan toe voegen?
Oh ja, Wouter, als ik dan de class-naam binnenkrijg, is het dan de bedoeling dat ik die explode op een backward slash en dat ik dan het eerste element van de array pak, en aan de hand daarvan de juiste directory bepaal?
Gewijzigd op 05/10/2013 23:37:27 door Ozzie PHP
Je kan niet zomaar heel je library usen. Je mag enkel inladen wat je op dat moment nodig hebt.
Waarom moet je new typen om een nieuw object aan te maken? Waarom moet je $ gebruiken voor variablen? Waarom moet je $this-> gebruiken als je een class instantie wil gebruiken? Omdat de php developers dat zo gemaakt hebben. Overigens als je use library; doet, en dan denkt dat als je new Foo() doet hij automatisch library\foo pakt, dat klinkt me totaal niet logisch in de oren. Wat nou als je dit doet?
Quote:
Oh ja, Wouter, als ik dan de class-naam binnenkrijg, is het dan de bedoeling dat ik die explode op een backward slash en dat ik dan het eerste element van de array pak, en aan de hand daarvan de juiste directory bepaal?
Het lijkt mij wel aardig als je eens gaat zoeken op bepaalde termen die hier al regelmatig voorbij zijn gekomen en als je dan even die documentjes leest. Het is altijd leuk als de topic starter ook wat inzet toont...
Dus:
1) Ga naar google en zoek op php psr-0
2) De eerste link lijkt al de goede te zijn, klik daar dus op
3) Lees het document volledig door van boven naar beneden
4) Kopieer eventueel de gegeven autoloader in je project
5) Klaar is kees!
6) Nu nog namespaces leren
7) Zoek daarvoor op php namespace
8) Al die php.net troep is te onduidelijk, maar de 3e link is gelukkig van het geweldige nettuts+
9) Klik op de 3e link
10) Lees ook dit artikel zorgvuldig van boven naar beneden
11) Heb je het nu door? Dan ga je wel naar php.net: language.namespaces
12) Heb je het nu niet door? Dan ga je terug naar de zoekresultaten en pak je het volgende artikel (doe maar die van daylerees.com)
13) Ga nu lekker wat spelen met een projectje met namepaces
14) Heb je het nog steeds niet door? Dan ga je naar phphulp.nl en stel je je vragen.
@Wouter: lol. Die tutorial van Dayle Rees had ik trouwens (ook) gelezen en die was gelukkig duidelijk. Op het moment dat ik een class heb met bijv. namespace Ozzie\library\foo en ik wil binnen die class een exception gooien, dan moet ik die dus vanuit de global space gooien middels throw new \Exception(), en als het een eigen exception betreft, dan wordt het bijv. throw new \Ozzie\library\Exception\Input(). Correct?
Wat betreft psr-o lees ik dit:
Code (php)
1
2
3
4
2
3
4
\Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
\Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
\Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
\Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php
\Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
\Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
\Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php
Hier wordt er vanuit gegaan dat alle classes in 1 en dezelfde library staan, namelijk "/path/to/project/lib/". Maar wat nu als je models en controllers ergens anders staan (bijvoorbeeld in een module). Hoe doe je dat dan met autoloading? Per class aangeven of het om een library class (new Ozzie\library\foo) gaat of om een controller of model (new Ozzie\controller\foo of new Ozzie\model\foo). En dan in de autoloader bepalen aan de hand van de class-naam welk path moet worden gebruikt? Is dat de handigste manier, of is er nog een andere optie?
Toevoeging op 06/10/2013 13:40:50:
Oh, nog iets wat ik tegenkom...
In 1 document werkt dit:
Maar kan ik nu ook nog terug naar de global namespace? Ergens las ik dat als ik de namespace leeglaat "namespace;" dat je dan naar de global namespace gaat, maar dat levert bij mij een parse error op.
Gewijzigd op 06/10/2013 13:20:59 door Ozzie PHP
Gewijzigd op 06/10/2013 13:55:43 door Wouter J
Oké, dat had je inderdaad al verteld. Als ik je dus goed begrijp, kun je zodra je een namespace instelt "namespace foo;" niet meer terug naar de global namespace? En klopt mijn beredenering wat betreft het autoloaden?
Quote:
Op het moment dat ik een class heb met bijv. namespace Ozzie\library\foo en ik wil binnen die class een exception gooien, dan moet ik die dus vanuit de global space gooien middels throw new \Exception(), en als het een eigen exception betreft, dan wordt het bijv. throw new \Ozzie\library\Exception\Input(). Correct?
Ja, correct. Behalve dat ik dus use statements zou gebruiken... :)
Quote:
Maar wat nu als je models en controllers ergens anders staan (bijvoorbeeld in een module). Hoe doe je dat dan met autoloading? Per class aangeven of het om een library class (new Ozzie\library\foo) gaat of om een controller of model (new Ozzie\controller\foo of new Ozzie\model\foo). En dan in de autoloader bepalen aan de hand van de class-naam welk path moet worden gebruikt? Is dat de handigste manier, of is er nog een andere optie?
Ik raad je aan eens te kijken hoe de Symfony PSR-0 classloader dit doet: http://symfony.com/doc/current/components/class_loader/class_loader.html (ook leuk voor jouw zijn performance specifieke class loaders: http://symfony.com/doc/current/components/class_loader/cache_class_loader.html )
Lees ook eens de PSR-4 specificaties (zie voor de stappen om dit te vinden mijn vorige reactie). Deze is specifiek gemaakt voor verschillende packages. Een goed voorbeeld van een PSR-4 class loader is de Composer autoloader.
Ah oke... dat wordt dan zoiets als
use Ozzie\library\Exception as Exception
En als je dan een Exception throwt doe je zoiets:
throw new Exception\Input('ongeldige input');
Zo bedoel je? En waar plaats je zo'n use statement, plaats je die binnen de class zelf, of erboven?
Ah, dat voorbeeld dat jij gaf werkt met prefixes... moet ik eens over nadenken hoe ik dat dan zou moeten implementeren...
Quote:
Zo bedoel je? En waar plaats je zo'n use statement, plaats je die binnen de class zelf, of erboven?
Ja, dat bedoelde ik. Vaak heb je verschillende soorten exception klassen, dus alias je de extension namespace. Vaak heb je echter maar 1 klasse, dan alias je de FQCN.
En waar je die plaatst? In een blok tussen het namespace statement en de class declaratie.
Oké, top. Thanks!