3v4l.org

run code in 300+ PHP versions simultaneously
<?php namespace RefactoringGuru\Flyweight\RealWorld; /** * Объекты Легковеса представляют данные, разделяемые несколькими объектами * Кошек. Это сочетание породы, цвета, текстуры и т.д. */ class CatVariation { /** * Так называемое «внутреннее» состояние. */ public $breed; public $image; public $color; public $texture; public $fur; public $size; public function __construct( string $breed, string $image, string $color, string $texture, string $fur, string $size ) { $this->breed = $breed; $this->image = $image; $this->color = $color; $this->texture = $texture; $this->fur = $fur; $this->size = $size; } /** * Этот метод отображает информацию о кошке. Метод принимает внешнее * состояние в качестве аргументов. Остальная часть состояния хранится * внутри полей Легковеса. * * Возможно, вы удивлены, почему мы поместили основную логику кошки в класс * РазновидностейКошек вместо того, чтобы держать её в классе Кошки. Я * согласен, это звучит странно. * * Имейте в виду, что в реальной жизни паттерн Легковес может быть либо * реализован с самого начала, либо принудительно применён к существующему * приложению, когда разработчики понимают, что они столкнулись с проблемой * ОЗУ. * * Во втором случае вы получаете такие же классы, как у нас. Мы как бы * «отрефакторили» идеальное приложение, где все данные изначально * находились внутри класса Кошки. Если бы мы реализовывали Легковес с * самого начала, названия наших классов могли бы быть другими и более * определёнными. Например, Кошка и КонтекстКошки. * * Однако действительная причина, по которой основное поведение должно * проживать в классе Легковеса, заключается в том, что у вас может вообще * не быть объявленного класса Контекста. Контекстные данные могут храниться * в массиве или какой-то другой, более эффективной структуре данных. */ public function renderProfile(string $name, string $age, string $owner) { echo "= $name =\n"; echo "Age: $age\n"; echo "Owner: $owner\n"; echo "Breed: $this->breed\n"; echo "Image: $this->image\n"; echo "Color: $this->color\n"; echo "Texture: $this->texture\n"; } } /** * Контекст хранит данные, уникальные для каждой кошки. * * Создавать отдельный класс для хранения контекста необязательно и не всегда * целесообразно. Контекст может храниться внутри громоздкой структуры данных в * коде Клиента и при необходимости передаваться в методы легковеса. */ class Cat { /** * Так называемое «внешнее» состояние. */ public $name; public $age; public $owner; /** * @var CatVariation */ private $variation; public function __construct(string $name, string $age, string $owner, CatVariation $variation) { $this->name = $name; $this->age = $age; $this->owner = $owner; $this->variation = $variation; } /** * Поскольку объекты Контекста не владеют всем своим состоянием, иногда для * удобства вы можете реализовать несколько вспомогательных методов * (например, для сравнения нескольких объектов Контекста между собой). */ public function matches(array $query): bool { foreach ($query as $key => $value) { if (property_exists($this, $key)) { if ($this->$key != $value) { return false; } } elseif (property_exists($this->variation, $key)) { if ($this->variation->$key != $value) { return false; } } else { return false; } } return true; } /** * Кроме того, Контекст может определять несколько методов быстрого доступа, * которые делегируют исполнение объекту-Легковесу. Эти методы могут быть * остатками реальных методов, извлечённых в класс Легковеса во время * массивного рефакторинга к паттерну Легковес. */ public function render(): string { $this->variation->renderProfile($this->name, $this->age, $this->owner); } } /** * Фабрика Легковесов хранит объекты Контекст и Легковес, эффективно скрывая * любое упоминание о паттерне Легковес от клиента. */ class CatDataBase { /** * Список объектов-кошек (Контексты). */ private $cats = []; /** * Список вариаций кошки (Легковесы). */ private $variations = []; /** * При добавлении кошки в базу данных мы сначала ищем существующую вариацию * кошки. */ public function addCat( string $name, string $age, string $owner, string $breed, string $image, string $color, string $texture, string $fur, string $size ) { $variation = $this->getVariation($breed, $image, $color, $texture, $fur, $size); $this->cats[] = new Cat($name, $age, $owner, $variation); echo "CatDataBase: Added a cat ($name, $breed).\n"; } /** * Возвращаем существующий вариант (Легковеса) по указанным данным или * создаём новый, если он ещё не существует. */ public function getVariation( string $breed, string $image, $color, string $texture, string $fur, string $size ): CatVariation { $key = $this->getKey(get_defined_vars()); if (!isset($this->variations[$key])) { $this->variations[$key] = new CatVariation($breed, $image, $color, $texture, $fur, $size); } return $this->variations[$key]; } /** * Эта функция помогает генерировать уникальные ключи массива. */ private function getKey(array $data): string { return md5(implode("_", $data)); } /** * Ищем кошку в базе данных, используя заданные параметры запроса. */ public function findCat(array $query) { foreach ($this->cats as $cat) { if ($cat->matches($query)) { return $cat; } } echo "CatDataBase: Sorry, your query does not yield any results."; } } /** * Клиентский код. */ $db = new CatDataBase(); echo "Client: Let's see what we have in \"cats.csv\".\n"; // Чтобы увидеть реальный эффект паттерна, вы должны иметь большую базу данных с // несколькими миллионами записей. Не стесняйтесь экспериментировать с кодом, // чтобы увидеть реальные масштабы паттерна. $handle = fopen(__DIR__ . "/cats.csv", "r"); $row = 0; $columns = []; while (($data = fgetcsv($handle)) !== false) { if ($row == 0) { for ($c = 0; $c < count($data); $c++) { $columnIndex = $c; $columnKey = strtolower($data[$c]); $columns[$columnKey] = $columnIndex; } $row++; continue; } $db->addCat( $data[$columns['name']], $data[$columns['age']], $data[$columns['owner']], $data[$columns['breed']], $data[$columns['image']], $data[$columns['color']], $data[$columns['texture']], $data[$columns['fur']], $data[$columns['size']], ); $row++; } fclose($handle); // ... echo "\nClient: Let's look for a cat named \"Siri\".\n"; $cat = $db->findCat(['name' => "Siri"]); if ($cat) { $cat->render(); } echo "\nClient: Let's look for a cat named \"Bob\".\n"; $cat = $db->findCat(['name' => "Bob"]); if ($cat) { $cat->render(); }
Output for 8.3.0 - 8.3.4, 8.3.6
Client: Let's see what we have in "cats.csv". Warning: fopen(/in/cats.csv): Failed to open stream: No such file or directory in /in/kttGv on line 236 Fatal error: Uncaught TypeError: fgetcsv(): Argument #1 ($stream) must be of type resource, false given in /in/kttGv:239 Stack trace: #0 /in/kttGv(239): fgetcsv(false) #1 {main} thrown in /in/kttGv on line 239
Process exited with code 255.
Output for 8.3.5
Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0 Client: Let's see what we have in "cats.csv". Warning: fopen(/in/cats.csv): Failed to open stream: No such file or directory in /in/kttGv on line 236 Fatal error: Uncaught TypeError: fgetcsv(): Argument #1 ($stream) must be of type resource, false given in /in/kttGv:239 Stack trace: #0 /in/kttGv(239): fgetcsv(false) #1 {main} thrown in /in/kttGv on line 239
Process exited with code 255.
Output for 8.0.9, 8.1.23 - 8.1.28, 8.2.10 - 8.2.18
Client: Let's see what we have in "cats.csv". Warning: fopen(/in/cats.csv): Failed to open stream: No such file or directory in /in/kttGv on line 236 Fatal error: Uncaught TypeError: fgetcsv(): Argument #1 ($stream) must be of type resource, bool given in /in/kttGv:239 Stack trace: #0 /in/kttGv(239): fgetcsv(false) #1 {main} thrown in /in/kttGv on line 239
Process exited with code 255.

preferences:
68.27 ms | 402 KiB | 28 Q