Van het weekend probeerde ik een class aan te maken met een dynamische classname en opeens kwam ik tot de ontdekking dat dat niet helemaal werkte zoals ik had verwacht.
De sitatuatie is dat ik een factory class heb in de namespace 'API'. In de namespace 'API\Flickr' heb ik een class met de naam Datamapper (volledig dus \API\Flickr\Datamapper). Nu heb ik de volgende situaties geprobeerd en het verbaasde me dat de de tweede niet werkt:
<?php
//code dus in de factory \API\Factory die \API\Flickr\Datamapper moet aanmaken
//direct aanmaken, werkt
$obj = new Flickr\Datamapper()
//met een dynamische naam, werkt niet
$classname = 'Flickr\\Datamapper';
$obj = new $classname();
//dynamische naam met volledige namespace, werkt wel
$classname = '\\API\\Flickr\\Datamapper';
$obj = new $classname();
?>
Kan iemand mij uitleggen waarom de tweede optie met relatieve namespace niet werkt, terwijl het wel werkt als de classname niet dynamisch is?
We missen hier wat context, aangezien de eerste onder normale omstandigheden in een "Fatal error: Class 'Flickr\Datamapper' not found" zou moeten stranden.
Serieus? Enige wat ik daarop kan zeggen is dat de eerste wel werkt, waarschijnlijk omdat dat wordt opgelost in mijn autoloader (?).
Mijn namespace kennis is nog vrij pril, dus wellicht dat ik nog gewoon wat kennis mis. Maar als ik het goed begrijp is de classname Flickr\Datamapper een relatieve namespace tov de factory waarin die wordt aangemaakt en is de absolute namespace dan \API\Flickr. Mijn autoloader krijgt dus als naam door \API\Flickr\Datamapper en vertaalt dat naar een pad waarin het bestand gevonden kan worden met de Datamapper class. Tot zover werkt het in elk geval en mijn veronderstelling was dat dat ook zou moeten werken met een dynamische classname.
Het lijkt me een kleinigheid. Als je $classname = 'Flickr\\Datamapper' inkort tot $classname = 'Flickr\Datamapper' met één backslash, dan werkt het wel:
Daar zeg je me wat, even de autoloader de classnames laten printen die hij doorkrijgt. Bij de eerste optie krijgt hij door '\API\Flickr\Datamapper', dus inclusief volledige namespace. Bij de tweede optie krijgt hij door 'Flickr\Datamapper', dus dan wordt de namespace blijkbaar niet aangevuld.
Het komt doordat de dynamische versie (blijkbaar) buiten de huidige scope uitgevoerd wordt, oftewel in de globale scope.
Oftewel de huidige namespace en eventuele uses worden genegeerd.
Had hier alweer een reply op gedicht.... maar die is verdwenen. Nog een keer dan :-)
Het lijkt er inderdaad op dat de huidige namespace in zo'n situatie niet meegenomen wordt. Vraag blijft bij mij wel staan waarom.
Oplossing is er op zich wel, zoals gemeld in de eerst post, door gewoon de volledige namespace op te geven. Nog ietsje mooier wat mij betreft kwam ik net achter, ik kan de huidige namespace er ook voorzetten via de __NAMESPACE__ constante. Dus dan krijg ik dit:
<?php
$classname = __NAMESPACE__.'\\Flickr\\Datamapper';
$obj = new $classname();
?>
Dit werkt ook en vind ik op het moment de best werkbare oplossing.