Skip to content

Instantly share code, notes, and snippets.

@Kcko
Last active July 26, 2022 21:47
Show Gist options
  • Save Kcko/d00dd3d7a35572359ca7c5899522195f to your computer and use it in GitHub Desktop.
Save Kcko/d00dd3d7a35572359ca7c5899522195f to your computer and use it in GitHub Desktop.
<?php
/**
* This is the abstract factory, each factory should extend this (WebOutputTypeFactory & DataOutputTypeFactory)
*/
abstract class AbstractOutputTypeFactory
{
abstract public function prettyOutput();
abstract public function uglyOutput();
}
/**
* All class which represent pretty output, should extend this class
*/
abstract class AbstractPrettyOutput
{
abstract public function getPrettyOutput();
}
/**
* All class which represent ugly output, should extend this class
*/
abstract class AbstractUglyOutput
{
abstract public function getUglyOutput();
}
/**
* This is a factory which can create all types of web output objects
*/
class WebOutputTypeFactory extends AbstractOutputTypeFactory
{
public function prettyOutput()
{
return new WebPrettyOutput();
}
public function uglyOutput()
{
return new WebUglyOutput();
}
}
/**
* This is a factory which can create all types of data output objects
*/
class DataOutputTypeFactory extends AbstractOutputTypeFactory
{
public function prettyOutput()
{
return new DataPrettyOutput();
}
public function uglyOutput()
{
return new DataUglyOutput();
}
}
/**
* This class will represent pretty web output (e.g. HTML)
*/
class WebPrettyOutput extends AbstractPrettyOutput
{
public function getPrettyOutput()
{
return '<h1>Imagine you had some really pretty web output here</h1>';
}
}
/**
* This class will represent ugly web output (e.g. XML)
*/
class WebUglyOutput extends AbstractUglyOutput
{
public function getUglyOutput()
{
return 'Imagine you had some really ugly web output here';
}
}
/**
* This class will represent pretty data output (e.g. JSON)
*/
class DataPrettyOutput extends AbstractPrettyOutput
{
public function getPrettyOutput()
{
return "{ 'text': 'Imagine you had some really pretty data output here' }";
}
}
/**
* This class will represent ugly data output(e.g. CSV)
*/
class DataUglyOutput extends AbstractUglyOutput
{
public function getUglyOutput()
{
return 'Imagine, you, had, some, really, ugly, CSV, output, here';
}
}
// this is used to create web outputs
$webFactory = new WebOutputTypeFactory();
$webPretty = $webFactory->prettyOutput();
echo $webPretty->getPrettyOutput(); //
// Imagine you had some really pretty web output here
$webUgly = $webFactory->uglyOutput();
echo $webUgly->getUglyOutput(); // Imagine you had some really ugly web output here
// this is used to create data outputs
$dataFactory = new DataOutputTypeFactory();
$dataPretty = $dataFactory->prettyOutput();
echo $dataPretty->getPrettyOutput(); // { 'text': 'Imagine you had some really pretty data output here' }
$dataUgly = $dataFactory->uglyOutput();
echo $dataUgly->getUglyOutput(); // Imagine, you, had, some, really, ugly, CSV, output, here
/*
The factory pattern is a powerful design pattern in all of its forms. Some people may tell you the simple factory shouldn’t be used, generally having poor reasons not to do so. At the end of the day we just want to get things done and unless you’re working with someone extremely anal, nobody should have a problem.
The abstract factory pattern is by far the hardest to understand out of the three. It’s not always obvious when a good time to use it would be. Don’t force this, there is a huge amount of abstraction in place which is only required when building complex systems. As long as you know what an abstract factory is, sooner or later you’ll find a genuine use case.
*/
<?php
// INTERFACES
interface WriterFactory
{
public function createCsvWriter(): CsvWriter;
public function createJsonWriter(): JsonWriter;
}
interface CsvWriter
{
public function write(array $line): string;
}
interface JsonWriter
{
public function write(array $data, bool $formatted): string;
}
// WRITTERS
class UnixCsvWriter implements CsvWriter
{
public function write(array $line): string
{
return join(',', $line) . "\n";
}
}
class WinCsvWriter implements CsvWriter
{
public function write(array $line): string
{
return join(',', $line) . "\r\n";
}
}
class UnixJsonWriter implements JsonWriter
{
public function write(array $data, bool $formatted): string
{
$options = 0;
if ($formatted) {
$options = JSON_PRETTY_PRINT;
}
return json_encode($data, $options);
}
}
class WinJsonWriter implements JsonWriter
{
public function write(array $data, bool $formatted): string
{
return json_encode($data, JSON_PRETTY_PRINT);
}
}
// FACTORIES
class UnixWriterFactory implements WriterFactory
{
public function createCsvWriter(): CsvWriter
{
return new UnixCsvWriter();
}
public function createJsonWriter(): JsonWriter
{
return new UnixJsonWriter();
}
}
class WinWriterFactory implements WriterFactory
{
public function createCsvWriter(): CsvWriter
{
return new WinCsvWriter();
}
public function createJsonWriter(): JsonWriter
{
return new WinJsonWriter();
}
}
// USAGE
$writeFactory = new WinWriterFactory();
$writerFactory->createJsonWriter(); // or $writerFactory->createCsvWriter()
<?php
// Intefaces
interface TemplateFactory
{
public function createTitleTemplate(): TitleTemplate;
public function createPageTemplate(): PageTemplate;
public function getRenderer(): TemplateRenderer;
}
interface TitleTemplate
{
public function getTemplateString(): string;
}
interface PageTemplate
{
public function getTemplateString(): string;
}
interface TemplateRenderer
{
public function render(string $templateString, array $arguments = []): string;
}
// Abstracts
abstract class BasePageTemplate implements PageTemplate
{
protected $titleTemplate;
public function __construct(TitleTemplate $titleTemplate)
{
$this->titleTemplate = $titleTemplate;
}
}
// Factories
class TwigTemplateFactory implements TemplateFactory
{
public function createTitleTemplate(): TitleTemplate
{
return new TwigTitleTemplate;
}
public function createPageTemplate(): PageTemplate
{
return new TwigPageTemplate($this->createTitleTemplate());
}
public function getRenderer(): TemplateRenderer
{
return new TwigRenderer();
}
}
class PHPTemplateFactory implements TemplateFactory
{
public function createTitleTemplate(): TitleTemplate
{
return new PHPTemplateTitleTemplate;
}
public function createPageTemplate(): PageTemplate
{
return new PHPTemplatePageTemplate($this->createTitleTemplate());
}
public function getRenderer(): TemplateRenderer
{
return new PHPTemplateRenderer();
}
}
// Rest clasess see above
class TwigTitleTemplate implements TitleTemplate
{
public function getTemplateString(): string
{
return "<h1>{{ title }}</h1>";
}
}
class PHPTemplateTitleTemplate implements TitleTemplate
{
public function getTemplateString(): string
{
return "<h1><?= \$title; ?></h1>";
}
}
class TwigPageTemplate extends BasePageTemplate
{
public function getTemplateString(): string
{
$renderedTitle = $this->titleTemplate->getTemplateString();
return <<<HTML
<div class="page">
$renderedTitle
<article class="content">{{ content }}</article>
</div>
HTML;
}
}
class PHPTemplatePageTemplate extends BasePageTemplate
{
public function getTemplateString(): string
{
$renderedTitle = $this->titleTemplate->getTemplateString();
return <<<HTML
<div class="page">
$renderedTitle
<article class="content"><?= \$content; ?></article>
</div>
HTML;
}
}
class TwigRenderer implements TemplateRenderer
{
public function render(string $templateString, array $arguments = []): string
{
return \Twig::render($templateString, $arguments);
}
}
class PHPTemplateRenderer implements TemplateRenderer
{
public function render(string $templateString, array $arguments = []): string
{
extract($arguments);
ob_start();
eval(' ?>' . $templateString . '<?php ');
$result = ob_get_contents();
ob_end_clean();
return $result;
}
}
/**
* The client code. Note that it accepts the Abstract Factory class as the
* parameter, which allows the client to work with any concrete factory type.
*/
class Page
{
public $title;
public $content;
public function __construct($title, $content)
{
$this->title = $title;
$this->content = $content;
}
// Here's how would you use the template further in real life. Note that the
// page class does not depend on any concrete template classes.
public function render(TemplateFactory $factory): string
{
$pageTemplate = $factory->createPageTemplate();
$renderer = $factory->getRenderer();
return $renderer->render($pageTemplate->getTemplateString(), [
'title' => $this->title,
'content' => $this->content
]);
}
}
// USAGE
/**
* Now, in other parts of the app, the client code can accept factory objects of
* any type.
*/
$page = new Page('Sample page', 'This it the body.');
echo "Testing actual rendering with the PHPTemplate factory:\n";
echo $page->render(new PHPTemplateFactory);
// Uncomment the following if you have Twig installed.
// echo "Testing rendering with the Twig factory:\n"; echo $page->render(new
// TwigTemplateFactory);
<?php
/**
* All animal should extend this abstract animal class
*/
abstract class AnimalAbstract
{
protected $species;
public function getSpecies() {
return $this->species;
}
}
/**
* used to represent a cat
*/
class Cat extends AnimalAbstract
{
protected $species = 'cat';
}
/**
* used to represent a dog
*/
class Dog extends AnimalAbstract
{
protected $species = 'dog';
}
/**
* used to represent a pig
*/
class Pig extends AnimalAbstract
{
protected $species = 'pig';
}
/**
* used to represent a chicken
*/
class Chicken extends AnimalAbstract
{
protected $species = 'chicken';
}
/**
* used to represent a zebra
*/
class Zebra extends AnimalAbstract
{
protected $species = 'zebra';
}
/**
* used to represent a giraffe
*/
class Giraffe extends AnimalAbstract
{
protected $species = 'giraffe';
}
/**
* used to represent a all factories should implement this interface
*/
interface AnimalFactoryInterface
{
public static function factory($animal);
}
/**
* this should be used to create all animals which are pets
*/
class PetAnimalFactory implements AnimalFactoryInterface
{
public static function factory($animal)
{
switch ($animal) {
case 'cat':
$obj = new Cat();
break;
case 'dog':
$obj = new Dog();
break;
default:
throw new Exception("Pet animal factory could not create animal of species '" . $animal . "'", 1000);
}
return $obj;
}
}
/**
* this should be used to create all animals which are farm animals
*/
class FarmAnimalFactory implements AnimalFactoryInterface
{
public static function factory($animal)
{
switch ($animal) {
case 'pig':
$obj = new Pig();
break;
case 'chicken':
$obj = new Chicken();
break;
default:
throw new Exception("Farm animal factory could not create animal of species '" . $animal . "'", 1000);
}
return $obj;
}
}
/**
* this should be used to create all animals which are safari animals
*/
class SafariAnimalFactory implements AnimalFactoryInterface
{
public static function factory($animal)
{
switch ($animal) {
case 'zebra':
$obj = new Zebra();
break;
case 'giraffe':
$obj = new Giraffe();
break;
default:
throw new Exception("Safari animal factory could not create animal of species '" . $animal . "'", 1000);
}
return $obj;
}
}
try {
$cat = PetAnimalFactory::factory('cat'); // object(Cat)#1
echo $cat->getSpecies(); // cat
$pig = FarmAnimalFactory::factory('pig'); // object(Pig)#1
echo $pig->getSpecies(); // pig
$giraffe = SafariAnimalFactory::factory('giraffe'); // object(Giraffe)#1
echo $giraffe->getSpecies(); // giraffe
$petChicken = PetAnimalFactory::factory('chicken'); // This will throw an Exception
} catch(Exception $e) {
echo $e->getMessage(); // PetAnimalFactory could not create animal of species 'chicken'
}
<?php
class Image {
static function create($type) {
switch ($type) {
case "gif": return new ImageGif;
default: throw new Exception("Nepodporovaný typ obrázku");
}
}
}
class ImageGif extends Image {
// metody specifické pro obrázky typu GIF
}
<?php
class MySQLDB
{
public function setHost($host)
{
// code
}
public function setDB($db)
{
// code
}
public function setUserName($user)
{
// code
}
public function setPassword($pwd)
{
// code
}
public function connect()
{
// code
}
}
class PostgreSQLDB
{
public function setHost($host)
{
// code
}
public function setDB($db)
{
// code
}
public function setUserName($user)
{
// code
}
public function setPassword($pwd)
{
// code
}
public function connect()
{
// code
}
}
class DBFactory
{
protected $driver = null;
public function setDriver($driver)
{
$this->driver = $driver;
}
public function makeDB($host, $user, $pass, $dbname)
{
if ($this->driver === 'mysql') {
$DB = new MySQLDB();
}
elseif ($this->driver === 'postgre') {
$DB = new PostgreSQLDB();
}
elseif ($this->driver === 'sqlite') {
$DB = new SQLiteDB();
}
$DB->setHost($host);
$DB->setDB($dbname);
$DB->setUserName($user);
$DB->setPassword($pass);
$DB->connect();
return $DB;
}
}
// usage
$dbFactory = new DBFactory;
$dbFactory->setDriver('mysql');
$DB = $dbFactory->makeDB("host", "db", "user", "pwd");
<?php
interface Logger
{
public function log(string $message);
}
interface LoggerFactory
{
public function createLogger(): Logger;
}
class StdoutLogger implements Logger
{
public function log(string $message)
{
echo $message;
}
}
class FileLogger implements Logger
{
private string $filePath;
public function __construct(string $filePath)
{
$this->filePath = $filePath;
}
public function log(string $message)
{
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
}
}
class StdoutLoggerFactory implements LoggerFactory
{
public function createLogger(): Logger
{
return new StdoutLogger();
}
}
class FileLoggerFactory implements LoggerFactory
{
private string $filePath;
public function __construct(string $filePath)
{
$this->filePath = $filePath;
}
public function createLogger(): Logger
{
return new FileLogger($this->filePath);
}
}
// usage
$loggerFactory = new StdoutLoggerFactory();
$logger = $loggerFactory->createLogger();
$loggerFactory = new FileLoggerFactory(sys_get_temp_dir());
$logger = $loggerFactory->createLogger();
<?php
interface SocialNetworkConnector
{
public function logIn(): void;
public function logOut(): void;
public function createPost($content): void;
}
abstract class SocialNetworkPoster
{
abstract public function getSocialNetwork(): SocialNetworkConnector;
public function post($content): void
{
// Call the factory method to create a Product object...
$network = $this->getSocialNetwork();
// ...then use it as you will.
$network->logIn();
$network->createPost($content);
$network->logout();
}
}
/**
* This Concrete Creator supports Facebook. Remember that this class also
* inherits the 'post' method from the parent class. Concrete Creators are the
* classes that the Client actually uses.
*/
class FacebookPoster extends SocialNetworkPoster
{
private $login, $password;
public function __construct(string $login, string $password)
{
$this->login = $login;
$this->password = $password;
}
public function getSocialNetwork(): SocialNetworkConnector
{
return new FacebookConnector($this->login, $this->password);
}
}
/**
* This Concrete Creator supports LinkedIn.
*/
class LinkedInPoster extends SocialNetworkPoster
{
private $email, $password;
public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}
public function getSocialNetwork(): SocialNetworkConnector
{
return new LinkedInConnector($this->email, $this->password);
}
}
/**
* This Concrete Product implements the Facebook API.
*/
class FacebookConnector implements SocialNetworkConnector
{
private $login, $password;
public function __construct(string $login, string $password)
{
$this->login = $login;
$this->password = $password;
}
public function logIn(): void
{
echo "Send HTTP API request to log in user $this->login with " .
"password $this->password\n";
}
public function logOut(): void
{
echo "Send HTTP API request to log out user $this->login\n";
}
public function createPost($content): void
{
echo "Send HTTP API requests to create a post in Facebook timeline.\n";
}
}
/**
* This Concrete Product implements the LinkedIn API.
*/
class LinkedInConnector implements SocialNetworkConnector
{
private $email, $password;
public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}
public function logIn(): void
{
echo "Send HTTP API request to log in user $this->email with " .
"password $this->password\n";
}
public function logOut(): void
{
echo "Send HTTP API request to log out user $this->email\n";
}
public function createPost($content): void
{
echo "Send HTTP API requests to create a post in LinkedIn timeline.\n";
}
}
// USAGE
new FacebookPoster("john_smith", "******");
new LinkedInPoster("john_smith@example.com", "******")
<?php
/*
https://www.jakowicz.com/factory-pattern-in-php/
Rules:
==========
1) The factory class must have a static method, this is called a factory method.
2) The factory method must return a class instance.
3) Only one object should be created and returned at a time.
*/
/**
* All animal should extend this abstract animal class
*/
abstract class AnimalAbstract
{
protected $species;
public function getSpecies() {
return $this->species;
}
}
/**
* used to represent a cat
*/
class Cat extends AnimalAbstract
{
protected $species = 'cat';
}
/**
* used to represent a dog
*/
class Dog extends AnimalAbstract
{
protected $species = 'dog';
}
/**
* The is the factory which creates animal objects
*/
class AnimalFactory
{
public static function factory($animal)
{
switch ($animal) {
case 'cat':
$obj = new Cat();
break;
case 'dog':
$obj = new Dog();
break;
default:
throw new Exception("Animal factory could not create animal of species '" . $animal . "'", 1000);
}
return $obj;
}
}
try {
$cat = AnimalFactory::factory('cat'); // object(Cat)#1
echo $cat->getSpecies(); // cat
$dog = AnimalFactory::factory('dog'); // object(Dog)#1
echo $dog->getSpecies(); // dog
$hippo = AnimalFactory::factory('hippopotamus'); // This will throw an Exception
} catch(Exception $e) {
echo $e->getMessage(); // AnimalFactory could not create animal of species 'hippopotamus'
}
<?php
/*
SimpleFactory is a simple factory pattern.
It differs from the static factory because it is not static. Therefore, you can have multiple factories, differently parameterized, you can subclass it and you can mock it. It always should be preferred over a static factory!
*/
class Bicycle
{
public function driveTo(string $destination)
{
}
}
class SimpleFactory
{
public function createBicycle(): Bicycle
{
return new Bicycle();
}
}
// usage
$factory = new SimpleFactory();
$bicycle = $factory->createBicycle();
$bicycle->driveTo('Paris');
<?php
// BAD
$student_obj = new Student();
$teacher_obj = new Teacher();
$classroom_obj = new Classroom($student_obj, $teacher_obj);
// GOOD
class Classroom_Factory {
public static function get_instance() {
$student_obj = new Student();
$teacher_obj = new Teacher();
return new Classroom($student_obj, $teacher_obj);
}
}
$classroom_obj = Classroom_Factory::get_instance();
<?php
/*
imilar to the AbstractFactory, this pattern is used to create series of related or dependent objects. The difference between this and the abstract factory pattern is that the static factory pattern uses just one static method to create all types of objects it can create. It is usually named factory or build.
*/
interface Formatter
{
public function format(string $input): string;
}
class FormatString implements Formatter
{
public function format(string $input): string
{
return $input;
}
}
class FormatNumber implements Formatter
{
public function format(string $input): string
{
return number_format((int) $input);
}
}
/**
* Note1: Remember, static means global state which is evil because it can't be mocked for tests
* Note2: Cannot be subclassed or mock-upped or have multiple different instances.
*/
use InvalidArgumentException;
final class StaticFactory
{
public static function factory(string $type): Formatter
{
if ($type == 'number') {
return new FormatNumber();
} elseif ($type == 'string') {
return new FormatString();
}
throw new InvalidArgumentException('Unknown format given');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment