├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── docker-compose.yml ├── docs └── diagrams │ ├── Creational.md │ └── Structural.md ├── example ├── abstract_factory.php ├── adapter.php ├── autoloader.php ├── bridge.php ├── builder.php ├── chain_of_responsibility.php ├── command.php ├── composite.php ├── decorator.php ├── facade.php ├── factory_method.php ├── flyweight.php ├── interpreter.php ├── iterator.php ├── mediator.php ├── memento.php ├── money.php ├── null_object.php ├── object_pool.php ├── observer.php ├── prototype.php ├── proxy.php ├── singleton.php ├── state.php ├── strategy.php ├── template_method.php └── visitor.php ├── logo.png └── src ├── AbstractFactory ├── AmericanOrderFactory.php ├── GermanOrderFactory.php ├── OrderManagementAbstractFactory.php └── Product │ ├── CarrierCompanyInterface.php │ ├── DHLCarrier.php │ ├── FedexCarrier.php │ ├── PaymentGatewayInterface.php │ ├── PaypalPaymentGateway.php │ └── StripePaymentGateway.php ├── Adapter ├── Component │ └── UserScore.php ├── OldIntegration.php ├── ScoreIntegrationAdapter.php └── ScoreIntegrationTarget.php ├── Bridge ├── AboutPage.php ├── DarkTheme.php ├── HomePage.php ├── LightTheme.php ├── Theme.php └── WebPage.php ├── Builder ├── AbstractPaymentMethodBuilder.php ├── DirectDebitPaymentMethodBuilder.php ├── PaymentMethodDirector.php └── Product │ ├── PaymentMethod.php │ └── TypeEnum.php ├── ChainOfResponsibility ├── AbstractLogger.php ├── ConsoleLogger.php ├── ErrorLogger.php ├── FileLogger.php ├── LogLevelEnum.php └── Logger.php ├── Command ├── AbstractPostCommand.php ├── Component │ └── Post.php ├── CreatePostCommand.php ├── DeletePostCommand.php ├── PostManager.php └── UpdatePostCommand.php ├── Composite ├── Contact.php ├── GroupComposite.php └── MessengerComponent.php ├── Decorator ├── Character.php ├── CharacterDecorator.php ├── Classes │ ├── Archer.php │ ├── Knight.php │ └── Wizard.php └── Items │ ├── GoblinRing.php │ ├── HermesBoots.php │ ├── LighteningBoltOfZeus.php │ ├── Mjolnir.php │ ├── SwordOfTheDestiny.php │ └── TridentOfPoseidon.php ├── Facade ├── Component │ ├── BarCodeUtil.php │ ├── IntegrationSoapErp.php │ ├── MarketplaceIntegration.php │ ├── Product.php │ └── ProductRepository.php └── ProductFlowFacade.php ├── FactoryMethod ├── CalculatorFactory.php ├── ElectronicTaxStrategyFactory.php ├── FoodTaxStrategyFactory.php └── TaxFreeStrategyFactory.php ├── Flyweight ├── CarStoreFlyweight.php └── Component │ ├── Car.php │ ├── Characteristic.php │ └── ColorEnum.php ├── Interpreter ├── AuthorSearch.php ├── Component │ ├── BlogPost.php │ └── BlogPostRepository.php ├── DateSearch.php ├── Expression.php ├── SearchQueryInterpreter.php └── TitleSearch.php ├── Iterator ├── UserCollectionIterator.php └── UserColletion.php ├── Mediator ├── ChatRoom.php ├── ChatRoomMediator.php └── User.php ├── Memento ├── Caretaker.php ├── TextEditor.php ├── TextSnapshot.php └── TextSnapshotMemento.php ├── Money ├── Currency.php └── Money.php ├── NullObject ├── BaseEvent.php ├── Component │ ├── EventDispatcher.php │ ├── EventDispatcherInterface.php │ └── NullEventDispatcher.php └── InvoiceCreatedEvent.php ├── ObjectPool ├── DatabaseConnection.php └── DatabaseConnectionPool.php ├── Observer ├── Component │ ├── Invoice.php │ └── InvoiceEventType.php ├── EmailAlertListener.php ├── EventListener.php ├── InvoiceEventManager.php └── PushNotificationListener.php ├── Prototype ├── BookA.php ├── BookB.php └── BookPrototype.php ├── Proxy ├── Component │ └── Address.php ├── GoogleMapsIntegration.php ├── GoogleMapsIntegrationProxy.php └── GoogleMapsIntegrationSubject.php ├── Singleton └── DBConnectionSingleton.php ├── State ├── InvoiceContext.php ├── State.php ├── StateInvoiceApproved.php ├── StateInvoiceCanceled.php ├── StateInvoiceDelivered.php ├── StateInvoicePending.php └── StateInvoiceShipped.php ├── Strategy ├── Component │ └── Product.php ├── Context.php ├── ElectronicTaxStrategy.php ├── FoodTaxStrategy.php ├── TaxCalculatorStrategy.php └── TaxFreeStrategy.php ├── TemplateMethod ├── CertificateGenerator.php ├── Component │ └── StudentDto.php ├── DevCertificateGenerator.php ├── MarketingCertificateGenerator.php └── PhotoCertificateGenerator.php └── Visitor ├── AdminUser.php ├── CanEditVisitor.php ├── EditorUser.php ├── RoleVisitor.php ├── User.php └── ViewerUser.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-fpm-alpine 2 | COPY . /app 3 | WORKDIR /app 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Gabriel Anhaia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | 3 | COLOR_RESET = \033[0m 4 | COLOR_INFO = \033[32m 5 | COLOR_COMMENT = \033[33m 6 | COLOR_TITLE = \033[0;31m 7 | 8 | help: 9 | @echo " ${COLOR_TITLE}> GENERAL COMMANDS:${COLOR_RESET}" 10 | @echo " ${COLOR_COMMENT}# make${COLOR_RESET} ${COLOR_INFO}build${COLOR_RESET} (Docker command)" 11 | @echo " ${COLOR_TITLE}> LIST OF DESIGN PATTERNS AVAILABLE WITH HELP COMMANDS:${COLOR_RESET}" 12 | @echo " ${COLOR_COMMENT}Abstract Factory:${COLOR_RESET} ${COLOR_INFO}# make abstract-factory${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make abstract-factory-help${COLOR_RESET}" 13 | @echo " ${COLOR_COMMENT}Adapter:${COLOR_RESET} ${COLOR_INFO}# make adapter${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make adapter-help${COLOR_RESET}" 14 | @echo " ${COLOR_COMMENT}Builder:${COLOR_RESET} ${COLOR_INFO}# make builder${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make builder-help${COLOR_RESET}" 15 | @echo " ${COLOR_COMMENT}Composite:${COLOR_RESET} ${COLOR_INFO}# make composite${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make composite-help${COLOR_RESET}" 16 | @echo " ${COLOR_COMMENT}Decorator:${COLOR_RESET} ${COLOR_INFO}# make decorator${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make decorator-help${COLOR_RESET}" 17 | @echo " ${COLOR_COMMENT}Facade:${COLOR_RESET} ${COLOR_INFO}# make facade${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make facade-help${COLOR_RESET}" 18 | @echo " ${COLOR_COMMENT}Factory Method:${COLOR_RESET} ${COLOR_INFO}# make factory-method${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make factory-method-help${COLOR_RESET}" 19 | @echo " ${COLOR_COMMENT}Flyweight:${COLOR_RESET} ${COLOR_INFO}# make flyweight${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make flyweight-help${COLOR_RESET}" 20 | @echo " ${COLOR_COMMENT}Iterator:${COLOR_RESET} ${COLOR_INFO}# make iterator${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make iterator-help${COLOR_RESET}" 21 | @echo " ${COLOR_COMMENT}Memento:${COLOR_RESET} ${COLOR_INFO}# make memento${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make memento-help${COLOR_RESET}" 22 | @echo " ${COLOR_COMMENT}Money:${COLOR_RESET} ${COLOR_INFO}# make money${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make money-help${COLOR_RESET}" 23 | @echo " ${COLOR_COMMENT}Null Object:${COLOR_RESET} ${COLOR_INFO}# make null-object${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make null-object-help${COLOR_RESET}" 24 | @echo " ${COLOR_COMMENT}Observer:${COLOR_RESET} ${COLOR_INFO}# make observer${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make observer-help${COLOR_RESET}" 25 | @echo " ${COLOR_COMMENT}Prototype:${COLOR_RESET} ${COLOR_INFO}# make prototype${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make prototype-help${COLOR_RESET}" 26 | @echo " ${COLOR_COMMENT}Proxy:${COLOR_RESET} ${COLOR_INFO}# make proxy${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make proxy-help${COLOR_RESET}" 27 | @echo " ${COLOR_COMMENT}Singleton:${COLOR_RESET} ${COLOR_INFO}# make singleton${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make singleton-help${COLOR_RESET}" 28 | @echo " ${COLOR_COMMENT}State:${COLOR_RESET} ${COLOR_INFO}# make state${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make state-help${COLOR_RESET}" 29 | @echo " ${COLOR_COMMENT}Strategy:${COLOR_RESET} ${COLOR_INFO}# make strategy${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make strategy-help${COLOR_RESET}" 30 | @echo " ${COLOR_COMMENT}Template Method:${COLOR_RESET} ${COLOR_INFO}# make template-method${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make template-method-help${COLOR_RESET}" 31 | @echo " ${COLOR_COMMENT}Command:${COLOR_RESET} ${COLOR_INFO}# make command${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make command-help${COLOR_RESET}" 32 | @echo " ${COLOR_COMMENT}Interpreter:${COLOR_RESET} ${COLOR_INFO}# make interpreter${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make interpreter-help${COLOR_RESET}" 33 | @echo " ${COLOR_COMMENT}Bridge:${COLOR_RESET} ${COLOR_INFO}# make bridge${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make bridge-help${COLOR_RESET}" 34 | @echo " ${COLOR_COMMENT}Visitor:${COLOR_RESET} ${COLOR_INFO}# make visitor${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make visitor-help${COLOR_RESET}" 35 | @echo " ${COLOR_COMMENT}Mediator:${COLOR_RESET} ${COLOR_INFO}# make mediator${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make mediator-help${COLOR_RESET}" 36 | @echo " ${COLOR_COMMENT}Object Pool:${COLOR_RESET} ${COLOR_INFO}# make object-pool${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make object-pool-help${COLOR_RESET}" 37 | @echo " ${COLOR_COMMENT}Chain of Responsibility:${COLOR_RESET} ${COLOR_INFO}# make chain-of-responsibility${COLOR_RESET} ${COLOR_COMMENT}OR${COLOR_RESET} ${COLOR_INFO}# make chain-of-responsibility-help${COLOR_RESET}" 38 | 39 | # Design Patterns Section 40 | build: 41 | docker build -t design-patterns . 42 | 43 | strategy: 44 | docker run -it design-patterns php /app/example/strategy.php 45 | 46 | template-method: 47 | docker run -it design-patterns php /app/example/template_method.php 48 | 49 | adapter: 50 | docker run -it design-patterns php /app/example/adapter.php 51 | 52 | null-object: 53 | docker run -it design-patterns php /app/example/null_object.php 54 | 55 | facade: 56 | docker run -it design-patterns php /app/example/facade.php 57 | 58 | builder: 59 | docker run -it design-patterns php /app/example/builder.php 60 | 61 | observer: 62 | docker run -it design-patterns php /app/example/observer.php 63 | 64 | decorator: 65 | docker run -it design-patterns php /app/example/decorator.php 66 | 67 | money: 68 | docker run -it design-patterns php /app/example/money.php 69 | 70 | factory-method: 71 | docker run -it design-patterns php /app/example/factory_method.php 72 | 73 | abstract-factory: 74 | docker run -it design-patterns php /app/example/abstract_factory.php 75 | 76 | proxy: 77 | docker run -it design-patterns php /app/example/proxy.php 78 | 79 | singleton: 80 | docker run -it design-patterns php /app/example/singleton.php 81 | 82 | memento: 83 | docker run -it design-patterns php /app/example/memento.php 84 | 85 | flyweight: 86 | docker run -it design-patterns php /app/example/flyweight.php 87 | 88 | composite: 89 | docker run -it design-patterns php /app/example/composite.php 90 | 91 | state: 92 | docker run -it design-patterns php /app/example/state.php 93 | 94 | prototype: 95 | docker run -it design-patterns php /app/example/prototype.php 96 | 97 | iterator: 98 | docker run -it design-patterns php /app/example/iterator.php 99 | 100 | command: 101 | docker run -it design-patterns php /app/example/command.php 102 | 103 | interpreter: 104 | docker run -it design-patterns php /app/example/interpreter.php 105 | 106 | bridge: 107 | docker run -it design-patterns php /app/example/bridge.php 108 | 109 | visitor: 110 | docker run -it design-patterns php /app/example/visitor.php 111 | 112 | mediator: 113 | docker run -it design-patterns php /app/example/mediator.php 114 | 115 | object-pool: 116 | docker run -it design-patterns php /app/example/object_pool.php 117 | 118 | chain-of-responsibility: 119 | docker run -it design-patterns php /app/example/chain_of_responsibility.php 120 | 121 | # Help Section 122 | 123 | abstract-factory-help: 124 | @echo "${COLOR_INFO}\n\t> The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme.${COLOR_RESET}\n" 125 | 126 | adapter-help: 127 | @echo "${COLOR_INFO}\n\t> The Adapter pattern converts the interface of a class into another interface the clients expect.${COLOR_RESET}\n" 128 | 129 | builder-help: 130 | @echo "${COLOR_INFO}\n\t> The Builder pattern separates the construction of a complex object from its representation.${COLOR_RESET}\n" 131 | 132 | composite-help: 133 | @echo "${COLOR_INFO}\n\t> The Composite pattern composes objects into tree structures to represent part-whole hierarchies.${COLOR_RESET}\n" 134 | 135 | decorator-help: 136 | @echo "${COLOR_INFO}\n\t> The Decorator pattern dynamically adds/overrides behaviour in an existing method of an object.${COLOR_RESET}\n" 137 | 138 | facade-help: 139 | @echo "${COLOR_INFO}\n\t> The Facade pattern provides a simplified interface to a larger body of code.${COLOR_RESET}\n" 140 | 141 | factory-method-help: 142 | @echo "${COLOR_INFO}\n\t> The Factory Method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate.${COLOR_RESET}\n" 143 | 144 | flyweight-help: 145 | @echo "${COLOR_INFO}\n\t> The Flyweight pattern minimizes memory use by sharing as much data as possible with other similar objects.${COLOR_RESET}\n" 146 | 147 | iterator-help: 148 | @echo "${COLOR_INFO}\n\t> The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.${COLOR_RESET}\n" 149 | 150 | memento-help: 151 | @echo "${COLOR_INFO}\n\t> The Memento pattern provides the ability to restore an object to its previous state.${COLOR_RESET}\n" 152 | 153 | money-help: 154 | @echo "${COLOR_INFO}\n\t> The Money pattern helps in avoiding floating point arithmetic issues when dealing with money.${COLOR_RESET}\n" 155 | 156 | null-object-help: 157 | @echo "${COLOR_INFO}\n\t> The Null Object pattern provides an object as a surrogate for the lack of an object of a given type.${COLOR_RESET}\n" 158 | 159 | observer-help: 160 | @echo "${COLOR_INFO}\n\t> The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified.${COLOR_RESET}\n" 161 | 162 | prototype-help: 163 | @echo "${COLOR_INFO}\n\t> The Prototype pattern specifies the kind of object to create using a prototypical instance and creates new objects by copying this prototype.${COLOR_RESET}\n" 164 | 165 | proxy-help: 166 | @echo "${COLOR_INFO}\n\t> The Proxy pattern provides a surrogate or placeholder for another object to control access to it.${COLOR_RESET}\n" 167 | 168 | singleton-help: 169 | @echo "${COLOR_INFO}\n\t> The Singleton pattern ensures a class only has one instance, and provides a global point of access to it.${COLOR_RESET}\n" 170 | 171 | state-help: 172 | @echo "${COLOR_INFO}\n\t> The State pattern allows an object to alter its behavior when its internal state changes.${COLOR_RESET}\n" 173 | 174 | strategy-help: 175 | @echo "${COLOR_INFO}\n\t> The Strategy pattern defines a family of algorithms, encapsulate each one, and make them interchangeable.${COLOR_RESET}\n" 176 | 177 | template-method-help: 178 | @echo "${COLOR_INFO}\n\t> The Template Method pattern defines the skeleton of an algorithm in an operation, deferring some steps to subclasses.${COLOR_RESET}\n" 179 | 180 | command-help: 181 | @echo "${COLOR_INFO}\n\t> The Command pattern encapsulates a request as an object, thereby allowing users to parameterize other objects with queues, requests, and operations.${COLOR_RESET}\n" 182 | 183 | interpreter-help: 184 | @echo "${COLOR_INFO}\n\t> The Interpreter pattern implements a specialized language.${COLOR_RESET}\n" 185 | 186 | bridge-help: 187 | @echo "${COLOR_INFO}\n\t> The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently. It promotes composition over inheritance.${COLOR_RESET}\n" 188 | 189 | visitor-help: 190 | @echo "${COLOR_INFO}\n\t> The Visitor pattern defines a new operation to a class without change.${COLOR_RESET}\n" 191 | 192 | mediator-help: 193 | @echo "${COLOR_INFO}\n\t> The Mediator pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.${COLOR_RESET}\n" 194 | 195 | object-pool-help: 196 | @echo "${COLOR_INFO}\n\t> The Object Pool pattern avoids expensive acquisition and release of resources by recycling objects that are no longer in use.${COLOR_RESET}\n" 197 | 198 | chain-of-responsibility-help: 199 | @echo "${COLOR_INFO}\n\t> The Chain of Responsibility pattern avoids coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.${COLOR_RESET}\n" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Logo - PHP 8.1 Design Patterns 2 | 3 | ![Licence](https://img.shields.io/badge/licence-MIT-blue) 4 | ![Package Stars](https://img.shields.io/badge/stars-%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-yellow) 5 | [![Build Status](https://travis-ci.com/gabrielanhaia/php-circuit-breaker.svg?branch=master)](https://travis-ci.com/gabrielanhaia/php-circuit-breaker) 6 | ![Code Coverage](https://img.shields.io/badge/coverage-100%25-green) 7 | 8 | Every week a new Pattern + Article on www.medium.com/@anhaia.gabriel and www.medium.com/devwarlocks 9 | 10 | # PHP 8.2+ - Design Patterns 11 | 12 | This repository was created to show the implementation of various Design Patterns using PHP 8.2. There is no 13 | dependency on a framework, and the examples are the most real as possible based on my own experiences solving real-life 14 | problems. Applying the design pattern and its concept in any PHP project will be effortless once you learn the design 15 | pattern and its concept. 16 | 17 | ### Classification/Type 18 | 19 | | Type | General Article | Diagrams | 20 | |--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| 21 | | ![Creational](https://img.shields.io/badge/Creational-light_green) | [LEARN HERE 📖](https://medium.com/devwarlocks/comparing-creational-design-patterns-making-the-right-choice-428cc351e926) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md) | 22 | | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md) | 23 | | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | SOON | SOON | 24 | 25 | ## Patterns implemented + Article 26 | 27 | | # | 📖 Pattern | Classification~Type | Code Example / Implementation | ✍️ Article / Post 📚 | Diagram | 28 | |----|-------------------------|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| 29 | | 1 | Abstract Factory | ![Creational](https://img.shields.io/badge/Creational-light_green) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/AbstractFactory) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md#abstract-factory) | 30 | | 2 | Builder | ![Creational](https://img.shields.io/badge/Creational-light_green) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Builder) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md#builder) | 31 | | 3 | Factory Method | ![Creational](https://img.shields.io/badge/Creational-light_green) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/FactoryMethod) | [LEARN HERE 📖](https://medium.com/devwarlocks/factory-method-pattern-php-design-patterns-3d1df7707c29) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md#factory-method) | 32 | | 4 | Prototype | ![Creational](https://img.shields.io/badge/Creational-light_green) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Prototype) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md#prototype) | 33 | | 5 | Singleton | ![Creational](https://img.shields.io/badge/Creational-light_green) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Singleton) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md#singleton) | 34 | | 6 | Object Pool | ![Creational](https://img.shields.io/badge/Creational-light_green) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/ObjectPool) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Creational.md#object-pool) | 35 | | 7 | Adapter | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Adapter) | [LEARN HERE 📖 ](https://medium.com/mestredev/adapter-php-8-75e00034ae48) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#adapter) | 36 | | 8 | Composite | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Composite) | [LEARN HERE 📖](https://medium.com/devwarlocks/composite-php-desing-patterns-dd01b1d010f7) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#composite) | 37 | | 9 | Bridge | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Bridge) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#bridge) | 38 | | 10 | Facade | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Facade) | [LEARN HERE 📖 ](https://medium.com/mestredev/facade-php-8-design-patterns-40b1ef8566b5) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#facade) | 39 | | 11 | Decorator | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Decorator) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#decorator) | 40 | | 12 | Flyweight | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Flyweight) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#flyweight) | 41 | | 13 | Proxy | ![Structural](https://img.shields.io/badge/Structural-8A2BE2) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Proxy) | SOON | [HERE](https://github.com/gabrielanhaia/php-design-patterns/blob/main/docs/diagrams/Structural.md#proxy) | 42 | | 14 | Template Method | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/TemplateMethod) | [LEARN HERE 📖 ](https://medium.com/mestredev/template-method-php-8-a357f3665a4b) | SOON | 43 | | 15 | Strategy | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Strategy) | [LEARN HERE 📖 ](https://medium.com/mestredev/strategy-in-php-8-design-patterns-2044e5ef54ed) | SOON | 44 | | 16 | Iterator | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Iterator) | SOON | SOON | 45 | | 17 | Memento | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Memento) | SOON | SOON | 46 | | 18 | Money | ![X](https://img.shields.io/badge/X-grey) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Money) | [LEARN HERE 📖](https://medium.com/devwarlocks/money-pattern-with-symfony-framework-55eff713b3d5) | SOON | 47 | | 19 | Null Object | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/NullObject) | SOON | SOON | 48 | | 20 | Observer | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Observer) | [LEARN HERE 📖](https://medium.com/devwarlocks/observer-php-design-patterns-a4367b137324) | SOON | 49 | | 21 | State | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/State) | SOON | SOON | 50 | | 22 | Command | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Command) | SOON | SOON | 51 | | 23 | Chain of Responsibility | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/ChainOfResponsibility) | SOON | SOON | 52 | | 24 | Interpreter | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Interpreter) | SOON | SOON | 53 | | 25 | Mediator | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Mediator) | SOON | SOON | 54 | | 26 | Visitor | ![Behavioral](https://img.shields.io/badge/Behavioral-red) | [HERE](https://github.com/gabrielanhaia/php-design-patterns/tree/main/src/Visitor) | [LEARN HERE 📖](https://medium.com/devwarlocks/visitor-php-design-patterns-289224470ebd) | SOON | 55 | 56 | ## Dependencies 57 | 58 | - [Docker](https://www.docker.com) 59 | 60 | ## Running the project 61 | 62 | I strongly recommend that you follow the articles (links above) and run them with Docker. It will be much easier, and 63 | you 64 | need to install Docker on your computer, independent of the OS you are using. With a few commands, you will run/test all 65 | the Design Patterns implemented throughout the course. 66 | 67 | #### 1. Build the container: 68 | 69 | ```bash 70 | docker build -t design-patterns . 71 | ``` 72 | 73 | or if you prefer 74 | 75 | ```bash 76 | make build 77 | ``` 78 | 79 | #### 2. Run the following commands to test the design patterns: 80 | 81 | ```bash 82 | make help 83 | ``` 84 | 85 | and then you will see the list of commands 86 | 87 | #### 3. You need to run the command with the pattern you want to test, for example: 88 | 89 | ```bash 90 | make strategy 91 | ``` 92 | 93 | #### 4. In case you want to see a short description of the pattern, you can run the `make PATTERN_NAME-help`, for example: 94 | 95 | ```bash 96 | make strategy-help 97 | ``` 98 | 99 | With that, you should see a one-line description of the pattern. 100 | 101 | ## Additional Information 102 | 103 | - If you take a look at the examples in the blog or inside the folders `/src/{$pattern_name}/` you will notice that 104 | almost 105 | All the examples contain a folder called `components`; those folders are the files/classes used to demonstrate how to 106 | use each pattern. Never consider what is in there as part of the pattern! They are not examples to be followed in your 107 | applications. 108 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | web: 4 | build: . 5 | volumes: 6 | - .:/app 7 | -------------------------------------------------------------------------------- /docs/diagrams/Creational.md: -------------------------------------------------------------------------------- 1 | # Creational Patterns 2 | 3 | ## Abstract Factory 4 | 5 | ```mermaid 6 | classDiagram 7 | Client -- AbstractFactory : Uses 8 | AbstractFactory <|.. ConcreteFactory1 : Implements 9 | AbstractFactory <|.. ConcreteFactory2 : Implements 10 | AbstractFactory : +createProductA() 11 | AbstractFactory : +createProductB() 12 | Client -- AbstractProductA : Uses 13 | Client -- AbstractProductB : Uses 14 | AbstractProductA <|.. ProductA1 : Implements 15 | AbstractProductA <|.. ProductA2 : Implements 16 | AbstractProductB <|.. ProductB1 : Implements 17 | AbstractProductB <|.. ProductB2 : Implements 18 | ConcreteFactory1 : +createProductA() 19 | ConcreteFactory1 : +createProductB() 20 | ConcreteFactory2 : +createProductA() 21 | ConcreteFactory2 : +createProductB() 22 | ``` 23 | 24 | --- 25 | 26 | ## Builder 27 | 28 | ```mermaid 29 | classDiagram 30 | Client -- Director : Uses 31 | Director o-- Builder : Uses 32 | Builder <|.. ConcreteBuilder : Implements 33 | Builder : +buildPartA() 34 | Builder : +buildPartB() 35 | Builder : +getProduct() 36 | ConcreteBuilder : +buildPartA() 37 | ConcreteBuilder : +buildPartB() 38 | ConcreteBuilder : +getProduct() 39 | Product <-- ConcreteBuilder : Builds 40 | Director : +construct() 41 | 42 | ``` 43 | 44 | --- 45 | 46 | ## Factory Method 47 | 48 | ```mermaid 49 | classDiagram 50 | Client -- Creator : Uses 51 | Creator <|.. ConcreteCreator : Implements 52 | Creator : +factoryMethod() 53 | Product <-- Creator : Creates 54 | ConcreteCreator : +factoryMethod() 55 | Product <|.. ConcreteProduct : Implements 56 | ConcreteProduct <-- ConcreteCreator : Creates 57 | ``` 58 | 59 | --- 60 | 61 | ## Prototype 62 | 63 | ```mermaid 64 | classDiagram 65 | Client -- Prototype : Uses 66 | Prototype <|.. ConcretePrototype : Implements 67 | Prototype : +clone() 68 | ConcretePrototype : +clone() 69 | Client -- ConcretePrototype : Creates clones 70 | ``` 71 | 72 | --- 73 | 74 | ## Singleton 75 | 76 | ```mermaid 77 | classDiagram 78 | Client -- Singleton : Uses 79 | Singleton : -Singleton() Private 80 | Singleton : +getInstance() Static 81 | ``` 82 | 83 | --- 84 | 85 | ## Object Pool 86 | 87 | ```mermaid 88 | classDiagram 89 | Client -- ObjectPool : Uses 90 | ObjectPool : +acquireReusable() 91 | ObjectPool : +releaseReusable(Reusable) 92 | Reusable <-- ObjectPool : Manages 93 | ``` 94 | -------------------------------------------------------------------------------- /docs/diagrams/Structural.md: -------------------------------------------------------------------------------- 1 | # Structural Patterns 2 | 3 | ## Adapter 4 | 5 | ```mermaid 6 | classDiagram 7 | class Target { 8 | +request(): void 9 | } 10 | 11 | class Adapter { 12 | -adaptee: Adaptee 13 | +request(): void 14 | } 15 | 16 | class Adaptee { 17 | +specificRequest(): void 18 | } 19 | 20 | Target <|-- Adapter : adapts 21 | Adapter o-- Adaptee : has-a 22 | 23 | ``` 24 | 25 | --- 26 | 27 | ## Bridge 28 | 29 | ```mermaid 30 | classDiagram 31 | Client -- Abstraction : Uses 32 | Abstraction o-- Implementor : Uses 33 | Implementor <|.. ConcreteImplementorA : Implements 34 | Implementor <|.. ConcreteImplementorB : Implements 35 | Abstraction : +operation() 36 | Implementor : +operationImpl() 37 | ConcreteImplementorA : +operationImpl() 38 | ConcreteImplementorB : +operationImpl() 39 | ``` 40 | 41 | --- 42 | 43 | ## Decorator 44 | 45 | ```mermaid 46 | classDiagram 47 | Client -- Component : Uses 48 | Decorator --|> Component : Extends 49 | ConcreteDecoratorA --|> Decorator : Extends 50 | ConcreteDecoratorB --|> Decorator : Extends 51 | ConcreteComponent --|> Component : Implements 52 | Decorator o-- Component : Wraps 53 | Component : +operation() 54 | Decorator : +operation() 55 | ConcreteDecoratorA : +addedBehavior() 56 | ConcreteDecoratorB : +addedBehavior() 57 | ``` 58 | 59 | --- 60 | 61 | 62 | ## Facade 63 | 64 | ```mermaid 65 | classDiagram 66 | Client -- Facade : Uses 67 | Facade o-- SubsystemClassA : Uses 68 | Facade o-- SubsystemClassB : Uses 69 | 70 | class Facade { 71 | +operationWrapper() 72 | } 73 | 74 | class SubsystemClassA { 75 | +operationA() 76 | } 77 | 78 | class SubsystemClassB { 79 | +operationB() 80 | } 81 | ``` 82 | 83 | --- 84 | 85 | ## Proxy 86 | 87 | ```mermaid 88 | classDiagram 89 | class Subject { 90 | +request(): void 91 | } 92 | 93 | class RealSubject { 94 | +request(): void 95 | } 96 | 97 | class Proxy { 98 | -realSubject: RealSubject 99 | +request(): void 100 | } 101 | 102 | Subject <|-- RealSubject 103 | Subject <|-- Proxy 104 | Proxy o-- RealSubject 105 | ``` 106 | 107 | 108 | --- 109 | 110 | ## Flyweight 111 | 112 | ```mermaid 113 | classDiagram 114 | class FlyweightFactory { 115 | +getFlyweight(key): Flyweight 116 | } 117 | 118 | class Flyweight { 119 | +operation(extrinsicState): void 120 | } 121 | 122 | class ConcreteFlyweight { 123 | -intrinsicState: string 124 | +operation(extrinsicState): void 125 | } 126 | 127 | FlyweightFactory o-- Flyweight 128 | Flyweight <|-- ConcreteFlyweight 129 | ``` 130 | 131 | 132 | --- 133 | 134 | ## Composite 135 | 136 | ```mermaid 137 | classDiagram 138 | class Component { 139 | +operation(): void 140 | +add(child: Component): void 141 | +remove(child: Component): void 142 | +getChild(index): Component 143 | } 144 | 145 | class Leaf { 146 | +operation(): void 147 | } 148 | 149 | class Composite { 150 | +operation(): void 151 | +add(child: Component): void 152 | +remove(child: Component): void 153 | +getChild(index): Component 154 | } 155 | 156 | Component <|-- Leaf 157 | Component <|-- Composite 158 | Composite o-- Component : contains 159 | ``` 160 | -------------------------------------------------------------------------------- /example/abstract_factory.php: -------------------------------------------------------------------------------- 1 | createCarrierCompany()->ship($orderData); 13 | ln(); 14 | $americanOrderFactory->createPaymentGateway()->pay($orderData); 15 | ln(); 16 | 17 | // ------------- 18 | 19 | $germanOrderFactory = new GermanOrderFactory(); 20 | lns();ln(); 21 | $germanOrderFactory->createCarrierCompany()->ship($orderData); 22 | ln(); 23 | $germanOrderFactory->createPaymentGateway()->pay($orderData); -------------------------------------------------------------------------------- /example/adapter.php: -------------------------------------------------------------------------------- 1 | getUserScore($userUuid); 13 | 14 | dump($users); 15 | -------------------------------------------------------------------------------- /example/autoloader.php: -------------------------------------------------------------------------------- 1 | getContent(); 13 | 14 | ln(); 15 | 16 | $lightTheme = new LightTheme(); 17 | $aboutPage = new AboutPage($lightTheme); 18 | echo $aboutPage->getContent(); -------------------------------------------------------------------------------- /example/builder.php: -------------------------------------------------------------------------------- 1 | buildPaymentMethod(); 12 | 13 | dump($paymentMethod); 14 | 15 | // Note: You can use the Builder without the Director if you need. -------------------------------------------------------------------------------- /example/chain_of_responsibility.php: -------------------------------------------------------------------------------- 1 | setNext($errorLogger)->setNext($fileLogger); 16 | 17 | // Make a request 18 | lns();ln(); 19 | $consoleLogger->log("This is an informational message.", LogLevelEnum::INFO); 20 | lns();ln(); 21 | $consoleLogger->log("This is an error message.", LogLevelEnum::ERROR); 22 | lns();ln(); 23 | $consoleLogger->log("This is a debug message.", LogLevelEnum::DEBUG); -------------------------------------------------------------------------------- /example/command.php: -------------------------------------------------------------------------------- 1 | setCommand($createPostCommand); 14 | $manager->executeCommand(); 15 | 16 | ln(); 17 | 18 | $updatePostCommand = new UpdatePostCommand($post, 'My first post! Updated', 'Hello world! Updated!'); 19 | $manager->setCommand($updatePostCommand); 20 | $manager->executeCommand(); 21 | 22 | ln(); 23 | 24 | $deletePostCommand = new DeletePostCommand($post); 25 | $manager->setCommand($deletePostCommand); 26 | $manager->executeCommand(); 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/composite.php: -------------------------------------------------------------------------------- 1 | add($gabriel); 16 | $phpDevelopersGroup->add($lucy); 17 | $phpDevelopersGroup->add($john); 18 | $organization1->add($phpDevelopersGroup); 19 | $generalGroupWithAllSubGroups->add($organization1); 20 | 21 | dump($generalGroupWithAllSubGroups->operation()); 22 | -------------------------------------------------------------------------------- /example/decorator.php: -------------------------------------------------------------------------------- 1 | getName()); 22 | ln(); 23 | echo sprintf('Total Strength: `%s`', $knightDecorator->getTotalStrength()); 24 | ln(); 25 | echo sprintf('Total Agility: `%s`', $knightDecorator->getTotalAgility()); 26 | ln(); 27 | echo sprintf('Total Dexterity: `%s`', $knightDecorator->getTotalDexterity()); 28 | ln(); 29 | echo sprintf('Total Intelligence: `%s`', $knightDecorator->getTotalIntelligence()); 30 | ln(); 31 | echo sprintf('Total Luck: `%s`', $knightDecorator->getTotalLuck()); 32 | ln(); 33 | 34 | // Wizard Decorator example: 35 | 36 | $wizardDecorator = new Wizard(); 37 | $wizardDecorator = new GoblinRing($wizardDecorator); 38 | $wizardDecorator = new GoblinRing($wizardDecorator); 39 | $wizardDecorator = new HermesBoots($wizardDecorator); 40 | $wizardDecorator = new TridentOfPoseidon($wizardDecorator); 41 | 42 | lns(); 43 | ln(); 44 | echo sprintf('Class name `%s`', $wizardDecorator->getName()); 45 | ln(); 46 | echo sprintf('Total Strength: `%s`', $wizardDecorator->getTotalStrength()); 47 | ln(); 48 | echo sprintf('Total Agility: `%s`', $wizardDecorator->getTotalAgility()); 49 | ln(); 50 | echo sprintf('Total Dexterity: `%s`', $wizardDecorator->getTotalDexterity()); 51 | ln(); 52 | echo sprintf('Total Intelligence: `%s`', $wizardDecorator->getTotalIntelligence()); 53 | ln(); 54 | echo sprintf('Total Luck: `%s`', $wizardDecorator->getTotalLuck()); -------------------------------------------------------------------------------- /example/facade.php: -------------------------------------------------------------------------------- 1 | 'Nice Mug', 'price' => 10.5, 'stock' => 50], 18 | ['name' => 'Smartphone Model X', 'price' => 100, 'stock' => 1000], 19 | ]; 20 | $productFlowFacade->createProducts($products); -------------------------------------------------------------------------------- /example/factory_method.php: -------------------------------------------------------------------------------- 1 | setName("Test Product") 14 | ->setCategory("Test Category") 15 | ->setPrice(100); 16 | 17 | lns();ln(); 18 | echo $factoryMethod1->make()->calculate($product); 19 | ln(); 20 | echo $factoryMethod2->make()->calculate($product); 21 | ln(); 22 | echo $factoryMethod3->make()->calculate($product); 23 | ln(); 24 | lns(); -------------------------------------------------------------------------------- /example/flyweight.php: -------------------------------------------------------------------------------- 1 | getCharacteristics('Chevrolet', 'Chevette', ColorEnum::BLUE) 14 | ); 15 | 16 | $chevette2 = new Car( 17 | 'XYZ123', 18 | $carStore->getCharacteristics('Chevrolet', 'Chevette', ColorEnum::BLUE) 19 | ); 20 | 21 | if ($chevette1->getCharacteristic() === $chevette2->getCharacteristic()) { 22 | dump('$chevette1->getCharacteristics() is literally the same object as $chevette2->getCharacteristics()'); 23 | } -------------------------------------------------------------------------------- /example/interpreter.php: -------------------------------------------------------------------------------- 1 | interpret('title:My Blog Post'); 18 | // $posts now contains an array of BlogPost objects with the title 'My Blog Post' 19 | foreach ($posts as $post) { 20 | echo $post; 21 | ln(); 22 | } 23 | 24 | lns(); 25 | ln(); 26 | echo "Search by author: 'John Doe'"; 27 | ln(); 28 | 29 | $posts = $interpreter->interpret('author:John Doe'); 30 | // $posts now contains an array of BlogPost objects written by 'John Doe' 31 | foreach ($posts as $post) { 32 | echo $post; 33 | ln(); 34 | } 35 | 36 | lns(); 37 | ln(); 38 | echo "Search by date: '2023-07-01'"; 39 | ln(); 40 | 41 | $posts = $interpreter->interpret('date:2023-07-01'); 42 | // $posts now contains an array of BlogPost objects published on 2023-07-01 43 | foreach ($posts as $post) { 44 | echo $post; 45 | ln(); 46 | } 47 | -------------------------------------------------------------------------------- /example/iterator.php: -------------------------------------------------------------------------------- 1 | addUser('Gabriel') 9 | ->addUser('Alice') 10 | ->addUser('Braian') 11 | ->addUser('George') 12 | ->addUser('Lucy'); 13 | 14 | lns(); 15 | ln(); 16 | echo '------- Regular traversal -------'; 17 | ln(); 18 | foreach ($userCollection->getIterator() as $user) { 19 | echo $user; 20 | ln(); 21 | } 22 | 23 | lns(); 24 | ln(); 25 | 26 | echo '------- Reverse traversal -------'; 27 | ln(); 28 | foreach ($userCollection->getReverseIterator() as $user) { 29 | echo $user; 30 | ln(); 31 | } -------------------------------------------------------------------------------- /example/mediator.php: -------------------------------------------------------------------------------- 1 | send('Hi there!'); 15 | ln(); 16 | $jane->send('Hey!'); -------------------------------------------------------------------------------- /example/memento.php: -------------------------------------------------------------------------------- 1 | setText('VERSION 1'); 9 | 10 | $caretaker = new Memento\Caretaker($textEditor); 11 | $caretaker->backup(); 12 | 13 | sleep(1); 14 | $textEditor->setText('VERSION 2'); 15 | $caretaker->backup(); 16 | 17 | sleep(1); 18 | $textEditor->setText('VERSION 3'); 19 | 20 | dump([ 21 | 'LAST TEXT SNAPSHOT' => $textEditor->getText(), 22 | 'LAST SNAPSHOT CREATED AT' => $caretaker->getLastSnapshotDate() ? $caretaker->getLastSnapshotDate()->format('Y-m-d H:i:s') : null 23 | ]); 24 | 25 | $lastSnapshotDate = $caretaker->getLastSnapshotDate() ? $caretaker->getLastSnapshotDate()->format('Y-m-d H:i:s') : null; 26 | $caretaker->undo(); 27 | dump([ 28 | 'LAST TEXT SNAPSHOT' => $textEditor->getText(), 29 | 'LAST SNAPSHOT CREATED AT' => $lastSnapshotDate 30 | ]); 31 | 32 | $lastSnapshotDate = $caretaker->getLastSnapshotDate() ? $caretaker->getLastSnapshotDate()->format('Y-m-d H:i:s') : null; 33 | $caretaker->undo(); 34 | dump([ 35 | 'LAST TEXT SNAPSHOT' => $textEditor->getText(), 36 | 'LAST SNAPSHOT CREATED AT' => $lastSnapshotDate 37 | ]); 38 | -------------------------------------------------------------------------------- /example/money.php: -------------------------------------------------------------------------------- 1 | isEqualTo($secondAmount) ? 'YES' : 'NO'; 12 | $isGreaterThan = $firstAmount->isGreaterThan($secondAmount) ? 'YES' : 'NO'; 13 | $isLowerThan = $firstAmount->isLowerThan($secondAmount) ? 'YES' : 'NO'; 14 | 15 | lns();ln(); 16 | echo '$firstAmount->isEqualTo(): ' . $isEqualTo . " ({$firstAmount} === {$secondAmount})"; 17 | ln(); 18 | echo '$firstAmount->isGreaterThan(): ' . $isGreaterThan . " ({$firstAmount} > {$secondAmount})"; 19 | ln(); 20 | echo '$firstAmount->isLowerThan(): ' . $isLowerThan . " ({$firstAmount} < {$secondAmount})"; 21 | ln(); 22 | -------------------------------------------------------------------------------- /example/null_object.php: -------------------------------------------------------------------------------- 1 | dispatch(); 14 | 15 | $invoiceCreatedEventNullObject = new InvoiceCreatedEvent($nullEventDispatcher); 16 | $invoiceCreatedEventNullObject->dispatch(); 17 | $invoiceCreatedEventNullObject->dispatch(); 18 | -------------------------------------------------------------------------------- /example/object_pool.php: -------------------------------------------------------------------------------- 1 | get(); 11 | $connection->query("SELECT * FROM users"); 12 | $pool->release($connection); -------------------------------------------------------------------------------- /example/observer.php: -------------------------------------------------------------------------------- 1 | subscribe(InvoiceEventType::CREATED, $emailAlertListener); 20 | $invoiceEventManager->subscribe(InvoiceEventType::APPROVED, $emailAlertListener); 21 | $invoiceEventManager->subscribe(InvoiceEventType::CANCELED, $emailAlertListener); 22 | ln(); 23 | $invoiceEventManager->subscribe(InvoiceEventType::APPROVED, $pushNotificationListener); 24 | $invoiceEventManager->subscribe(InvoiceEventType::CANCELED, $pushNotificationListener); 25 | lns(); 26 | ln(); 27 | // ------------------------- 28 | 29 | $invoice = new Invoice( 30 | 1234, 31 | 100.55, 32 | InvoiceEventType::CREATED->name, 33 | 'gabriel@anhaia.com' 34 | ); 35 | 36 | $invoiceEventManager->notifySubscribers( InvoiceEventType::CREATED, $invoice, ['created_at' => new \DateTime()]); 37 | ln(); 38 | $invoice->setState(InvoiceEventType::APPROVED->name); 39 | $invoiceEventManager->notifySubscribers( InvoiceEventType::APPROVED, $invoice, ['approved_at' => new \DateTime()]); 40 | ln(); 41 | $invoice->setState(InvoiceEventType::CANCELED->name); 42 | $invoiceEventManager->notifySubscribers( InvoiceEventType::CANCELED, $invoice, ['canceled_at' => new \DateTime()]); -------------------------------------------------------------------------------- /example/prototype.php: -------------------------------------------------------------------------------- 1 | setAuthorName('Gabriel Anhaia') 9 | ->setSubject('Technology'); 10 | 11 | $books = []; 12 | for ($x = 0; $x < 10; $x++) { 13 | $books = clone $bookA; 14 | } 15 | 16 | $bookB = new Prototype\BookB(); 17 | $bookB->setAuthorName('Gabriel Anhaia') 18 | ->setSubject('Technology'); 19 | 20 | for ($x = 0; $x < 10; $x++) { 21 | $books = clone $bookB; 22 | } -------------------------------------------------------------------------------- /example/proxy.php: -------------------------------------------------------------------------------- 1 | getAddressByCoordinates(1234, 5678); 12 | dump($address); 13 | 14 | $googleMapsIntegrationProxy = new GoogleMapsIntegrationProxy($googleMapsIntegrationOriginalClass); 15 | $addressFromProxy = $googleMapsIntegrationProxy->getAddressByCoordinates(1234, 5678); 16 | dump($addressFromProxy); 17 | 18 | /** 19 | Note: The Proxy pattern is much more powerful. 20 | You can even use the proxy to manage a Cache layer between the original class, or even add extra validations in this middle layer. 21 | */ -------------------------------------------------------------------------------- /example/singleton.php: -------------------------------------------------------------------------------- 1 | doSomething(); 11 | ln(); 12 | echo $dbConnectionInstance2->doSomething(); 13 | ln(); 14 | 15 | if (spl_object_id($dbConnectionInstance1) === spl_object_id($dbConnectionInstance2)) { 16 | dump('Object "$dbConnectionInstance1" and "$dbConnectionInstance2" are exactly the same in memory.'); 17 | } -------------------------------------------------------------------------------- /example/state.php: -------------------------------------------------------------------------------- 1 | doSomething2(); 10 | $invoiceContext->doSomething1(); 11 | 12 | lns();ln(); 13 | 14 | $invoiceContext->doSomething2(); 15 | $invoiceContext->doSomething1(); 16 | 17 | lns();ln(); 18 | 19 | $invoiceContext->doSomething1(); 20 | $invoiceContext->doSomething2(); 21 | 22 | lns();ln(); 23 | 24 | $invoiceContext->doSomething1(); 25 | $invoiceContext->doSomething2(); 26 | -------------------------------------------------------------------------------- /example/strategy.php: -------------------------------------------------------------------------------- 1 | setName('Product Test') 13 | ->setCategory('electronics') 14 | ->setPrice(100); 15 | 16 | switch ($product->getCategory()) { 17 | case 'electronics': 18 | $strategy = new ElectronicTaxStrategy; 19 | break; 20 | case 'food': 21 | $strategy = new FoodTaxStrategy; 22 | break; 23 | case 'books': 24 | $strategy = new TaxFreeStrategy; 25 | break; 26 | default: 27 | throw new \Exception('Strategy not found for this category.'); 28 | } 29 | 30 | $context = new Context($strategy); 31 | $context->calculateProduct($product); 32 | 33 | lns();ln(); 34 | echo 'Product name: ' . $product->getName(); 35 | ln(); 36 | echo 'Product price: ' . $product->getPrice(); 37 | ln(); 38 | echo 'Total taxes: ' . $product->getTaxes(); 39 | -------------------------------------------------------------------------------- /example/template_method.php: -------------------------------------------------------------------------------- 1 | 'Building Applications with PHP 8', 12 | 'social_media_marketing' => 'Social Media Marketing', 13 | 'photo_masterclass' => 'Photography Masterclass' 14 | ]; 15 | 16 | $student = new StudentDto( 17 | 'Gabriel Anhaia', 18 | $coursesAvailable['php_8'], 19 | new \DateTimeImmutable(), 20 | 'gabriel@anhaia.com', 21 | 123456789 22 | ); 23 | 24 | // A Factory can be implemented here, but this is not the purpose of this example. 25 | switch ($student->getCourseName()) { 26 | case $coursesAvailable['php_8']: 27 | $template = new DevCertificateGenerator; 28 | break; 29 | case $coursesAvailable['social_media_marketing']: 30 | $template = new MarketingCertificateGenerator; 31 | break; 32 | case $coursesAvailable['photo_masterclass']: 33 | $template = new PhotoCertificateGenerator; 34 | break; 35 | default: 36 | throw new \Exception('Course does not have a template for Certificate flow.'); 37 | } 38 | 39 | $template->processCertificateFlow($student); -------------------------------------------------------------------------------- /example/visitor.php: -------------------------------------------------------------------------------- 1 | accept($canEditVisitor)) { 15 | echo 'User 1 can edit'; 16 | } else { 17 | echo 'User 1 cannot edit'; 18 | } 19 | 20 | ln();lns();ln(); 21 | $user2 = new EditorUser(); 22 | if ($user2->accept($canEditVisitor)) { 23 | echo 'User 2 can edit'; 24 | } else { 25 | echo 'User 2 cannot edit'; 26 | } 27 | 28 | ln();lns();ln(); 29 | $user3 = new ViewerUser(); 30 | if ($user3->accept($canEditVisitor)) { 31 | echo 'User 3 can edit'; 32 | } else { 33 | echo 'User 3 cannot edit'; 34 | } 35 | ln();lns(); -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielanhaia/php-design-patterns/0e3556f05a5e731f4937439fd8b84ff9fba056cd/logo.png -------------------------------------------------------------------------------- /src/AbstractFactory/AmericanOrderFactory.php: -------------------------------------------------------------------------------- 1 | uuid = $uuid; 20 | $this->name = $name; 21 | $this->score = $score; 22 | } 23 | 24 | /** 25 | * @param string $uuid 26 | * @return UserScore 27 | */ 28 | public function setUuid(string $uuid): UserScore 29 | { 30 | $this->uuid = $uuid; 31 | return $this; 32 | } 33 | 34 | /** 35 | * @param string $name 36 | * @return UserScore 37 | */ 38 | public function setName(string $name): UserScore 39 | { 40 | $this->name = $name; 41 | return $this; 42 | } 43 | 44 | /** 45 | * @return string 46 | */ 47 | public function getUuid(): string 48 | { 49 | return $this->uuid; 50 | } 51 | 52 | /** 53 | * @return string 54 | */ 55 | public function getName(): string 56 | { 57 | return $this->name; 58 | } 59 | 60 | /** 61 | * @return float 62 | */ 63 | public function getScore(): float 64 | { 65 | return $this->score; 66 | } 67 | 68 | /** 69 | * @param float $score 70 | * @return UserScore 71 | */ 72 | public function setScore(float $score): UserScore 73 | { 74 | $this->score = $score; 75 | return $this; 76 | } 77 | } -------------------------------------------------------------------------------- /src/Adapter/OldIntegration.php: -------------------------------------------------------------------------------- 1 | true 25 | //]); 26 | 27 | //$requestData = ['user_uuid' => $uuid]; 28 | 29 | //$response = $soapClient->GetScore($requestData); 30 | 31 | $response = [ 32 | 'uuid' => '7de2a62e-7628-4d2f-a02b-fe395c5ce85b', 33 | 'name' => 'Gabriel Anhaia', 34 | 'score' => 65.2 35 | ]; 36 | 37 | return $response; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Adapter/ScoreIntegrationAdapter.php: -------------------------------------------------------------------------------- 1 | oldIntegration = $oldIntegration; 23 | } 24 | 25 | /** 26 | * @param string $uuid 27 | * @return UserScore 28 | */ 29 | public function getUserScore(string $uuid): UserScore 30 | { 31 | $rawUser = $this->oldIntegration->getScore($uuid); 32 | 33 | $formattedUser = new UserScore( 34 | $rawUser['uuid'], 35 | $rawUser['name'], 36 | $rawUser['score'] 37 | ); 38 | 39 | return $formattedUser; 40 | } 41 | } -------------------------------------------------------------------------------- /src/Adapter/ScoreIntegrationTarget.php: -------------------------------------------------------------------------------- 1 | theme->getColor(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Bridge/DarkTheme.php: -------------------------------------------------------------------------------- 1 | theme->getColor(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Bridge/LightTheme.php: -------------------------------------------------------------------------------- 1 | theme = $theme; 12 | } 13 | 14 | abstract public function getContent(): string; 15 | } -------------------------------------------------------------------------------- /src/Builder/AbstractPaymentMethodBuilder.php: -------------------------------------------------------------------------------- 1 | reset(); 14 | } 15 | 16 | public function reset() 17 | { 18 | $this->paymentMethod = new PaymentMethod(); 19 | } 20 | 21 | public function getPaymentMethod(): PaymentMethod 22 | { 23 | return $this->paymentMethod; 24 | } 25 | 26 | public abstract function buildPaymentMethodType(): AbstractPaymentMethodBuilder; 27 | 28 | public abstract function buildMaxInstalments(): AbstractPaymentMethodBuilder; 29 | 30 | public abstract function buildFeePercentageOverInstalments(): AbstractPaymentMethodBuilder; 31 | 32 | public abstract function buildPercentageDiscount(): AbstractPaymentMethodBuilder; 33 | 34 | public abstract function buildIsCardAllowed(): AbstractPaymentMethodBuilder; 35 | 36 | public abstract function buildIsSepaMandateAllowed(): AbstractPaymentMethodBuilder; 37 | 38 | public abstract function buildLimitDaysRefund(): AbstractPaymentMethodBuilder; 39 | } -------------------------------------------------------------------------------- /src/Builder/DirectDebitPaymentMethodBuilder.php: -------------------------------------------------------------------------------- 1 | paymentMethod->setPaymentMethodType(TypeEnum::DEBIT); 12 | return $this; 13 | } 14 | 15 | public function buildMaxInstalments(): AbstractPaymentMethodBuilder 16 | { 17 | $this->paymentMethod->setMaxInstalments(6); 18 | return $this; 19 | } 20 | 21 | public function buildFeePercentageOverInstalments(): AbstractPaymentMethodBuilder 22 | { 23 | $this->paymentMethod->setPercentageDiscount(12.5); 24 | return $this; 25 | } 26 | 27 | public function buildPercentageDiscount(): AbstractPaymentMethodBuilder 28 | { 29 | $this->paymentMethod->setPercentageDiscount(5.0); 30 | return $this; 31 | } 32 | 33 | public function buildIsCardAllowed(): AbstractPaymentMethodBuilder 34 | { 35 | $this->paymentMethod->setIsCardAllowed(true); 36 | return $this; 37 | } 38 | 39 | public function buildIsSepaMandateAllowed(): AbstractPaymentMethodBuilder 40 | { 41 | $this->paymentMethod->setIsSepaMandateAllowed(true); 42 | return $this; 43 | } 44 | 45 | public function buildLimitDaysRefund(): AbstractPaymentMethodBuilder 46 | { 47 | $this->paymentMethod->setLimitDaysRefund(30); 48 | return $this; 49 | } 50 | } -------------------------------------------------------------------------------- /src/Builder/PaymentMethodDirector.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 14 | } 15 | 16 | public function changeBuilder(AbstractPaymentMethodBuilder $builder): void 17 | { 18 | $this->builder = $builder; 19 | } 20 | 21 | public function buildPaymentMethod(): PaymentMethod 22 | { 23 | $this->builder->buildPaymentMethodType() 24 | ->buildMaxInstalments() 25 | ->buildFeePercentageOverInstalments() 26 | ->buildPercentageDiscount() 27 | ->buildIsCardAllowed() 28 | ->buildIsSepaMandateAllowed() 29 | ->buildLimitDaysRefund(); 30 | 31 | return $this->builder->getPaymentMethod(); 32 | } 33 | } -------------------------------------------------------------------------------- /src/Builder/Product/PaymentMethod.php: -------------------------------------------------------------------------------- 1 | paymentMethodType; 27 | } 28 | 29 | /** 30 | * @param TypeEnum $paymentMethodType 31 | */ 32 | public function setPaymentMethodType(TypeEnum $paymentMethodType): void 33 | { 34 | $this->paymentMethodType = $paymentMethodType; 35 | } 36 | 37 | /** 38 | * @return int 39 | */ 40 | public function getMaxInstalments(): int 41 | { 42 | return $this->maxInstalments; 43 | } 44 | 45 | /** 46 | * @param int $maxInstalments 47 | */ 48 | public function setMaxInstalments(int $maxInstalments): void 49 | { 50 | $this->maxInstalments = $maxInstalments; 51 | } 52 | 53 | /** 54 | * @return float 55 | */ 56 | public function getFeePercentageOverInstalments(): float 57 | { 58 | return $this->feePercentageOverInstalments; 59 | } 60 | 61 | /** 62 | * @param float $feePercentageOverInstalments 63 | */ 64 | public function setFeePercentageOverInstalments(float $feePercentageOverInstalments): void 65 | { 66 | $this->feePercentageOverInstalments = $feePercentageOverInstalments; 67 | } 68 | 69 | /** 70 | * @return float 71 | */ 72 | public function getPercentageDiscount(): float 73 | { 74 | return $this->percentageDiscount; 75 | } 76 | 77 | /** 78 | * @param float $percentageDiscount 79 | */ 80 | public function setPercentageDiscount(float $percentageDiscount): void 81 | { 82 | $this->percentageDiscount = $percentageDiscount; 83 | } 84 | 85 | /** 86 | * @return bool 87 | */ 88 | public function isCardAllowed(): bool 89 | { 90 | return $this->isCardAllowed; 91 | } 92 | 93 | /** 94 | * @param bool $isCardAllowed 95 | */ 96 | public function setIsCardAllowed(bool $isCardAllowed): void 97 | { 98 | $this->isCardAllowed = $isCardAllowed; 99 | } 100 | 101 | /** 102 | * @return bool 103 | */ 104 | public function isSepaMandateAllowed(): bool 105 | { 106 | return $this->isSepaMandateAllowed; 107 | } 108 | 109 | /** 110 | * @param bool $isSepaMandateAllowed 111 | */ 112 | public function setIsSepaMandateAllowed(bool $isSepaMandateAllowed): void 113 | { 114 | $this->isSepaMandateAllowed = $isSepaMandateAllowed; 115 | } 116 | 117 | /** 118 | * @return int 119 | */ 120 | public function getLimitDaysRefund(): int 121 | { 122 | return $this->limitDaysRefund; 123 | } 124 | 125 | /** 126 | * @param int $limitDaysRefund 127 | */ 128 | public function setLimitDaysRefund(int $limitDaysRefund): void 129 | { 130 | $this->limitDaysRefund = $limitDaysRefund; 131 | } 132 | } -------------------------------------------------------------------------------- /src/Builder/Product/TypeEnum.php: -------------------------------------------------------------------------------- 1 | nextLogger = $logger; 13 | return $logger; 14 | } 15 | 16 | public function log(string $message, LogLevelEnum $level): void 17 | { 18 | if ($this->level->value <= $level->value) { 19 | $this->writeLog($message); 20 | } 21 | if ($this->nextLogger !== null) { 22 | $this->nextLogger->log($message, $level); 23 | } 24 | } 25 | 26 | abstract protected function writeLog(string $message): void; 27 | } -------------------------------------------------------------------------------- /src/ChainOfResponsibility/ConsoleLogger.php: -------------------------------------------------------------------------------- 1 | level = $level; 10 | } 11 | 12 | protected function writeLog(string $message): void 13 | { 14 | echo "Console Logger: $message\n"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ChainOfResponsibility/ErrorLogger.php: -------------------------------------------------------------------------------- 1 | level = $level; 10 | } 11 | 12 | protected function writeLog(string $message): void 13 | { 14 | echo "Error Logger: $message\n"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/ChainOfResponsibility/FileLogger.php: -------------------------------------------------------------------------------- 1 | level = $level; 10 | } 11 | 12 | protected function writeLog(string $message): void 13 | { 14 | echo "File Logger: $message\n"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/ChainOfResponsibility/LogLevelEnum.php: -------------------------------------------------------------------------------- 1 | title}' has been created.\n"; 15 | } 16 | 17 | public function update(string $newTitle, string $newContent): void 18 | { 19 | // Logic for updating a post 20 | $this->title = $newTitle; 21 | $this->content = $newContent; 22 | echo "Post has been updated to '{$this->title}'.\n"; 23 | } 24 | 25 | public function delete(): void 26 | { 27 | // Logic for deleting a post 28 | echo "Post '{$this->title}' has been deleted.\n"; 29 | } 30 | } -------------------------------------------------------------------------------- /src/Command/CreatePostCommand.php: -------------------------------------------------------------------------------- 1 | post->create(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Command/DeletePostCommand.php: -------------------------------------------------------------------------------- 1 | post->delete(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Command/PostManager.php: -------------------------------------------------------------------------------- 1 | command = $command; 12 | } 13 | 14 | public function executeCommand(): void 15 | { 16 | $this->command->execute(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Command/UpdatePostCommand.php: -------------------------------------------------------------------------------- 1 | post->update($this->newTitle, $this->newContent); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Composite/Contact.php: -------------------------------------------------------------------------------- 1 | name}"; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Composite/GroupComposite.php: -------------------------------------------------------------------------------- 1 | children = new \SplObjectStorage(); 13 | } 14 | 15 | public function add(MessengerComponent $component): void 16 | { 17 | $this->children->attach($component); 18 | $component->setParent($this); 19 | } 20 | 21 | public function remove(MessengerComponent $component): void 22 | { 23 | $this->children->detach($component); 24 | $component->setParent(null); 25 | } 26 | 27 | public function isComposite(): bool 28 | { 29 | return true; 30 | } 31 | 32 | public function operation(): string 33 | { 34 | $results = []; 35 | foreach ($this->children as $child) { 36 | $results[] = "\n\t" . $child->operation(); 37 | } 38 | 39 | return "MessageGroup:{$this->name}(" . "\t" . implode("+", $results) . "\n" . ")"; 40 | } 41 | } -------------------------------------------------------------------------------- /src/Composite/MessengerComponent.php: -------------------------------------------------------------------------------- 1 | name = $name; 14 | } 15 | 16 | public function setParent(MessengerComponent $parent) 17 | { 18 | $this->parent = $parent; 19 | } 20 | 21 | public function getParent(): MessengerComponent 22 | { 23 | return $this->parent; 24 | } 25 | 26 | public function isComposite(): bool 27 | { 28 | return false; 29 | } 30 | 31 | public function add(MessengerComponent $component): void 32 | { 33 | // Nothing happens by default. 34 | } 35 | 36 | public function remove(MessengerComponent $component): void 37 | { 38 | // Nothing happens by default. 39 | } 40 | 41 | public abstract function operation(): string; 42 | } -------------------------------------------------------------------------------- /src/Decorator/Character.php: -------------------------------------------------------------------------------- 1 | name; 22 | } 23 | 24 | public function getTotalStrength(): int 25 | { 26 | return $this->totalStrength; 27 | } 28 | 29 | public function getTotalIntelligence(): int 30 | { 31 | return $this->totalIntelligence; 32 | } 33 | 34 | public function getTotalAgility(): int 35 | { 36 | return $this->totalAgility; 37 | } 38 | 39 | 40 | public function getTotalDexterity(): int 41 | { 42 | return $this->totalDexterity; 43 | } 44 | 45 | public function getTotalLuck(): int 46 | { 47 | return $this->totalLuck; 48 | } 49 | } -------------------------------------------------------------------------------- /src/Decorator/CharacterDecorator.php: -------------------------------------------------------------------------------- 1 | character = $character; 12 | } 13 | 14 | public function getName(): string 15 | { 16 | return $this->character->getName(); 17 | } 18 | 19 | public function getTotalStrength(): int 20 | { 21 | return $this->character->getTotalStrength() + $this->totalStrength; 22 | } 23 | 24 | public function getTotalIntelligence(): int 25 | { 26 | return $this->character->getTotalIntelligence() + $this->totalIntelligence; 27 | } 28 | public function getTotalAgility(): int 29 | { 30 | return $this->character->getTotalAgility() + $this->totalAgility; 31 | } 32 | public function getTotalDexterity(): int 33 | { 34 | return $this->character->getTotalDexterity() + $this->totalDexterity; 35 | } 36 | 37 | public function getTotalLuck(): int 38 | { 39 | return $this->character->getTotalLuck() + $this->totalLuck; 40 | } 41 | } -------------------------------------------------------------------------------- /src/Decorator/Classes/Archer.php: -------------------------------------------------------------------------------- 1 | name = 'Archer'; 12 | $this->totalStrength = 10; 13 | $this->totalAgility = 27; 14 | $this->totalDexterity = 20; 15 | $this->totalIntelligence = 15; 16 | $this->totalLuck = 15; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Decorator/Classes/Knight.php: -------------------------------------------------------------------------------- 1 | name = 'Knight'; 12 | $this->totalStrength = 26; 13 | $this->totalAgility = 8; 14 | $this->totalDexterity = 10; 15 | $this->totalIntelligence = 10; 16 | $this->totalLuck = 15; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Decorator/Classes/Wizard.php: -------------------------------------------------------------------------------- 1 | name = 'Wizard'; 12 | $this->totalStrength = 8; 13 | $this->totalAgility = 15; 14 | $this->totalDexterity = 16; 15 | $this->totalIntelligence = 20; 16 | $this->totalLuck = 25; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Decorator/Items/GoblinRing.php: -------------------------------------------------------------------------------- 1 | totalLuck = 8; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Decorator/Items/HermesBoots.php: -------------------------------------------------------------------------------- 1 | totalDexterity = 40; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Decorator/Items/LighteningBoltOfZeus.php: -------------------------------------------------------------------------------- 1 | totalDexterity = 36; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Decorator/Items/Mjolnir.php: -------------------------------------------------------------------------------- 1 | totalStrength = 40; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Decorator/Items/SwordOfTheDestiny.php: -------------------------------------------------------------------------------- 1 | totalStrength = 16; 15 | $this->totalAgility = -5; 16 | } 17 | } -------------------------------------------------------------------------------- /src/Decorator/Items/TridentOfPoseidon.php: -------------------------------------------------------------------------------- 1 | totalIntelligence = 20; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Facade/Component/BarCodeUtil.php: -------------------------------------------------------------------------------- 1 | getName() . " updated on the ERP system.\n"; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Facade/Component/MarketplaceIntegration.php: -------------------------------------------------------------------------------- 1 | getName() . " created on the Marketplace.\n"; 16 | } 17 | 18 | public function insertPrice(string $productUuid, float $price): void 19 | { 20 | // It could call an external service here to insert price into the marketplace. 21 | echo $productUuid . " price inserted on the Marketplace.\n"; 22 | 23 | } 24 | 25 | public function insertStock(string $productUuid, int $stock = 0): void 26 | { 27 | // It could call an external service here to insert the amount of products in stock. 28 | echo $productUuid . " stock amount inserted on the Marketplace.\n"; 29 | } 30 | } -------------------------------------------------------------------------------- /src/Facade/Component/Product.php: -------------------------------------------------------------------------------- 1 | isOnMarketplace; 25 | } 26 | 27 | /** 28 | * @param bool $isOnMarketplace 29 | * @return Product 30 | */ 31 | public function setIsOnMarketplace(bool $isOnMarketplace): Product 32 | { 33 | $this->isOnMarketplace = $isOnMarketplace; 34 | return $this; 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getUuid(): string 41 | { 42 | return $this->uuid; 43 | } 44 | 45 | /** 46 | * @param string $uuid 47 | * @return Product 48 | */ 49 | public function setUuid(string $uuid): Product 50 | { 51 | $this->uuid = $uuid; 52 | return $this; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getName(): string 59 | { 60 | return $this->name; 61 | } 62 | 63 | /** 64 | * @param string $name 65 | * @return Product 66 | */ 67 | public function setName(string $name): Product 68 | { 69 | $this->name = $name; 70 | return $this; 71 | } 72 | 73 | /** 74 | * @return int 75 | */ 76 | public function getBarCode(): int 77 | { 78 | return $this->barCode; 79 | } 80 | 81 | /** 82 | * @param int $barCode 83 | * @return Product 84 | */ 85 | public function setBarCode(int $barCode): Product 86 | { 87 | $this->barCode = $barCode; 88 | return $this; 89 | } 90 | 91 | /** 92 | * @return int 93 | */ 94 | public function getStock(): int 95 | { 96 | return $this->stock; 97 | } 98 | 99 | /** 100 | * @param int $stock 101 | * @return Product 102 | */ 103 | public function setStock(int $stock): Product 104 | { 105 | $this->stock = $stock; 106 | return $this; 107 | } 108 | 109 | /** 110 | * @return float 111 | */ 112 | public function getPrice(): float 113 | { 114 | return $this->price; 115 | } 116 | 117 | /** 118 | * @param float $price 119 | * @return Product 120 | */ 121 | public function setPrice(float $price): Product 122 | { 123 | $this->price = $price; 124 | return $this; 125 | } 126 | } -------------------------------------------------------------------------------- /src/Facade/Component/ProductRepository.php: -------------------------------------------------------------------------------- 1 | getName() . " persisted.\n"; 17 | } 18 | 19 | public function update(Product $product): void 20 | { 21 | // It could update into the database here. 22 | echo $product->getName() . " updated.\n"; 23 | } 24 | } -------------------------------------------------------------------------------- /src/Facade/ProductFlowFacade.php: -------------------------------------------------------------------------------- 1 | productRepository = $productRepository; 29 | $this->marketplaceIntegration = $marketplaceIntegration; 30 | $this->integrationSoapErp = $integrationSoapErp; 31 | } 32 | 33 | public function createProducts(array $products): void 34 | { 35 | foreach ($products as $product) { 36 | $product = (new Product) 37 | ->setBarCode(BarCodeUtil::generateBarCode()) 38 | ->setName($product['name']) 39 | ->setPrice($product['price']) 40 | ->setStock($product['stock']) 41 | ->setUuid('3786ebae-4816-46a8-88ac-7aa5b530524f'); 42 | 43 | $this->productRepository->persist($product); 44 | 45 | $this->marketplaceIntegration->createProduct($product); 46 | 47 | $this->marketplaceIntegration->insertPrice($product->getUuid(), $product->getPrice()); 48 | 49 | $this->marketplaceIntegration->insertStock($product->getUuid(), $product->getStock()); 50 | 51 | $product->setIsOnMarketplace(true); 52 | 53 | $this->productRepository->update($product); 54 | 55 | $this->integrationSoapErp->setProductOnMarketplace($product, true); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/FactoryMethod/CalculatorFactory.php: -------------------------------------------------------------------------------- 1 | getCharacteristicKey($brand, $model, $colorEnum); 16 | if (!isset($this->characteristics[$characteristicHash])) { 17 | $this->characteristics[$characteristicHash] = new Characteristic($brand, $model, $colorEnum); 18 | } 19 | 20 | return $this->characteristics[$characteristicHash]; 21 | } 22 | 23 | public function getCharacteristicKey(string $brand, string $model, ColorEnum $colorEnum) 24 | { 25 | return base64_decode($brand . $model . $colorEnum->name); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Flyweight/Component/Car.php: -------------------------------------------------------------------------------- 1 | serialNumber = $serialNumber; 18 | $this->characteristic = $characteristic; 19 | } 20 | 21 | /** 22 | * @return string 23 | */ 24 | public function getSerialNumber(): string 25 | { 26 | return $this->serialNumber; 27 | } 28 | 29 | /** 30 | * @param string $serialNumber 31 | * @return Car 32 | */ 33 | public function setSerialNumber(string $serialNumber): Car 34 | { 35 | $this->serialNumber = $serialNumber; 36 | return $this; 37 | } 38 | 39 | /** 40 | * @return Characteristic 41 | */ 42 | public function getCharacteristic(): Characteristic 43 | { 44 | return $this->characteristic; 45 | } 46 | 47 | /** 48 | * @param Characteristic $characteristic 49 | * @return Car 50 | */ 51 | public function setCharacteristic(Characteristic $characteristic): Car 52 | { 53 | $this->characteristic = $characteristic; 54 | return $this; 55 | } 56 | } -------------------------------------------------------------------------------- /src/Flyweight/Component/Characteristic.php: -------------------------------------------------------------------------------- 1 | brand = $brand; 21 | $this->model = $model; 22 | $this->color = $color; 23 | } 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function getBrand(): string 29 | { 30 | return $this->brand; 31 | } 32 | 33 | /** 34 | * @param string $brand 35 | * @return Characteristic 36 | */ 37 | public function setBrand(string $brand): Characteristic 38 | { 39 | $this->brand = $brand; 40 | return $this; 41 | } 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function getModel(): string 47 | { 48 | return $this->model; 49 | } 50 | 51 | /** 52 | * @param string $model 53 | * @return Characteristic 54 | */ 55 | public function setModel(string $model): Characteristic 56 | { 57 | $this->model = $model; 58 | return $this; 59 | } 60 | 61 | /** 62 | * @return ColorEnum 63 | */ 64 | public function getColor(): ColorEnum 65 | { 66 | return $this->color; 67 | } 68 | 69 | /** 70 | * @param ColorEnum $color 71 | * @return Characteristic 72 | */ 73 | public function setColor(ColorEnum $color): Characteristic 74 | { 75 | $this->color = $color; 76 | return $this; 77 | } 78 | } -------------------------------------------------------------------------------- /src/Flyweight/Component/ColorEnum.php: -------------------------------------------------------------------------------- 1 | blogPostRepository = $blogPostRepository; 12 | } 13 | 14 | public function interpret(string $context): array { 15 | return $this->blogPostRepository->findByAuthor($context); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Interpreter/Component/BlogPost.php: -------------------------------------------------------------------------------- 1 | title = $title; 16 | $this->author = $author; 17 | $this->date = $date; 18 | } 19 | 20 | public function __toString() { 21 | return "Title: {$this->title}, Author: {$this->author}, Date: {$this->date->format('Y-m-d')}"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Interpreter/Component/BlogPostRepository.php: -------------------------------------------------------------------------------- 1 | blogPosts = [ 14 | new BlogPost('My Blog Post', 'John Doe', new \DateTime('2023-07-01')), 15 | new BlogPost('Another Blog Post', 'Jane Doe', new \DateTime('2023-07-02')), 16 | new BlogPost('Yet Another Blog Post', 'John Doe', new \DateTime('2023-07-03')), 17 | ]; 18 | } 19 | 20 | public function findByTitle(string $title): array { 21 | return array_filter($this->blogPosts, function(BlogPost $blogPost) use ($title) { 22 | return $blogPost->title === $title; 23 | }); 24 | } 25 | 26 | public function findByAuthor(string $author): array { 27 | return array_filter($this->blogPosts, function(BlogPost $blogPost) use ($author) { 28 | return $blogPost->author === $author; 29 | }); 30 | } 31 | 32 | public function findByDate(\DateTimeInterface $date): array { 33 | return array_filter($this->blogPosts, function(BlogPost $blogPost) use ($date) { 34 | return $blogPost->date->format('Y-m-d') === $date->format('Y-m-d'); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Interpreter/DateSearch.php: -------------------------------------------------------------------------------- 1 | blogPostRepository = $blogPostRepository; 14 | } 15 | 16 | public function interpret(string $context): array 17 | { 18 | // Convert the string to a DateTime object 19 | $date = \DateTime::createFromFormat('Y-m-d', $context); 20 | 21 | return $this->blogPostRepository->findByDate($date); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Interpreter/Expression.php: -------------------------------------------------------------------------------- 1 | blogPostRepository = $blogPostRepository; 14 | } 15 | 16 | /** 17 | * @throws \Exception 18 | */ 19 | public function interpret(string $query): array 20 | { 21 | // The query format is "TYPE:CONTEXT" 22 | $elements = explode(':', $query, 2); 23 | $type = $elements[0]; 24 | $context = $elements[1] ?? ''; 25 | 26 | switch ($type) { 27 | case 'title': 28 | $expression = new TitleSearch($this->blogPostRepository); 29 | break; 30 | case 'author': 31 | $expression = new AuthorSearch($this->blogPostRepository); 32 | break; 33 | case 'date': 34 | $expression = new DateSearch($this->blogPostRepository); 35 | break; 36 | default: 37 | throw new \Exception("Invalid search type: $type"); 38 | } 39 | 40 | return $expression->interpret($context); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Interpreter/TitleSearch.php: -------------------------------------------------------------------------------- 1 | blogPostRepository = $blogPostRepository; 14 | } 15 | 16 | public function interpret(string $context): array 17 | { 18 | return $this->blogPostRepository->findByTitle($context); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Iterator/UserCollectionIterator.php: -------------------------------------------------------------------------------- 1 | collection = $collection; 16 | $this->reverse = $reverse; 17 | } 18 | 19 | public function rewind(): void 20 | { 21 | $this->position = $this->reverse 22 | ? ($this->collection->count() - 1) 23 | : 0; 24 | } 25 | 26 | public function current(): string 27 | { 28 | return $this->collection->getUsers()[$this->position]; 29 | } 30 | 31 | public function key(): int 32 | { 33 | return $this->position; 34 | } 35 | 36 | public function next(): void 37 | { 38 | $this->position = $this->position + ($this->reverse ? -1 : 1); 39 | } 40 | 41 | public function valid(): bool 42 | { 43 | return isset($this->collection->getUsers()[$this->position]); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Iterator/UserColletion.php: -------------------------------------------------------------------------------- 1 | users; 16 | } 17 | 18 | public function addUser(string $user): self 19 | { 20 | $this->users[] = $user; 21 | return $this; 22 | } 23 | 24 | public function getIterator(): \Iterator 25 | { 26 | return new UserCollectionIterator($this); 27 | } 28 | 29 | public function getReverseIterator(): \Iterator 30 | { 31 | return new UserCollectionIterator($this, true); 32 | } 33 | 34 | public function count(): int 35 | { 36 | return sizeof($this->users); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Mediator/ChatRoom.php: -------------------------------------------------------------------------------- 1 | getName(); 11 | 12 | echo $time . '[' . $sender . ']:' . $message; 13 | } 14 | } -------------------------------------------------------------------------------- /src/Mediator/ChatRoomMediator.php: -------------------------------------------------------------------------------- 1 | name = $name; 13 | $this->chatMediator = $chatMediator; 14 | } 15 | 16 | public function getName(): string 17 | { 18 | return $this->name; 19 | } 20 | 21 | public function send(string $message): void 22 | { 23 | $this->chatMediator->showMessage($this, $message); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Memento/Caretaker.php: -------------------------------------------------------------------------------- 1 | textEditor = $textEditor; 15 | } 16 | 17 | public function getLastSnapshotDate(): ?\DateTime 18 | { 19 | if (empty($this->textSnapshots)) { 20 | return null; 21 | } 22 | 23 | return $this->textSnapshots[array_key_last($this->textSnapshots)]->getUpdatedAt(); 24 | } 25 | 26 | public function backup(): void 27 | { 28 | $this->textSnapshots[] = $this->textEditor->createSnapshot(); 29 | } 30 | 31 | public function undo(): void 32 | { 33 | if (sizeof($this->textSnapshots) === 0) { 34 | return; 35 | } 36 | 37 | $restoredTextEditor = array_pop($this->textSnapshots); 38 | $this->textEditor->restore($restoredTextEditor); 39 | } 40 | } -------------------------------------------------------------------------------- /src/Memento/TextEditor.php: -------------------------------------------------------------------------------- 1 | text); 12 | } 13 | 14 | public function getText(): string 15 | { 16 | return $this->text; 17 | } 18 | 19 | public function setText(string $text): TextEditor 20 | { 21 | $this->text = $text; 22 | 23 | return $this; 24 | } 25 | 26 | public function restore(TextSnapshot $textSnapshot) 27 | { 28 | $this->text = $textSnapshot->getText(); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Memento/TextSnapshot.php: -------------------------------------------------------------------------------- 1 | text = $text; 14 | $this->updatedAt = new \DateTime(); 15 | } 16 | 17 | public function getText(): string 18 | { 19 | return $this->text; 20 | } 21 | 22 | public function getUpdatedAt(): \DateTime 23 | { 24 | return $this->updatedAt; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Memento/TextSnapshotMemento.php: -------------------------------------------------------------------------------- 1 | name = $name; 16 | $this->symbol = $symbol; 17 | } 18 | 19 | public static function USD(): Currency 20 | { 21 | return new self('American Dollar', 'USD'); 22 | } 23 | 24 | public static function EUR(): Currency 25 | { 26 | return new self('Euro', 'EUR'); 27 | } 28 | 29 | public function getName(): string 30 | { 31 | return $this->name; 32 | } 33 | 34 | public function getSymbol(): string 35 | { 36 | return $this->symbol; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Money/Money.php: -------------------------------------------------------------------------------- 1 | amount = $amount; 16 | $this->currency = $currency; 17 | } 18 | 19 | public function __toString(): string 20 | { 21 | return (string) $this->amount; 22 | } 23 | 24 | public function getAmount(): int 25 | { 26 | return $this->amount; 27 | } 28 | 29 | public function setAmount(int $amount): Money 30 | { 31 | $this->amount = $amount; 32 | return $this; 33 | } 34 | 35 | public function getCurrency(): Currency 36 | { 37 | return $this->currency; 38 | } 39 | 40 | public function setCurrency(Currency $currency): Money 41 | { 42 | $this->currency = $currency; 43 | return $this; 44 | } 45 | 46 | public function isEqualTo(Money $money): bool 47 | { 48 | return ($this->amount === $money->getAmount() 49 | && $this->currency->getSymbol() === $money->currency->getSymbol()); 50 | } 51 | 52 | /** 53 | * TODO: The currency should be considered as well. 54 | */ 55 | public function isGreaterThan(Money $money): bool 56 | { 57 | return $this->amount > $money->getAmount(); 58 | } 59 | 60 | /** 61 | * TODO: The currency should be considered as well. 62 | */ 63 | public function isLowerThan(Money $money): bool 64 | { 65 | return $this->amount < $money->getAmount(); 66 | } 67 | } -------------------------------------------------------------------------------- /src/NullObject/BaseEvent.php: -------------------------------------------------------------------------------- 1 | eventDispatcher = $eventDispatcher; 19 | } 20 | 21 | public abstract function dispatch(): void; 22 | } -------------------------------------------------------------------------------- /src/NullObject/Component/EventDispatcher.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch($serializedEvent); 14 | } 15 | } -------------------------------------------------------------------------------- /src/ObjectPool/DatabaseConnection.php: -------------------------------------------------------------------------------- 1 | availableConnections) === 0) { 13 | $connection = DatabaseConnection::getInstance(); 14 | $this->inUseConnections[] = $connection; 15 | return $connection; 16 | } else { 17 | $connection = array_pop($this->availableConnections); 18 | $this->inUseConnections[] = $connection; 19 | return $connection; 20 | } 21 | } 22 | 23 | public function release(DatabaseConnection $connection): void 24 | { 25 | $index = array_search($connection, $this->inUseConnections, true); 26 | if ($index !== false) { 27 | unset($this->inUseConnections[$index]); 28 | $this->availableConnections[] = $connection; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Observer/Component/Invoice.php: -------------------------------------------------------------------------------- 1 | invoiceNumber = $invoiceNumber; 18 | $this->invoiceAmount = $invoiceAmount; 19 | $this->state = $state; 20 | $this->customerEmailAddress = $customerEmailAddress; 21 | } 22 | 23 | public function getInvoiceNumber(): int 24 | { 25 | return $this->invoiceNumber; 26 | } 27 | 28 | public function getInvoiceAmount(): float 29 | { 30 | return $this->invoiceAmount; 31 | } 32 | 33 | public function getState(): string 34 | { 35 | return $this->state; 36 | } 37 | 38 | public function getCustomerEmailAddress(): string 39 | { 40 | return $this->customerEmailAddress; 41 | } 42 | 43 | public function setInvoiceNumber(int $invoiceNumber): Invoice 44 | { 45 | $this->invoiceNumber = $invoiceNumber; 46 | return $this; 47 | } 48 | 49 | public function setInvoiceAmount(float $invoiceAmount): Invoice 50 | { 51 | $this->invoiceAmount = $invoiceAmount; 52 | return $this; 53 | } 54 | 55 | public function setState(string $state): Invoice 56 | { 57 | $this->state = $state; 58 | return $this; 59 | } 60 | 61 | public function setCustomerEmailAddress(string $customerEmailAddress): Invoice 62 | { 63 | $this->customerEmailAddress = $customerEmailAddress; 64 | return $this; 65 | } 66 | } -------------------------------------------------------------------------------- /src/Observer/Component/InvoiceEventType.php: -------------------------------------------------------------------------------- 1 | getCustomerEmailAddress(), 14 | $invoice->getInvoiceNumber(), 15 | $invoice->getState() 16 | ); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Observer/EventListener.php: -------------------------------------------------------------------------------- 1 | isListenerSubscribed($eventType, $eventListener)) { 15 | return; 16 | } 17 | 18 | echo sprintf("Listener `%s` subscribed to `%s`.\n", get_class($eventListener), $eventType->name); 19 | 20 | $this->listeners[$eventType->name][] = $eventListener; 21 | } 22 | 23 | public function unsubscribe(InvoiceEventType $eventType, EventListener $eventListener): void 24 | { 25 | if (empty($this->listeners[$eventType->name])) { 26 | return; 27 | } 28 | 29 | foreach ($this->listeners[$eventType->name] as $index => $listener) { 30 | if ($listener instanceof $eventListener) { 31 | unset($this->listeners[$eventType->name][$index]); 32 | return; 33 | } 34 | } 35 | } 36 | 37 | public function notifySubscribers(InvoiceEventType $eventType, Invoice $invoice, array $extraData = []): void 38 | { 39 | if (empty($this->listeners[$eventType->name])) { 40 | return; 41 | } 42 | 43 | /** @var EventListener $listener */ 44 | foreach ($this->listeners[$eventType->name] as $listener) { 45 | $listener->notified($invoice, $extraData); 46 | } 47 | } 48 | 49 | /** 50 | * This function was created just to check if the subscriber is already registered to the listener. 51 | * In another languages and/or if you are using a different way to store the listeners/subscribers, probably you do not even need it. 52 | */ 53 | private function isListenerSubscribed(InvoiceEventType $eventType, EventListener $eventListener): bool 54 | { 55 | if (empty($this->listeners[$eventType->name])) { 56 | return false; 57 | } 58 | 59 | foreach ($this->listeners[$eventType->name] as $listener) { 60 | if ($listener instanceof $eventListener) { 61 | return true; 62 | } 63 | } 64 | 65 | return false; 66 | } 67 | } -------------------------------------------------------------------------------- /src/Observer/PushNotificationListener.php: -------------------------------------------------------------------------------- 1 | getInvoiceNumber(), 14 | $invoice->getState() 15 | ); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Prototype/BookA.php: -------------------------------------------------------------------------------- 1 | setSubject('Book A'); 10 | } 11 | 12 | public function __clone() 13 | { 14 | echo 'Book A cloned';ln(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Prototype/BookB.php: -------------------------------------------------------------------------------- 1 | setSubject('Book B'); 10 | } 11 | 12 | public function __clone() 13 | { 14 | echo 'Book B cloned';ln(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Prototype/BookPrototype.php: -------------------------------------------------------------------------------- 1 | title; 18 | } 19 | 20 | public function setTitle(string $title): BookPrototype 21 | { 22 | $this->title = $title; 23 | return $this; 24 | } 25 | 26 | public function getSubject(): string 27 | { 28 | return $this->subject; 29 | } 30 | 31 | public function setSubject(string $subject): BookPrototype 32 | { 33 | $this->subject = $subject; 34 | return $this; 35 | } 36 | 37 | public function getAuthorName(): string 38 | { 39 | return $this->authorName; 40 | } 41 | 42 | public function setAuthorName(string $authorName): BookPrototype 43 | { 44 | $this->authorName = $authorName; 45 | return $this; 46 | } 47 | } -------------------------------------------------------------------------------- /src/Proxy/Component/Address.php: -------------------------------------------------------------------------------- 1 | street; 25 | } 26 | 27 | /** 28 | * @param string $street 29 | * @return Address 30 | */ 31 | public function setStreet(string $street): Address 32 | { 33 | $this->street = $street; 34 | return $this; 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getNumber(): string 41 | { 42 | return $this->number; 43 | } 44 | 45 | /** 46 | * @param string $number 47 | * @return Address 48 | */ 49 | public function setNumber(string $number): Address 50 | { 51 | $this->number = $number; 52 | return $this; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getDistrict(): string 59 | { 60 | return $this->district; 61 | } 62 | 63 | /** 64 | * @param string $district 65 | * @return Address 66 | */ 67 | public function setDistrict(string $district): Address 68 | { 69 | $this->district = $district; 70 | return $this; 71 | } 72 | 73 | /** 74 | * @return string 75 | */ 76 | public function getCounty(): string 77 | { 78 | return $this->county; 79 | } 80 | 81 | /** 82 | * @param string $county 83 | * @return Address 84 | */ 85 | public function setCounty(string $county): Address 86 | { 87 | $this->county = $county; 88 | return $this; 89 | } 90 | 91 | /** 92 | * @return string 93 | */ 94 | public function getCountry(): string 95 | { 96 | return $this->country; 97 | } 98 | 99 | /** 100 | * @param string $country 101 | * @return Address 102 | */ 103 | public function setCountry(string $country): Address 104 | { 105 | $this->country = $country; 106 | return $this; 107 | } 108 | 109 | /** 110 | * @return string 111 | */ 112 | public function getPostcode(): string 113 | { 114 | return $this->postcode; 115 | } 116 | 117 | /** 118 | * @param string $postcode 119 | * @return Address 120 | */ 121 | public function setPostcode(string $postcode): Address 122 | { 123 | $this->postcode = $postcode; 124 | return $this; 125 | } 126 | } -------------------------------------------------------------------------------- /src/Proxy/GoogleMapsIntegration.php: -------------------------------------------------------------------------------- 1 | 'Charlottenstrasse', 15 | 'number' => 1234, 16 | 'postcode' => 12345, 17 | 'district' => 'Mitte', 18 | 'county' => 'Berlin', 19 | 'country' => 'Germany' 20 | ]; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Proxy/GoogleMapsIntegrationProxy.php: -------------------------------------------------------------------------------- 1 | originalGoogleMapsIntegration = $originalGoogleMapsIntegration; 14 | } 15 | 16 | public function getAddressByCoordinates(float $latitude, float $longitude) 17 | { 18 | $response = $this->originalGoogleMapsIntegration->getAddressByCoordinates($latitude, $longitude); 19 | 20 | return (new Address) 21 | ->setCountry($response['country']) 22 | ->setCounty($response['county']) 23 | ->setDistrict($response['district']) 24 | ->setNumber($response['number']) 25 | ->setPostcode($response['postcode']) 26 | ->setStreet($response['street']); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Proxy/GoogleMapsIntegrationSubject.php: -------------------------------------------------------------------------------- 1 | state = $state; 12 | $this->state->setContext($this); 13 | } 14 | 15 | public function transitionTo(State $state): void 16 | { 17 | echo "State transition to " . get_class($state) . ".\n"; 18 | $this->state = $state; 19 | $this->state->setContext($this); 20 | } 21 | 22 | public function doSomething1(): void 23 | { 24 | $this->state->handle1(); 25 | } 26 | 27 | public function doSomething2(): void 28 | { 29 | $this->state->handle2(); 30 | } 31 | 32 | public function cancelOrder(): void 33 | { 34 | $this->state->cancelOrder(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/State/State.php: -------------------------------------------------------------------------------- 1 | invoiceContext = $invoiceContext; 12 | } 13 | 14 | public function cancelOrder(): void 15 | { 16 | $this->invoiceContext->transitionTo(new StateInvoiceCanceled()); 17 | } 18 | 19 | public abstract function handle1(): void; 20 | 21 | public abstract function handle2(): void; 22 | } -------------------------------------------------------------------------------- /src/State/StateInvoiceApproved.php: -------------------------------------------------------------------------------- 1 | invoiceContext->transitionTo(new StateInvoiceShipped()); 12 | } 13 | 14 | public function handle2(): void 15 | { 16 | echo "Handle 2 of " . StateInvoiceApproved::class; 17 | ln(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/State/StateInvoiceCanceled.php: -------------------------------------------------------------------------------- 1 | invoiceContext->transitionTo(new StateInvoiceApproved()); 12 | } 13 | 14 | public function handle2(): void 15 | { 16 | echo "Handle 2 of " . StateInvoicePending::class; 17 | ln(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/State/StateInvoiceShipped.php: -------------------------------------------------------------------------------- 1 | invoiceContext->transitionTo(new StateInvoiceDelivered()); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Strategy/Component/Product.php: -------------------------------------------------------------------------------- 1 | category; 27 | } 28 | 29 | /** 30 | * @param string $category 31 | * @return Product 32 | */ 33 | public function setCategory(string $category): Product 34 | { 35 | $this->category = $category; 36 | return $this; 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getName(): string 43 | { 44 | return $this->name; 45 | } 46 | 47 | /** 48 | * @param string $name 49 | * @return Product 50 | */ 51 | public function setName(string $name): Product 52 | { 53 | $this->name = $name; 54 | return $this; 55 | } 56 | 57 | /** 58 | * @return float 59 | */ 60 | public function getPrice(): float 61 | { 62 | return $this->price; 63 | } 64 | 65 | /** 66 | * @param float $price 67 | * @return Product 68 | */ 69 | public function setPrice(float $price): Product 70 | { 71 | $this->price = $price; 72 | return $this; 73 | } 74 | 75 | /** 76 | * @return float 77 | */ 78 | public function getTaxes(): float 79 | { 80 | return $this->taxes; 81 | } 82 | 83 | /** 84 | * @param float $taxes 85 | * @return Product 86 | */ 87 | public function setTaxes(float $taxes): Product 88 | { 89 | $this->taxes = $taxes; 90 | return $this; 91 | } 92 | } -------------------------------------------------------------------------------- /src/Strategy/Context.php: -------------------------------------------------------------------------------- 1 | taxCalculatorStrategy = $taxCalculatorStrategy; 23 | } 24 | 25 | public function calculateProduct(Product $product): void 26 | { 27 | $taxes = $this->taxCalculatorStrategy->calculate($product); 28 | 29 | $product->setTaxes($taxes); 30 | } 31 | } -------------------------------------------------------------------------------- /src/Strategy/ElectronicTaxStrategy.php: -------------------------------------------------------------------------------- 1 | getPrice() * (self::TAX_RATE / 100); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Strategy/FoodTaxStrategy.php: -------------------------------------------------------------------------------- 1 | getPrice() * (self::TAX_RATE / 100); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Strategy/TaxCalculatorStrategy.php: -------------------------------------------------------------------------------- 1 | generateCertificate($studentDto); 23 | $this->sendCertificateByEmail($studentDto, $certificate); 24 | } 25 | 26 | protected abstract function generateCertificate(StudentDto $studentDto): string; 27 | 28 | protected abstract function sendCertificateByEmail(StudentDto $studentDto, string $certificateString): void; 29 | 30 | protected function notifyStudentBySms(int $phoneNumber, string $studentName) { 31 | // This is just a simulation. 32 | dump("SMS message sent to {$phoneNumber}: Hello {$studentName}, you certificate was generated."); 33 | } 34 | } -------------------------------------------------------------------------------- /src/TemplateMethod/Component/StudentDto.php: -------------------------------------------------------------------------------- 1 | name = $name; 28 | $this->courseName = $courseName; 29 | $this->completedAt = $completedAt; 30 | $this->email = $email; 31 | $this->phoneNumber = $phoneNumber; 32 | } 33 | 34 | /** 35 | * @return string 36 | */ 37 | public function getName(): string 38 | { 39 | return $this->name; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getCourseName(): string 46 | { 47 | return $this->courseName; 48 | } 49 | 50 | /** 51 | * @return \DateTimeImmutable 52 | */ 53 | public function getCompletedAt(): \DateTimeImmutable 54 | { 55 | return $this->completedAt; 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | public function getEmail(): string 62 | { 63 | return $this->email; 64 | } 65 | 66 | /** 67 | * @return int 68 | */ 69 | public function getPhoneNumber(): int 70 | { 71 | return $this->phoneNumber; 72 | } 73 | } -------------------------------------------------------------------------------- /src/TemplateMethod/DevCertificateGenerator.php: -------------------------------------------------------------------------------- 1 | getCompletedAt()->format('Y-m-d'); 18 | 19 | return "[DEVELOPMENT] Student {$studentDto->getName()} completed the course {$studentDto->getCourseName()} on {$completedAt}"; 20 | } 21 | 22 | protected function sendCertificateByEmail(StudentDto $studentDto, string $certificateString): void 23 | { 24 | dump("Email sent to {$studentDto->getEmail()} with certificate: {$certificateString}"); 25 | 26 | $this->notifyStudentBySms($studentDto->getPhoneNumber(), $studentDto->getName()); 27 | } 28 | } -------------------------------------------------------------------------------- /src/TemplateMethod/MarketingCertificateGenerator.php: -------------------------------------------------------------------------------- 1 | getCompletedAt()->format('Y-m-d'); 18 | 19 | return "[MARKETING] Student {$studentDto->getName()} completed the course {$studentDto->getCourseName()} on {$completedAt}"; 20 | } 21 | 22 | protected function sendCertificateByEmail(StudentDto $studentDto, string $certificateString): void 23 | { 24 | dump("Email sent to {$studentDto->getEmail()} with certificate: {$certificateString}"); 25 | 26 | $this->notifyStudentBySms($studentDto->getPhoneNumber(), $studentDto->getName()); 27 | } 28 | } -------------------------------------------------------------------------------- /src/TemplateMethod/PhotoCertificateGenerator.php: -------------------------------------------------------------------------------- 1 | getCompletedAt()->format('Y-m-d'); 18 | 19 | $this->notifyStudentBySms($studentDto->getPhoneNumber(), $studentDto->getName()); 20 | 21 | return "[PHOTOGRAPHY] Student {$studentDto->getName()} completed the course {$studentDto->getCourseName()} on {$completedAt}"; 22 | } 23 | 24 | protected function sendCertificateByEmail(StudentDto $studentDto, string $certificateString): void 25 | { 26 | dump("Email sent to {$studentDto->getEmail()} with certificate: {$certificateString}"); 27 | 28 | $this->notifyStudentBySms($studentDto->getPhoneNumber(), $studentDto->getName()); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Visitor/AdminUser.php: -------------------------------------------------------------------------------- 1 | visitAdmin($this); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Visitor/CanEditVisitor.php: -------------------------------------------------------------------------------- 1 | visitEditor($this); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Visitor/RoleVisitor.php: -------------------------------------------------------------------------------- 1 | visitViewer($this); 10 | } 11 | } --------------------------------------------------------------------------------