php.de

Zurück   php.de > Webentwicklung > Software-Design

Software-Design Diskussionen auf Profi-Niveau: PHP Lösungen auf konzeptioneller Ebene

Antwort
 
LinkBack Themen-Optionen Bewertung: Bewertung: 1 Stimmen, 5,00 durchschnittlich.
Alt 23.06.2011, 17:01  
Erfahrener Benutzer
 
Registriert seit: 30.07.2008
Beiträge: 1.169
PHP-Kenntnisse:
Fortgeschritten
xm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphäre
Standard DI-Container

Ich habe jetzt einige Zeit an einer eigenen Implementierung eines DI-Containers gesessen und was dabei heraus gekommen ist, würde ich gerne mal zur Diskussion in den Raum werfen. Es ist noch nicht 100%ig durch getestet, aber man erkennt die Funktionsweise, denke ich. Schwerpunkte waren Performance und Flexibilität. So - los geht's:

Der eigentliche Container:
PHP-Code:
class ZN_DI_Container {

        
/**
         * @var ZN_DI_Container
         */
        
protected static $instance null;

        
/**
         * @var array
         */
        
protected $config          = array();

        
/**
         * @var array
         */
        
protected $storage         = array();

        
/**
         * @var array
         */
        
protected $loading         = array();
        
        
/**
         * Indicator for binding references
         *
         * @var string
         */
        
protected $bindingPrefix   ':';
        
        
/**
         * Indicator for service references
         *
         * @var string
         */
        
protected $servicePrefix   '@';

        
/**
         * Affords a singleton mechanism if needed
         *
         * @param boolean $new
         * @return ZN_DI_Container
         */
        
public static function instance($new false) {
            if (
$new) {
                return new 
self();
            }
            if (
self::$instance === null) {
                
self::$instance = new self();
            }

            return 
self::$instance;
        }

        public function 
__construct() {
        }

        
/**
         * @param array $config
         * @return ZN_DI_Container
         */
        
public function setConfig(array $config) {
            
$this->config $config;

            return 
$this;
        }

        
/**
         * @return array
         */
        
public function getConfig() {
            return 
$this->config;
        }

        
/**
         * Retrieves an assembled service using the specified di configuration
         *
         * @param string $serviceId
         * @param boolean $singleton = true
         * @return object
         */
        
public function get($serviceId$singleton true) {
            if (!
$this->hasServiceConfig($serviceId) && !$this->hasStoredService($serviceId)) {
                
//If no config specified, the service must be injected manually
                
throw new Exception('Service "'.$serviceId.'" must be set manually');
            }
            
            if (isset(
$this->loading[$serviceId])) {
                
//prevent endless loops
                
throw new Exception('Circular reference on "'.$serviceId.'"');
            }
            
            
$this->loading[$serviceId] = true;
            
            if (
                (
$singleton && $this->hasStoredService($serviceId)) ||
                (!
$this->hasServiceConfig($serviceId) && $this->hasStoredService($serviceId)) //Manually set service
            
) {
                
//If singleton or manually injected service -> return existing instance
                
unset($this->loading[$serviceId]);
                return 
$this->storage[$serviceId];
            }
            
            
$serviceConfig $this->getServiceConfig($serviceId);
            
$class         $serviceConfig['class'];
            
$service       null;
            
$args          = array();
            if (isset(
$serviceConfig['args']) && is_array($serviceConfig['args']) && count($serviceConfig['args']) > 0) {
                
//Constructor args available
                
$ref     = new ReflectionClass($class);
                
$service $ref->newInstanceArgs($this->prepareParamSetForMethodCall($serviceConfig['args']));
            } else {
                
$service = new $class();
            }
            
            
//Call methods which are specified in the configuration
            
if (isset($serviceConfig['methods']) && is_array($serviceConfig['methods']) && count($serviceConfig['methods']) > 0) {
                foreach (
$serviceConfig['methods'] as $method=>$calls) {
                    foreach (
$calls as $call=>$params) {
                        
$this->callServiceMethod($service$method$this->prepareParamSetForMethodCall($params));
                    }
                }
            }
            unset(
$this->loading[$serviceId]);

            if (
$singleton) {
                
$this->storage[$serviceId] = $service;
                return 
$this->storage[$serviceId];
            } else {
                return 
$service;
            }
        }
        
        
/**
         * Calls a method on a service depending on given arguments
         *
         * @param object $service
         * @param string method
         * @param array $args
         * @return mixed
         */
        
protected function callServiceMethod($service$method, array $args) {
            if (
count($args) > 1) {
                
//if there are more than one argument, use call_user_func_array
                
return call_user_func_array(array($service$method$this->prepareParamSetForMethodCall($args)));
            } else if (
count($args) == 1) {
                return 
$service->$method($args[0]);
            } else {
                return 
$service->$method();
            }
        }
        
        
/**
         * Prepares argument lists based on the specified di configuration
         *
         * @param mixed
         * @return mixed
         */
        
public function prepareParamSetForMethodCall($params) {
            if (
is_object($params)) {
                throw new 
UnexpectedValueException('Invalid data format');
            }
            if (
is_array($params)) {
                foreach (
$params as $k=>$v) {
                    if (
is_array($v)) {
                        
$params[$k] = $this->prepareParamSetForMethodCall($v);
                    } else if (
$this->isBindingReference($v)) {
                        
$params[$k] = $this->getBinding($this->getBindingIdFromString($v));
                    } else if (
$this->isServiceReference($v)) {
                        
$params[$k] = $this->get($this->getServiceIdFromString($v));
                    }
                }
            } else {
                if (
$this->isBindingReference($params)) {
                    
$params $this->getBinding($this->getBindingIdFromString($params));
                } else if (
$this->isServiceReference($params)) {
                    
$params $this->get($this->getServiceIdFromString($params));
                }
            }
            
            return 
$params;
        }

        
/**
         * Injects a service object
         *
         * @param string $serviceId
         * @param object $service
         * @param boolean $overwrite = true
         */
        
public function set($serviceId$service$overwrite true) {
            if (
$this->hasStoredService($serviceId) && !$overwrite) {
                throw new 
Exception('Service "'.$serviceId.'" is already set');
            }
            
            
$this->storage[$serviceId] = $service;

            return 
$this;
        }
        
        
/**
         * Checks if a service is already stored (injected or singleton)
         *
         * @param string $serviceId
         * @return boolean
         */
        
public function hasStoredService($serviceId) {
            return isset(
$this->storage[$serviceId]);
        }
        
        
/**
         * Checks if a configuration set for a service is set
         *
         * @param string $serviceId
         * @return boolean
         */
        
public function hasServiceConfig($serviceId) {
            return isset(
$this->config['services'][$serviceId]);
        }
        
        
/**
         * Retrieves a configuration set for a cetain service
         *
         * @param string $serviceId
         * @return array
         */
        
public function getServiceConfig($serviceId) {
            if (!
$this->hasServiceConfig($serviceId)) {
                throw new 
UnexpectedValueException('Config for service "'.$serviceId.'" not specified');
            }
            return 
$this->config['services'][$serviceId];
        }
        
        
/**
         * Specifies a configuration set for a service
         *
         * @param string $serviceId
         * @param array $config
         * @$param boolean $overwrite = true
         * @return ZN_DI_Container
         */
        
public function setServiceConfig($serviceId, array $config$overwrite true) {
            if (
$this->hasServiceConfig($serviceId) && !$overwrite) {
                throw new 
Exception('Service config for "'.$serviceId.'" is already set');
            }
            
            if (!isset(
$config['class'])) {
                throw new 
Exception('No class set for serice "'.$serviceId.'"');
            }
            
            
$this->config['services'][$serviceId] = $config;
            
            return 
$this;
        }
        
        
/**
         * Checks if a string is a reference to another service
         *
         * @param string
         * @return boolean
         */
        
public function isServiceReference($serviceId) {
            return 
is_string($serviceId) && strpos($serviceId$this->getServicePrefix()) === 0;
        }
        
        
/**
         * @param string $servicePrefix
         * @return ZN_DI_Container
         */
        
public function setServicePrefix($servicePrefix) {
            
$this->servicePrefix $servicePrefix;
            
            return 
$this;
        }
        
        
/**
         * @return string
         */
        
public function getServicePrefix() {
            return 
$this->servicePrefix;
        }
        
        
/**
         * Retrieves the service id from a string which indicates a service reference
         *
         * @param string $str
         * @return string
         */
        
public function getServiceIdFromString($str) {
            return 
substr($strstrlen($this->getServicePrefix()));
        }
        
        
/**
         * @param $bindingId
         * @return boolean
         */
        
public function hasBinding($bindingId) {
            return isset(
$this->config['bindings'][$bindingId]);
        }
        
        
/**
         * Checks if a string is a reference to binding
         *
         * @param string
         * @return boolean
         */
        
public function isBindingReference($str) {
            return 
is_string($str) && strpos($str$this->getBindingPrefix()) === 0;
        }
        
        
/**
         * Retrieves the binding id from a string which indicates a binding reference
         *
         * @param string $str
         * @return string
         */
        
public function getBindingIdFromString($str) {
            return 
substr($strstrlen($this->getBindingPrefix()));
        }
        
        
/**
         * Injects a binding
         *
         * @param string bindingId
         * @param mixed $value
         * @return ZN_DI_Container
         */
        
public function setBinding($bindingId$value) {
            
$this->config['bindings'][$bindingId] = $value;
            
            return 
$this;
        }
        
        
/**
         * Retrieves a specified binding by an id. If $recursive
         * is true the result is checked if it is a binding reference
         * itself
         *
         * @param string $bindingId
         * @param boolean $recursive = true
         */
        
public function getBinding($bindingId$recursive true) {
            if (!
$this->hasBinding($bindingId)) {
                throw new 
UnexpectedValueException('Binding "'.$bindingId.'" not specified or invalid');
            }
            
            
$rsl $this->config['bindings'][$bindingId];
            
            if (
$this->isBindingReference($rsl)) {
                return 
$this->getBinding($this->getBindingIdFromString($rsl), $recursive);
            } else {
                return 
$rsl;
            }
        }
        
        
/**
         * @param string $bindingPrefix
         * @return ZN_DI_Container
         */
        
public function setBindingPrefix($bindingPrefix) {
            
$this->bindingPrefix $bindingPrefix;
            
            return 
$this;
        }
        
        
/**
         * @return string
         */
        
public function getBindingPrefix() {
            return 
$this->bindingPrefix;
        }
    } 
Eine Bsp.-Konfiguration:
PHP-Code:

    
return array(
        
'bindings'=>array(
            
'dbDSN'=>'mysql:host=localhost;dbname=...',
            
'dbUser'=>'root',
            
'dbPassword'=>'123456',
            
'dbEncodingCommand'=>'SET NAMES UTF8',
            
'dbDefaultFetchMode'=>PDO::FETCH_ASSOC
        
),
        
'services'=>array(
            
'test'=>array(
                
'class'=>'TestClass',
                
'args'=>array(),
                
'methods'=>array(
                    
'setService'=>array(
                        array(
'@service'//Muss manuell injiziert werden, da nicht vorhanden
                    
),
                    
'setDb'=>array(
                        array(
'@dbConnection')
                    )
                )
            ),
            
'dbConnection'=>array(
                
'class'=>'PDO',
                
'args'=>array(
                    
':dbDSN',
                    
':dbUser',
                    
':dbPassword',
                    array(
                        
PDO::MYSQL_ATTR_INIT_COMMAND=>':dbEncodingCommand',
                        
PDO::ATTR_DEFAULT_FETCH_MODE=>':dbDefaultFetchMode'
                    
)
                )
            )
        )
    ); 
Ein paar Beispiele:
PHP-Code:
    class TestClass {
        
        public function 
setService(StdClass $service) {
        
        }
        
        public function 
setDb(PDO $db) {
        
        }
    }
    
    require_once(
dirname(__FILE__).'/zn/di/container.php');
    
    
$di ZN_DI_Container::instance();
    
$di->setConfig(require(dirname(__FILE__).'/config.php'));
    
    
    
//Binding setzen
    
$di->setBinding('key''value');
    
    
//Service manuell setzen
    
$obj = new StdClass();
    
$di->set('service'$obj);
    
    
var_dump($di->get('test'));
    
    
//$db = $di->get('dbConnection'); 
Ich denke, die Konfiguration ist selbst erklärend. Wer sich wundert, dass es bei den "methods" immer doppelt verschachtelte Arrays gibt: Das hat den Grund, dass man einen Funktionsaufruf mehrmal durchführen kann (z. B. mit verschiedenen Parametern).

Was haltet ihr von der ganzen Sache? Gibt es was, was verbessert werden könnte? Was, was gar nicht geht?
xm22 ist offline   Mit Zitat antworten
Sponsor Mitteilung
PHP Code Flüsterer

Registriert seit: 21.08.2005
Beiträge: 4682
PHP-Kenntnisse:
Fortgeschritten

Alt 24.06.2011, 18:55  
Erfahrener Benutzer
 
Registriert seit: 21.12.2009
Beiträge: 415
PHP-Kenntnisse:
Fortgeschritten
G.Schuster ist zur Zeit noch ein unbeschriebenes Blatt
Standard

Wozu sol ldas gut sein, den Singleton umgehen zu können?
Zudem kommt man an die neue Instanz nie wieder ran, sofern man sie nicht händisch weiterreicht.
Ausserdem würde ich den Konstruktor auf protected setzen, soll ja eigentlich nicht von extern instanziierbar sein.
__________________
actra.development - Schwabstr. 2 - 70825 Münchingen
www.actra.de/velopment/ - eMail: php.de@actra.de
Zend Certified Engineer for PHP5
G.Schuster ist offline   Mit Zitat antworten
Alt 25.06.2011, 19:27  
Erfahrener Benutzer
 
Registriert seit: 30.07.2008
Beiträge: 1.169
PHP-Kenntnisse:
Fortgeschritten
xm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphäre
Standard

Der Gedanke war, dass man einerseits immer eine Singleton-Instanz verfügbar haben kann, was zu 99,9% der Fall sein wird. Wenn es aber notwendig sein sollte - aus was für Gründen auch immer - dann kann man sich eben eine weitere Instanz holen. Das mit dem protected Konstruktor stimmt.
xm22 ist offline   Mit Zitat antworten
Alt 27.06.2011, 11:16  
Erfahrener Benutzer
 
Registriert seit: 02.09.2009
Beiträge: 1.020
PHP-Kenntnisse:
Fortgeschritten
mquadrat befindet sich auf einem aufstrebenden Ast
Standard

Schaut printipiell ganz gut aus. Die Bindings sind nett. Mir persönlich ist das zu viel Konfiguration. Da kriegst du unter Umständen Probleme falls du mit Third-Party-Modulen/Bundles arbeitest. Die müssen nämlich alle auf die gleiche Config..
__________________
Wir suchen PHP Entwickler (Vollzeit) im Raum Darmstadt / Rhein-Main. Infos via E-Mail mueller@new-frontiers.de
mquadrat ist offline   Mit Zitat antworten
Alt 27.06.2011, 15:02  
Erfahrener Benutzer
 
Registriert seit: 30.07.2008
Beiträge: 1.169
PHP-Kenntnisse:
Fortgeschritten
xm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphäre
Standard

Mhja.. Das ist halt die Sache - Mir ist keine Lösung eingefallen, mit der man die Flexibilität so weit erhalten kann. Viele DI-Container, die ich gesehen hatte, unterstützen nur Setter und bei verschachtelten Parametern war sowieso bei allen Schluss.

Was mir noch vorschwebt, ist ein Konfigurationsobjekt, das man auch mit yaml, ini oder xml füttern kann.
xm22 ist offline   Mit Zitat antworten
Alt 27.06.2011, 17:38  
Erfahrener Benutzer
 
Registriert seit: 02.09.2009
Beiträge: 1.020
PHP-Kenntnisse:
Fortgeschritten
mquadrat befindet sich auf einem aufstrebenden Ast
Standard

Ich favorisiere den Ansatz von MEF bei .NET. Da MEF prinzipiell dafür sorgen soll, dass man Extensions einfach nur in das Programmverzeichnis kopieren muss und alles funktioniert, gibt es dort überhaupt keine starre Konfiguration. Die verwenden Autodiscovery auf Basis von dynamischen Katalogen und Annotationen.

Verschachtelte Parameter sind immer kompliziert. Ob MEF das kann, weiß ich jetzt gar nicht. Ggf. müsste man dafür auch ne Kombination aus MEF und Unity verwenden (eigenständiger DI Container für .NET). Wobei man sicherlich seine Strukturen auch so wählen kann, dass man sich die Verschachtelei sparen kann.
__________________
Wir suchen PHP Entwickler (Vollzeit) im Raum Darmstadt / Rhein-Main. Infos via E-Mail mueller@new-frontiers.de
mquadrat ist offline   Mit Zitat antworten
Alt 28.06.2011, 09:27  
Erfahrener Benutzer
 
Registriert seit: 30.07.2008
Beiträge: 1.169
PHP-Kenntnisse:
Fortgeschritten
xm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphäre
Standard

Wenn ich das richtig verstanden habe, wird da die Konfiguration quasi programmatisch injiziert. Das ist wiederum was, worauf ich nicht so stehe. Die zentrale Verwaltung in einer Datei gefällt mir besser, weil ich immer weiß, wo ich was zu ändern habe und ich muss im Falle einer Änderung die eigentliche Applikation nicht anfassen. Oder habe ich da was falsch verstanden?
xm22 ist offline   Mit Zitat antworten
Alt 28.06.2011, 09:56  
Erfahrener Benutzer
 
Registriert seit: 30.07.2008
Beiträge: 1.169
PHP-Kenntnisse:
Fortgeschritten
xm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphärexm22 sorgt für eine eindrucksvolle Atmosphäre
Standard

Zitat:
Wobei man sicherlich seine Strukturen auch so wählen kann, dass man sich die Verschachtelei sparen kann.
Ich glaube, das Verschachteln von Parametern ist eine Spezialität von PHP. Ich kenne keine andere Sprache, in der man als Parameter assoziative Arrays als Parameter benutzt. Das ist der einzige Hinkefuß an der der ganzen Sache. Ansonsten könnte man alles mit Service-Referenzen erledigen.

Ich muss leider zugeben, dass meine .Net-Kenntnisse nicht ausreichen, um durchzublicken, was mef und unity letztendlich machen, außer, dass sie für DI genutzt werden können.


EDIT: Bzgl. der Konfiguration: Was auch Prämisse war, war, dass man fremde Objekte benutzen kann, ohne irgendwas an denen oder für die anpassen zu müssen, sondern einen einzigen generischen Weg hat, die zu holen.

Geändert von xm22 (28.06.2011 um 10:11 Uhr).
xm22 ist offline   Mit Zitat antworten
Alt 28.06.2011, 10:44  
Erfahrener Benutzer
 
Registriert seit: 02.09.2009
Beiträge: 1.020
PHP-Kenntnisse:
Fortgeschritten
mquadrat befindet sich auf einem aufstrebenden Ast
Standard

In allen anderen Sprachen würde man dafür eine Klasse anlegen

Bei MEF (Managed Extensibility Framework) geht es darum Plugin-Architekturen umzusetzen. Die tatsächlich verwendeten Plugins / Module stehen zur Design-Time nicht fest, entsprechend kann auch keine feste Kopplung verwendet werden. Jedes Modul kann eigene Services zur Verfügung stellen (Export) oder Services konsumieren (Import). Da die Module aber nicht bekannt sind, entfällt natürlich auch die Möglichkeit Abhängigkeiten zu konfigurieren. Unity ist ein klassischer DI-Container mit Konfiguration.

Sicher, über Konfiguration kannst du fremde Objekte nutzen ohne diese zu ändern. Du kannst allerdings keine Dienste konfigurieren, von denen du noch gar nicht weißt, dass die später mal zur Verfügung gestellt werden Beide Ansätze sind valide. Man könnte sogar einen hybriden Ansatz nehmen. Autodiscovery für Module, die für dieses Framework erstellt wurden und Konfiguration für Third-Party-Libraries.
__________________
Wir suchen PHP Entwickler (Vollzeit) im Raum Darmstadt / Rhein-Main. Infos via E-Mail mueller@new-frontiers.de
mquadrat ist offline   Mit Zitat antworten
Alt 28.06.2011, 10:47  
moderatives Dielektrikum
 
Benutzerbild von nikosch
 
Registriert seit: 21.05.2008
Beiträge: 35.994
PHP-Kenntnisse:
Fortgeschritten
nikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunft
Standard

Zitat:
Du kannst allerdings keine Dienste konfigurieren, von denen du noch gar nicht weißt, dass die später mal zur Verfügung gestellt werden
Da DI quasi global arbeitet, ist es sowieso recht gefährlich, Dienste zu nutzen, die nur vielleicht mal zur Verfügung gestellt werden
__________________
--
One pixel is still too big. Please make it smaller. ASAP.

Initiative Mittelstand.
Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers.
--
nikosch ist offline   Mit Zitat antworten
Antwort


Themen-Optionen
Thema bewerten
Thema bewerten:

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are an
Gehe zu

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
Dependency Injection Container Anyone PHP-Fortgeschrittene 33 16.06.2011 08:28
jQuery <div> Container generieren lassen ? dreamcatcher JavaScript, Ajax und mehr 5 23.02.2011 14:01
[Erledigt] Mehrere DIV Container mit einem Klick ändern (mit mehreren Request Aufrufe Lebenssonde JavaScript, Ajax und mehr 11 26.08.2010 09:01
Netz von Objekten mit Abhängigkeiten darstellen/zeichnen serPHPico PHP-Fortgeschrittene 12 25.08.2010 18:52
kürzeren div container authoatisch der höhe des contends anpassen litterauspirna HTML, Usability und Barrierefreiheit 15 29.07.2009 00:57
2 div Container immer gleich lang Kein Genie HTML, Usability und Barrierefreiheit 4 27.07.2009 13:38
Links vom Untermenü in Container öffnen Surfer PHP Tipps 2008 0 27.12.2008 19:24
[Erledigt] Zentrieren von a Blocks im DIV Container Daniel HTML, Usability und Barrierefreiheit 21 24.09.2008 09:37
[CSS] Container in Container Igäl HTML, Usability und Barrierefreiheit 5 04.09.2007 11:29
CSS - Bild im div container überlappt andere container Buschdieb HTML, Usability und Barrierefreiheit 13 19.07.2007 23:17
IE6: Container mit Float dabei wird margin doppelt gewertet DonTermi HTML, Usability und Barrierefreiheit 1 11.01.2007 09:02
CSS: Verschachtelter Container und Text danach HTML, Usability und Barrierefreiheit 2 01.12.2005 10:00
[Erledigt] Container im Firefox falsch dargestellt HTML, Usability und Barrierefreiheit 27 15.08.2005 23:18
&amp;lt;div&amp;gt;- container vertikal auf seite zentrieren HTML, Usability und Barrierefreiheit 3 08.05.2005 11:16

Besucher kamen über folgende Suchanfragen bei Google auf diese Seite
php di, di container, php di container, php container, php di-container, di php, php service container, container php, container.php, di-container, dicontainer php, di-container php, php, container?, di mit php, php xml di container, php config container, service container php, php5 di, \di container\, div container php variable zurückgeben

Alle Zeitangaben in WEZ +2. Es ist jetzt 01:36 Uhr.




Powered by vBulletin® Version 3.7.2 (Deutsch)
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
Aprilia-Forum, Aquaristik-Forum, Liebeskummer-Forum, Zierfisch-Forum, Geizkragen-Forum