mironsoft.deBlogDesign Patterns
Magento 2 · Design Patterns
Prototype & Object Pool
Pattern in Magento 2

Das Prototype Pattern klont bestehende Objekte statt sie neu zu instanziieren. Das Object Pool Pattern verwaltet einen Vorrat wiederverwendbarer, teurer Objekte. Beide Muster dienen der Performance — und beide sind in Magento 2 tief im Framework verankert.

12 Min. Lesezeit PHP 8.4 Magento 2.4.8

Der PHP clone()-Mechanismus im Detail

Das Prototype Pattern basiert in PHP auf einem eingebauten Sprachmechanismus: dem clone-Schlüsselwort. Wenn man clone $object aufruft, erzeugt PHP intern eine neue Instanz derselben Klasse und kopiert alle Properties des Originals. Dabei wird kein Konstruktor aufgerufen — das ist der entscheidende Unterschied zu new. Anschließend ruft PHP die magische Methode __clone() auf dem frisch erzeugten Klon auf — sofern sie definiert ist.

Das Prototype Pattern ist eines der klassischen Erzeugungsmuster aus dem GoF-Buch (Gang of Four). Es eignet sich immer dann, wenn die vollständige Initialisierung eines Objekts aufwendig ist — etwa weil Datenbankabfragen, Netzwerkkommunikation oder komplexe Berechnungen notwendig sind — und wenn man viele ähnliche Objekte benötigt, die sich nur in wenigen Details unterscheiden. Statt jeden Schritt der Initialisierung zu wiederholen, nimmt man ein bereits fertig konfiguriertes Objekt als Vorlage und erstellt davon eine Kopie.

In PHP 8.4 ist der clone-Mechanismus unverändert, wird aber durch neue Sprachfeatures ergänzt. Readonly-Properties können seit PHP 8.3 in __clone() geschrieben werden — was zuvor einen Fehler verursachte. Das ermöglicht echte immutable Value Objects, die dennoch geklont und leicht verändert werden können. PHP 8.4 führt zusätzlich die clone with-Syntax ein, die es erlaubt, Properties beim Klonen direkt zu überschreiben, ohne __clone() schreiben zu müssen.

In Magento 2.4.8 wird der PHP-clone-Mechanismus an mehreren Stellen bewusst eingesetzt — manchmal offensichtlich, manchmal verborgen im Framework-Innenleben. Um zu verstehen, warum das so ist, muss man zuerst den Unterschied zwischen shallow copy und deep copy kennen.

<?php
declare(strict_types=1);

// PHP 8.4: clone with — direkte Property-Überschreibung beim Klonen
class ProductPrice
{
    public function __construct(
        public readonly string $sku,
        public readonly float $price,
        public readonly string $currency = 'EUR'
    ) {}
}

$basePrice = new ProductPrice('SKU-001', 29.99, 'EUR');

// PHP 8.4: clone with überschreibt Properties ohne __clone()
$priceUsd = clone $basePrice with { currency: 'USD', price: 32.50 };

// Klassischer Weg: __clone() für komplexere Objekte
class QuoteItemPrototype
{
    public function __construct(
        private string $sku,
        private float $qty,
        private ?AddressData $shippingAddress = null
    ) {}

    // Deep clone: verschachtelte Objekte explizit klonen
    public function __clone(): void
    {
        if ($this->shippingAddress !== null) {
            $this->shippingAddress = clone $this->shippingAddress;
        }
    }
}

Shallow Copy vs. Deep Copy — der entscheidende Unterschied

PHP's clone erzeugt standardmäßig eine Shallow Copy. Das bedeutet: Alle Properties mit skalaren Typen (int, float, string, bool, null) werden vollständig kopiert und existieren unabhängig im Klon. Arrays werden ebenfalls kopiert — ihre direkten Werte sind unabhängig. Enthält ein Array jedoch Objekte, werden diese nicht geklont, sondern als Referenz übernommen.

Bei Objekt-Properties sieht es kritischer aus: PHP kopiert nicht das Objekt selbst, sondern nur den Zeiger darauf. Original und Klon zeigen nach einem einfachen clone auf dieselbe Instanz des verschachtelten Objekts. Eine Mutation am inneren Objekt — egal ob über Original oder Klon durchgeführt — verändert beide gleichzeitig. Das ist in den meisten Fällen nicht gewünscht.

Eine Deep Copy klont alle enthaltenen Objekte rekursiv mit. In PHP implementiert man das durch Überschreiben der __clone()-Methode. Darin wird jede Objekt-Property explizit mit clone versehen. Enthält das innere Objekt wiederum Objekte, muss auch dieses eine __clone()-Methode haben, damit die Deep-Copy-Kette vollständig ist.

In Magento 2 sind die meisten Model-Objekte (Product, Quote, Order) Instanzen von Magento\Framework\DataObject, das intern ein Array für alle Daten nutzt. Da Arrays bei shallow copy kopiert werden, sind die einfachen Datenwerte nach einem clone unabhängig. Extension Attributes dagegen sind Objekte — hier ist besondere Vorsicht geboten.

Das Prototype Pattern in Magento 2: Quote Items klonen

Das bekannteste Beispiel für das Prototype Pattern in Magento 2 findet sich im Warenkorb-System. Wenn ein konfigurierbares Produkt (Configurable Product) in den Warenkorb gelegt wird, erzeugt Magento intern zwei Quote Items: eines für das konfigurierbare Produkt selbst (das Parent Item) und eines für die konkrete Simple-Produkt-Variante (das Child Item). Das Child Item wird nicht vollständig neu initialisiert — es entsteht als Klon des Parent Items.

Diese Vorgehensweise ist bewusst gewählt. Das Parent Item hat bereits alle relevanten Properties gesetzt: Menge, Quote-Referenz, Store-Scope, benutzerdefinierten Preis und weitere Felder. Diese Werte soll das Child Item übernehmen. Statt jeden Wert manuell zu übertragen, erzeugt Magento einen Klon und passt nur die abweichenden Properties an — den konkreten Produkt-Bezug und die Parent-Kind-Beziehung.

Auch der Produkttyp-Pool in Magento 2 nutzt das Prototype-Konzept. Die Klasse Magento\Catalog\Model\Product\Type hält Prototyp-Instanzen der einzelnen TypeModel-Klassen (Simple, Configurable, Bundle, Virtual, Downloadable) vor. Beim ersten Zugriff auf einen Produkttyp wird die Instanz erstellt und gecacht. Alle nachfolgenden Anfragen erhalten dieselbe Instanz zurück. Da TypeModels zustandslos sind, ist hier kein Klonen notwendig — die Prototyp-Instanz kann direkt wiederverwendet werden.

Ein weiteres Einsatzgebiet ist der Batch-Import: Beim Importieren Tausender ähnlicher Produkte lässt sich ein vollständig konfiguriertes Basis-Produkt-Objekt als Prototyp verwenden. Für jede Zeile des Imports wird es geklont und nur SKU, Preis und variantenspezifische Attribute werden überschrieben. Das spart die komplette Objektinitialisierung mit ihren Default-Werten für jeden einzelnen Datensatz.

<?php
declare(strict_types=1);

namespace Mironsoft\Import\Model;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\Data\ProductInterfaceFactory;

/**
 * Prototype-based product import processor.
 * Clones a pre-configured base product for each import row.
 */
class ProductImportProcessor
{
    private ?ProductInterface $prototype = null;

    public function __construct(
        private readonly ProductInterfaceFactory $productFactory,
        private readonly array $defaultAttributes = []
    ) {}

    /**
     * Get or build the prototype product with shared default configuration.
     */
    private function getPrototype(): ProductInterface
    {
        if ($this->prototype === null) {
            $this->prototype = $this->productFactory->create();
            $this->prototype->setTypeId('simple');
            $this->prototype->setAttributeSetId(4);
            $this->prototype->setStatus(1);
            $this->prototype->setVisibility(4);

            foreach ($this->defaultAttributes as $code => $value) {
                $this->prototype->setData($code, $value);
            }
        }

        // Prototype Pattern: clone statt new für jedes Import-Item
        return clone $this->prototype;
    }

    /**
     * Process a single import row using the cloned prototype.
     */
    public function processRow(array $row): ProductInterface
    {
        $product = $this->getPrototype();
        $product->setSku($row['sku']);
        $product->setName($row['name']);
        $product->setPrice((float) $row['price']);

        return $product;
    }
}

Warum clone() statt new — die Performance-Perspektive

Die Entscheidung, clone statt new zu verwenden, ist keine Frage der Eleganz, sondern der Performance. Wenn der Konstruktor eines Objekts Datenbankabfragen ausführt, externe Services aufruft oder aufwendige Berechnungen durchführt, wiederholt jeder new-Aufruf diese Kosten. clone hingegen ist ein reiner Speicheroperationsaufruf: PHP kopiert die Property-Tabelle des Objekts, ohne den Konstruktor auszuführen.

Bei einfachen PHP-Objekten ohne teure Initialisierung ist der Unterschied minimal. Bei Magento-Objekten, die über den DI-Container erstellt werden und dabei Factory-Klassen, Konfigurationsleser und andere abhängige Services aufrufen, kann der Unterschied messbar sein — insbesondere in Schleifen über Tausende von Datensätzen. Import-Jobs, Reindex-Prozesse und Batch-CLI-Kommandos profitieren davon am stärksten.

Ein weiterer Aspekt: Mit clone sichert man den aktuellen Zustand des Prototyps zu einem bestimmten Zeitpunkt. Alle Default-Werte, Konfigurationsparameter und gemeinsamen Properties werden in einem Schritt übertragen. Das reduziert die Fehleranfälligkeit beim manuellen Setzen von Werten: Was vergessen wird zu setzen, ist automatisch vorhanden, weil es vom Prototyp geerbt wurde.

Magento nutzt dieses Prinzip auch im Zusammenspiel mit Factories: Eine Factory erstellt mit create() immer eine neue Instanz. Wenn die Factory intern mit einem Prototyp arbeitet und diesen klont statt vollständig zu instanziieren, kombiniert man die saubere Factory-API mit der Effizienz des Prototype Patterns. Dieses Muster wird in einigen Magento-Core-Factories implizit verwendet.

Wann clone() gefährlich ist: Referenzfallen in Magento-Objekten

Das Prototype Pattern birgt echte Risiken, wenn man die Shallow-Copy-Natur von PHP's clone nicht beachtet. Magento-Objekte sind besonders anfällig, weil sie häufig verschachtelte Objekte enthalten, die als References weitergegeben werden. Extension Attributes sind ein klassisches Beispiel: Ein ProductInterface kann über getExtensionAttributes() ein Extension Attribute Object zurückgeben. Wird das Produkt geklont ohne __clone(), teilen sich Original und Klon dasselbe Extension Attribute Objekt.

Besonders gefährlich wird es, wenn der Klon an eine andere Stelle im Code weitergegeben wird und dort das Extension Attribute Objekt verändert. Die Änderung wirkt sich dann unbemerkt auch auf das Original aus. In einem Magento-System, das Tausende von Requests verarbeitet und bei dem Observer, Plugins und Repositories gemeinsam auf Objekte zugreifen, können solche Referenzfehler schwer zu debuggende Probleme verursachen.

Auch Quote Address Objekte, Order Items mit Product-Referenzen und Custom-Data-Objekte können betroffen sein. Immer wenn man clone auf einem Magento-Objekt verwendet, sollte man die __clone()-Methode implementieren und alle Objekt-Properties explizit klonen. Das ist mehr Arbeit, aber deutlich sicherer.

Ein weiteres Risiko besteht bei Objekten mit Datenbankidentität: Wenn man ein persistiertes Produkt klont und den Klon speichert, wird ein neues Produkt mit derselben ID erstellt — oder die ID muss explizit zurückgesetzt werden. Das Prototype Pattern eignet sich daher besser für transiente Objekte (Quote Items, temporäre Berechnungen) als für Entities mit Datenbankidentität.

<?php
declare(strict_types=1);

namespace Mironsoft\Catalog\Model;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\Data\ProductExtensionInterface;

/**
 * Safe product cloning with explicit deep copy for nested objects.
 */
class SafeProductCloner
{
    /**
     * Create a deep clone of a product, including extension attributes.
     */
    public function deepClone(ProductInterface $product): ProductInterface
    {
        $clone = clone $product;

        // Shallow copy DANGER: extension attributes are shared!
        // We must explicitly clone the extension attributes object.
        $extAttributes = $product->getExtensionAttributes();
        if ($extAttributes !== null) {
            $clone->setExtensionAttributes(clone $extAttributes);
        }

        // Reset database identity — this is a new product, not an update
        $clone->setId(null);
        $clone->unsEntityId();

        return $clone;
    }
}

Das Object Pool Pattern: Konzept und Einsatzgebiete

Das Object Pool Pattern löst ein anderes Performance-Problem als das Prototype Pattern. Es richtet sich nicht gegen teure Objektinitialisierung per Konstruktor, sondern gegen die wiederholte Herstellung ressourcenintensiver Verbindungen oder Handles. Die Kernidee: Statt jedes Mal ein neues Objekt zu erstellen, wenn man eines braucht, hält man einen Vorrat (Pool) bereits erstellter Objekte bereit und entnimmt bei Bedarf eines davon.

Das klassische Beispiel sind Datenbankverbindungen. Das Öffnen einer neuen TCP-Verbindung zur Datenbank, der Handshake, die Authentifizierung und die Initialisierung des MySQL-Sessions können je nach Systemlast zwischen 5 und 50 Millisekunden dauern. Würde jede SQL-Abfrage eine neue Verbindung öffnen und nach der Abfrage wieder schließen, wäre ein Magento-Request mit hunderten von Datenbankoperationen massiv verlangsamt. Der Connection Pool behebt das: Eine Verbindung wird einmal geöffnet und für alle nachfolgenden Abfragen innerhalb desselben Requests wiederverwendet.

Weitere typische Einsatzgebiete für Object Pools: HTTP-Client-Instanzen für API-Calls an externe Dienste (ERP, PIM, Zahlungsanbieter), SFTP-Verbindungen für Dateitransfers, Redis-Client-Instanzen für Cache-Operationen und Elasticsearch-Client-Verbindungen. All diese Objekte haben gemeinsam, dass ihr Aufbau teuer ist und sie für wiederholte Operationen wiederverwendet werden können.

Im Gegensatz zum Prototype Pattern müssen Pool-Objekte nach ihrer Nutzung nicht zurückgegeben werden — in einem PHP-Request-Modell (FPM) endet der Pool ohnehin mit dem Request. In PHP überleben Object Pools nie über Request-Grenzen hinweg, da jeder PHP-FPM-Worker einen eigenen Prozess und damit eigenen Speicher hat. Object Pools in PHP sind daher immer Request-scoped, was die Implementierung wesentlich einfacher macht als in Sprachen wie Java oder C# mit Shared-Memory-Pools.

Object Pool in Magento 2: Shared Instances und Connection Pooling

Magento 2 implementiert das Object Pool Pattern auf zwei Ebenen. Die erste und umfassendste Implementierung ist der DI-Container selbst: Injectable Objects werden standardmäßig als Shared Instances verwaltet. Der ObjectManager hält intern ein $sharedInstances-Array, das als impliziter Object Pool fungiert. Der erste Aufruf von get() für einen Klassentyp erstellt die Instanz und legt sie im Pool ab. Alle nachfolgenden Aufrufe geben dieselbe Instanz zurück.

Diese implizite Object-Pool-Implementierung ist der Grund, warum Constructor Injection in Magento 2 so effizient ist. Services, Repositories, Logger, Session-Objekte und alle anderen Injectable-Klassen werden pro Request nur einmal instanziiert, unabhängig davon, wie viele Klassen sie einbinden. Das Ergebnis sind weniger Objekte im Speicher, weniger Konstruktoraufrufe und schnellere Request-Verarbeitung.

Die zweite, explizitere Object-Pool-Implementierung findet sich in Magento\Framework\App\ResourceConnection. Diese Klasse implementiert einen echten Connection Pool für Datenbankverbindungen. Sie hält ein internes $connections-Array, das Verbindungsinstanzen nach ihrem $resourceName indexiert. Standardmäßig gibt es in Magento zwei Verbindungen: die Haupt-Schreib-Verbindung (default) und die Read-Verbindung (indexer). In größeren Setups mit Read-Replicas kommen weitere hinzu.

Auch der ElasticSearch- und OpenSearch-Client in Magento 2 nutzt das Pool-Prinzip: Ein einziges Client-Objekt wird für alle Suchanfragen und Index-Operationen innerhalb eines Requests wiederverwendet. Dasselbe gilt für den Redis-Client in Magento\Framework\Cache\Backend\Redis — die Verbindung wird einmal aufgebaut und für alle Cache-Lese- und Schreiboperationen verwendet.

// Magento\Framework\App\ResourceConnection — vereinfachter Blick auf den Connection Pool
class ResourceConnection
{
    /** @var \Magento\Framework\DB\Adapter\AdapterInterface[] — der Pool */
    private array $connections = [];

    /**
     * Get or create a database connection for the given resource name.
     * This is the Object Pool pattern: reuse instead of recreate.
     */
    public function getConnection(string $resourceName = self::DEFAULT_CONNECTION): AdapterInterface
    {
        $connectionName = $this->config->getConnectionName($resourceName);

        if (!isset($this->connections[$connectionName])) {
            // First access: create and add to pool
            $this->connections[$connectionName] = $this->connectionFactory->create(
                $this->config->getConnectionConfig($connectionName)
            );
        }

        // Subsequent access: return from pool — no new connection overhead
        return $this->connections[$connectionName];
    }

    /**
     * Close all pooled connections and clear the pool.
     * Called at the end of the request lifecycle.
     */
    public function closeConnection(string $resourceName = self::DEFAULT_CONNECTION): void
    {
        $connectionName = $this->config->getConnectionName($resourceName);
        if (isset($this->connections[$connectionName])) {
            $this->connections[$connectionName]->closeConnection();
            unset($this->connections[$connectionName]);
        }
    }
}

Eigenen Object Pool implementieren mit PHP 8.4

Für eigene Integrationen — etwa API-Verbindungen zu ERP-Systemen, Zahlungsdienstleistern oder externen Produktdatenbanken — lohnt es sich, einen eigenen Object Pool zu implementieren. Das Muster ist in Magento 2 einfach umzusetzen: Man erstellt eine Klasse mit einem internen Array als Pool, die per Constructor Injection die notwendigen Factories einbindet. Da die Klasse selbst als Shared Instance im DI-Container verwaltet wird, überlebt der Pool den gesamten Request.

Ein realer Anwendungsfall: Ein Magento-Shop integriert mehrere externe Lieferanten-APIs. Für jeden Lieferanten gibt es eine eigene Basis-URL und Authentifizierung. Der HTTP-Client-Pool cached eine Client-Instanz pro Lieferant. Der erste Request an einen Lieferanten erstellt den Client (Verbindungsaufbau, Zertifikat-Handling, Timeout-Konfiguration). Alle weiteren Requests verwenden dieselbe Instanz und sparen den Verbindungsaufbau-Overhead.

Die Pool-Implementierung sollte eine release()-Methode anbieten, mit der einzelne Pool-Einträge explizit freigegeben werden können — etwa wenn eine Verbindung fehlerhaft ist und neu aufgebaut werden muss. Eine clear()-Methode entleert den gesamten Pool, was in Tests und nach Fehlerszenarien nützlich ist.

In PHP 8.4 lässt sich der Object Pool durch readonly-Properties für die injizierten Factories und durch typisierte Generics-Kommentare (für IDEs und statische Analyse) sehr prägnant schreiben. Constructor Property Promotion reduziert den Boilerplate-Code erheblich, und der Einsatz von declare(strict_types=1) verhindert implizite Typkonvertierungen, die in Pool-Schlüsseln zu schwer debuggenden Fehlern führen können.

<?php
declare(strict_types=1);

namespace Mironsoft\Integration\Model\Pool;

use Magento\Framework\HTTP\ClientFactory;
use Magento\Framework\HTTP\ClientInterface;
use Psr\Log\LoggerInterface;

/**
 * HTTP client pool — reuses one client instance per supplier base URL.
 * Registered as shared="true" in di.xml (default for injectable classes).
 */
class HttpClientPool
{
    /** @var array<string, ClientInterface> Pool indexed by base URL */
    private array $pool = [];

    public function __construct(
        private readonly ClientFactory $clientFactory,
        private readonly LoggerInterface $logger,
        private readonly int $timeout = 30
    ) {}

    /**
     * Get or create an HTTP client for the given base URL.
     * First call per base URL creates the client; subsequent calls reuse it.
     */
    public function getClient(string $baseUrl): ClientInterface
    {
        if (!isset($this->pool[$baseUrl])) {
            $this->logger->debug('HttpClientPool: creating new client', ['baseUrl' => $baseUrl]);

            $client = $this->clientFactory->create();
            $client->setTimeout($this->timeout);
            $client->setHeaders([
                'Accept'       => 'application/json',
                'Content-Type' => 'application/json',
            ]);

            $this->pool[$baseUrl] = $client;
        }

        return $this->pool[$baseUrl];
    }

    /**
     * Remove a specific client from the pool (e.g., after a connection error).
     */
    public function release(string $baseUrl): void
    {
        unset($this->pool[$baseUrl]);
        $this->logger->debug('HttpClientPool: released client', ['baseUrl' => $baseUrl]);
    }

    /**
     * Clear all pooled clients — useful in test teardown or error recovery.
     */
    public function clear(): void
    {
        $this->pool = [];
    }

    /**
     * Get current pool size for monitoring/diagnostics.
     */
    public function size(): int
    {
        return count($this->pool);
    }
}

Prototype & Object Pool — Das Wichtigste auf einen Blick

PHP clone() = Shallow Copy

Primitive und Arrays werden kopiert. Objekt-Properties als Referenz übernommen. Für Deep Copy: __clone() implementieren und alle verschachtelten Objekte explizit klonen.

Quote Items: Prototype in Aktion

Child Items bei konfigurierbaren Produkten entstehen als clone $parentItem. Nur abweichende Properties (Produkt, Parent-Referenz) werden angepasst.

Shared Instances = impliziter Pool

DI-Container verwaltet Injectable Objects als Shared Instances — de facto ein Object Pool. Erste Instanziierung legt sie im Pool ab, alle folgenden Injections verwenden dieselbe.

Connection Pool = expliziter Pool

ResourceConnection cached Datenbankverbindungen nach resourceName. Einmal geöffnet, für alle DB-Operationen des Requests wiederverwendet.

Mironsoft

Magento 2 Performance & Architektur

Performance-Optimierung mit Pattern-Architektur?

Wir analysieren Magento 2 Projekte auf Performance-Engpässe und implementieren Prototype und Object Pool Patterns dort, wo sie den größten Unterschied machen — für schnellere API-Integration, effiziente Batch-Prozesse und skalierbare Architektur.

API Integration

HTTP Client Pools für effiziente API-Calls ohne Verbindungsaufbau-Overhead

Batch Processing

Prototype Pattern für schnelle Varianten-Erstellung in Import/Export-Prozessen

Performance Audit

Objekt-Erstellungs-Bottlenecks identifizieren und durch Pooling lösen

Zusammenfassung

Das Prototype Pattern und das Object Pool Pattern verfolgen dasselbe Ziel auf unterschiedlichen Wegen: die Kosten der Objekterstellung zu minimieren. Das Prototype Pattern tut das, indem es clone statt new verwendet und damit teure Konstruktoraufrufe vermeidet. Das Object Pool Pattern tut es, indem es erstellte Objekte behält und wiederverwendet statt sie wegzuwerfen und neu zu erstellen.

In Magento 2.4.8 sind beide Muster tief im Framework verankert. Das Prototype Pattern zeigt sich beim Klonen von Quote Items und in Import-Prozessen. Das Object Pool Pattern ist allgegenwärtig: Der DI-Container selbst ist ein Object Pool für alle Injectable Services, und ResourceConnection ist ein expliziter Connection Pool für Datenbankverbindungen.

Für eigene Magento-Module gilt: Wer teure Objekte (HTTP-Clients, externe Verbindungen) mehrfach benötigt, sollte einen eigenen Pool als Shared Instance implementieren. Wer ähnliche Objekte in Schleifen erzeugt, sollte das Prototype Pattern mit sorgfältigem Deep-Copy-Management in Betracht ziehen. Beide Patterns zahlen auf das Gleiche ein: weniger Overhead, schnellere Requests und ein robusteres System.

FAQ: Prototype & Object Pool in Magento 2

1 Was ist das Prototype Pattern in der Softwareentwicklung?
Das Prototype Pattern ist eines der klassischen GoF-Creational-Patterns. Es erzeugt neue Objekte nicht durch den Konstruktor, sondern durch Klonen eines bestehenden Prototyp-Objekts. In PHP über clone $obj umgesetzt. Sinnvoll, wenn die Initialisierung eines Objekts teuer ist oder wenn viele ähnliche Objekte mit leicht abweichenden Eigenschaften benötigt werden.
2 Was ist der Unterschied zwischen shallow copy und deep copy in PHP?
Shallow copy: Primitive Werte werden kopiert, Objekt-Properties als Referenz übernommen. Änderungen an verschachtelten Objekten wirken auf beide Instanzen. Deep copy: Alle enthaltenen Objekte werden ebenfalls geklont. In PHP über __clone() implementiert, darin alle Objekt-Properties explizit mit clone versehen.
3 Wie verwendet Magento 2 das Prototype Pattern intern?
Beim Hinzufügen konfigurierbarer Produkte zum Warenkorb: Child Items entstehen als clone $parentItem. Nur abweichende Properties (Produkt, Parent-Referenz) werden angepasst. Auch der Produkttyp-Pool hält Prototyp-Instanzen der TypeModel-Klassen vor und wiederverwendet sie direkt.
4 Wann ist clone() in Magento 2 gefährlich?
Wenn das geklonte Objekt verschachtelte Objekte enthält, die nicht explizit mitgeklont werden. Dann teilen Original und Klon dieselbe Referenz. Besonders gefährlich bei Magento-Objekten mit Extension Attributes, Address-Objekten oder komplexen Data-Objekten. Immer __clone() implementieren.
5 Was ist das Object Pool Pattern und wofür wird es eingesetzt?
Der Object Pool verwaltet einen Vorrat vorinstanziierter, wiederverwendbarer Objekte. Statt ein neues Objekt zu erstellen, wird eines aus dem Pool entnommen. Eingesetzt für teure Objekte: Datenbankverbindungen, HTTP-Clients, SFTP-Verbindungen, Redis-Client-Instanzen — alles wo der Aufbau einer neuen Verbindung viel Zeit kostet.
6 Wie implementiert Magento 2 das Object Pool Pattern?
Implizit: DI-Container verwaltet Shared Instances im $sharedInstances-Array — de facto ein Object Pool. Explizit: ResourceConnection cached Datenbankverbindungen nach resourceName und gibt sie bei jedem Aufruf wiederverwendet zurück.
7 Wie implementiert man einen eigenen Object Pool in Magento 2?
Eine Shared-Instance-Klasse mit internem Array als Pool, die per Constructor Injection eine Factory einbindet. getClient(string $key) prüft ob die Instanz existiert, erstellt sie andernfalls und gibt sie zurück. Da die Pool-Klasse selbst eine Shared Instance ist, überlebt der Pool den gesamten Request.
8 Was ist der Unterschied zwischen Object Pool und Singleton?
Singleton: genau eine Instanz global. Object Pool: mehrere Instanzen desselben Typs, nach Schlüsseln unterschieden. In PHP-FPM ist jeder Request ein eigener Prozess — Object Pools sind Request-scoped und automatisch thread-safe ohne Synchronisierung.
9 Wie testet man Klassen, die clone verwenden?
clone ist reines PHP und erfordert keine Mocks. Unit Test: Original modifizieren, klonen, beide unabhängig prüfen. Für __clone()-Tests: verschachtelte Objekte nach dem Klonen auf Referenzgleichheit oder -ungleichheit prüfen (=== Operator).
10 Kann man das Prototype Pattern für Produkt-Varianten in Importen nutzen?
Ja. Ein Basis-Produkt-Objekt wird konfiguriert, dann für jede Variante geklont und nur abweichende Properties (SKU, Preis, EAV-Attributwerte) gesetzt. Das spart die komplette Objektinitialisierung mit Default-Werten, Extension Attributes und Standardkonfiguration für jeden Datensatz.