osob.de Unicorn Logo
Blogpost overview

PHPStorm und PSR-11 Container | 10.01.2021

<p>Das Ziel von PSR-11 besteht darin, zu standardisieren, wie Frameworks und Bibliotheken einen Container verwenden, um (gemeinsame) Objekte und Parameter abzurufen. Egal, ob nun PHP-DI oder eine andere PSR-11-kompatible Bibliothek genutzt wird: Es macht Spaß und hilft, besseren Code zu schreiben.</p> <p>Wer, wie ich, PHPStorm nutzt, wird sicherlich irgendwann mal den Wunsch gehabt haben, auch bei Containern die Autovervollständigung zu nutzen. Das ist relativ einfach über die Attributierung im Quellcode mithilfe von PHPDoc zu realisieren. Dazu wird bei der Deklaration der Variable einfach das @var-Attribut auf die Klasse gesetzt:</p> <pre>&lt;?php // File1.php<br>/** @var Psr\Log\LoggerInterface LoggerInterface **/ $loggerInterface = $container-&gt;get(\Logger::class);</pre> <p>Nun hatte ich jedoch den Fall, dass ich in einem anderen Datei-Scope (also innerhalb von PHPStorm in einem zweiten Dokument, welches nicht direkt, sondern nur über Composer in den PHP-Code-Scope eingebunden wird) zu arbeiten hatte. Ich hatte also die Dateien File1.php und File2.php. Und die IDE konnte nicht erkennen, dass File2.php auf das Objekt aus File1.php zurückgriff.</p> <p>

You like what you find here? A donation helps to keep this project running.

</p> <p>Innerhalb von File2.php funktionierte der Container natürlich (und ich erhalte den entsprechenden Service bzw. die Funktionen/Methoden), jedoch war die Autovervollständigung auf die identische Attributierung angewiesen (im Datei-Scope von File2.php). Grundsätzlich ist dies kein wirkliches Problem (auch wenn dadurch der Garbage Collector ein wenig mehr Futter erhielt). Jedoch hätte eine Implementierung eines anderen Interfaces ein komplettes Reflektieren des PHP-Codes notwendig gemacht. Hand aufs Herz: Auch das ist schnell in PHPStorm gemacht.</p> <p>PHPStorm bietet aber noch eine weitere Möglichkeit, die Autovervollständigung für PSR-11-kompatible Container zu realisieren: durch <a href="https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#create-metadata-files-inside-your-project">PHPStorm advanced metadata</a>. Dabei wird eine Datei mit dem Namen .phpstorm.meta.php im Root-Verzeichnis des Projektes angelegt.</p> <p>In dieser werden dann die verschiedenen Provider/Services definiert, die dann im Script genutzt werden. Jedoch gibt es da noch ein Problem: Aktuell werden nur Konstanten und Strings unterstützt. Für die Klassen, die über ::class definiert werden, funktioniert es nicht. Zumindest nicht ganz sauber. Ich war nicht der <a href="https://youtrack.jetbrains.com/issue/WI-51632">einzige mit der Absicht bzw. dem Problem</a> und so wurde hier schon der Feature-Wunsch artikuliert.</p> <p>Damit das Ganze nun doch funktioniert, wird in der PHPSTORM_META nicht die Klasse mit ::class eingebunden, sondern als String FQDN hinterlegt. Für mein Logger-Beispiel bedeutet dies:</p> <pre>&lt;?php namespace PHPSTORM_META {

override(\Psr\Container\ContainerInterface::get(),
    map([
        &#039;\Logger&#039; =&amp;gt; \Psr\Log\LoggerInterface::class
    ])
);

}</pre> <p>Am Ende des Tages funktioniert es genau so, wie es soll. Auch wenn es eigentlich zwei verschiedene Dinge sind, die hier zum Funktionieren vermischt werden. Aber es funktioniert. Der Vorteil besteht nun darin, dass an einer zentralen Stelle die notwendige Konfiguration vorgenommen werden kann und im eigentlichen Code auf die Methoden und Attribute des Containers zurückgegriffen werden kann.</p>