Created
November 19, 2024 01:48
-
-
Save SoftCreatR/5c599353cddd24b3f9c95b7e5a457426 to your computer and use it in GitHub Desktop.
This script serves as an exaggerated example of over-engineering a simple "Hello World" application in PHP. It incorporates numerous design patterns, interfaces, traits, and a rudimentary dependency injection container to demonstrate how complexity can be artificially introduced into a basic task.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Copyright by SoftCreatR.dev. | |
* | |
* License: https://softcreatr.dev/license-terms | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
* IN THE SOFTWARE. | |
* | |
* The above copyright notice and this disclaimer notice shall be included in all | |
* copies or substantial portions of the Software. | |
*/ | |
declare(strict_types=1); | |
/** | |
* Autoloader Registration | |
* | |
* Registers an autoload function to dynamically load classes based on their | |
* namespaces and directory structures. This autoloader searches within the | |
* 'src' and 'lib' directories for class files corresponding to the fully | |
* qualified class names. | |
* | |
* @return void | |
*/ | |
\spl_autoload_register(static function (string $class_name): void { | |
$paths = [ | |
__DIR__ . '/src/' . \str_replace('\\', '/', $class_name) . '.php', | |
__DIR__ . '/lib/' . \str_replace('\\', '/', $class_name) . '.php', | |
]; | |
/** | |
* Iterate through each path and include the class file if it exists. | |
*/ | |
foreach ($paths as $path) { | |
if (\file_exists($path)) { | |
include $path; | |
return; | |
} | |
} | |
}); | |
/** | |
* Interface MessageProviderInterface | |
* | |
* Defines the contract for any message provider within the application. | |
* Ensures that implementing classes can supply a message string. | |
*/ | |
interface MessageProviderInterface | |
{ | |
/** | |
* Retrieve the message to be processed and displayed. | |
* | |
* @return string The message content. | |
*/ | |
public function getMessage(): string; | |
} | |
/** | |
* Interface FormatterInterface | |
* | |
* Establishes a standard for message formatting within the application. | |
* Any formatter must implement this interface to ensure consistent | |
* message transformation. | |
*/ | |
interface FormatterInterface | |
{ | |
/** | |
* Format the provided message string according to specific rules. | |
* | |
* @param string $message The original message to format. | |
* @return string The formatted message. | |
*/ | |
public function format(string $message): string; | |
} | |
/** | |
* Interface LoggerInterface | |
* | |
* Outlines the essential logging functionalities required by the | |
* application. Implementing classes must provide mechanisms to log | |
* messages effectively. | |
*/ | |
interface LoggerInterface | |
{ | |
/** | |
* Log a message to a designated logging medium. | |
* | |
* @param string $message The message content to log. | |
* @return void | |
*/ | |
public function log(string $message): void; | |
} | |
/** | |
* Trait LoggerTrait | |
* | |
* Provides logging capabilities to classes that utilize this trait. | |
* Facilitates the injection and usage of a LoggerInterface implementation. | |
*/ | |
trait LoggerTrait | |
{ | |
/** | |
* @var LoggerInterface The logger instance used for logging messages. | |
*/ | |
protected LoggerInterface $logger; | |
/** | |
* Inject a logger instance into the using class. | |
* | |
* @param LoggerInterface $logger The logger implementation. | |
* @return void | |
*/ | |
public function setLogger(LoggerInterface $logger): void | |
{ | |
$this->logger = $logger; | |
} | |
/** | |
* Log a message using the injected logger instance. | |
* | |
* @param string $message The message to log. | |
* @return void | |
*/ | |
public function logMessage(string $message): void | |
{ | |
$this->logger->log($message); | |
} | |
} | |
/** | |
* Class SimpleLogger | |
* | |
* A straightforward implementation of the LoggerInterface that writes | |
* log messages to a file named 'app.log'. This logger appends each | |
* message with a newline character to ensure readability. | |
*/ | |
class SimpleLogger implements LoggerInterface | |
{ | |
/** | |
* Path to the log file where messages will be appended. | |
* | |
* @var string | |
*/ | |
private string $logFilePath; | |
/** | |
* Constructor | |
* | |
* Initializes the log file path upon instantiation. | |
*/ | |
public function __construct() | |
{ | |
$this->logFilePath = __DIR__ . '/app.log'; | |
} | |
/** | |
* Log a message by appending it to the log file. | |
* | |
* @param string $message The message content to log. | |
* @return void | |
*/ | |
public function log(string $message): void | |
{ | |
// Append the message to the log file with a newline. | |
\file_put_contents($this->logFilePath, $message . \PHP_EOL, \FILE_APPEND); | |
} | |
} | |
/** | |
* Abstract Class AbstractMessageProvider | |
* | |
* Serves as a foundational blueprint for message providers within the | |
* application. Enforces the implementation of message retrieval logic | |
* through the abstract method 'retrieveMessageParts'. | |
*/ | |
abstract class AbstractMessageProvider implements MessageProviderInterface | |
{ | |
/** | |
* Retrieve the constituent parts of the message. | |
* | |
* Implementing classes must define how the message parts are obtained. | |
* | |
* @return array An array of message segments. | |
*/ | |
abstract protected function retrieveMessageParts(): array; | |
/** | |
* Assemble the complete message from its parts. | |
* | |
* @return string The fully constructed message. | |
*/ | |
public function getMessage(): string | |
{ | |
$parts = $this->retrieveMessageParts(); | |
return \implode(' ', $parts); | |
} | |
} | |
/** | |
* Class HelloWorldProvider | |
* | |
* A concrete implementation of AbstractMessageProvider and FormatterInterface. | |
* Constructs the "Hello World" message by piecing together predefined segments | |
* and formatting the final output. Utilizes logging to record the formatting | |
* process. | |
*/ | |
class HelloWorldProvider extends AbstractMessageProvider implements FormatterInterface | |
{ | |
use LoggerTrait; | |
/** | |
* Retrieve the individual parts that constitute the "Hello World" message. | |
* | |
* @return array An array containing the greeting, subject, and punctuation. | |
*/ | |
protected function retrieveMessageParts(): array | |
{ | |
return [ | |
$this->getGreeting(), | |
$this->getSubject(), | |
$this->getPunctuation(), | |
]; | |
} | |
/** | |
* Get the greeting component of the message. | |
* | |
* @return string The greeting phrase. | |
*/ | |
private function getGreeting(): string | |
{ | |
return "Hello"; | |
} | |
/** | |
* Get the subject component of the message. | |
* | |
* @return string The subject noun. | |
*/ | |
private function getSubject(): string | |
{ | |
return "World"; | |
} | |
/** | |
* Get the punctuation component of the message. | |
* | |
* @return string The punctuation mark. | |
*/ | |
private function getPunctuation(): string | |
{ | |
return "!"; | |
} | |
/** | |
* Format the complete message by capitalizing words and appending exclamation marks. | |
* | |
* Logs the formatted message for auditing purposes. | |
* | |
* @param string $message The original message to format. | |
* @return string The formatted message. | |
*/ | |
public function format(string $message): string | |
{ | |
$formatted = \ucwords(\strtolower($message)); | |
$this->logMessage("Formatted message: {$formatted}"); | |
return $formatted; | |
} | |
} | |
/** | |
* Class Application | |
* | |
* Orchestrates the overall flow of the application by managing message | |
* providers, handling dependency injection, and executing the primary | |
* runtime operations. Implements the Singleton design pattern to ensure | |
* a single instance throughout the application's lifecycle. | |
*/ | |
class Application | |
{ | |
/** | |
* @var Application|null The singleton instance of the Application. | |
*/ | |
private static ?Application $instance = null; | |
/** | |
* @var MessageProviderInterface[] An array holding registered message providers. | |
*/ | |
private array $providers = []; | |
/** | |
* @var Container The dependency injection container managing dependencies. | |
*/ | |
private Container $container; | |
/** | |
* @var LoggerInterface The logger instance for logging application activities. | |
*/ | |
private LoggerInterface $logger; | |
/** | |
* Private Constructor | |
* | |
* Initializes the Application with a dependency injection container and a logger. | |
* | |
* @param Container $container The dependency injection container. | |
* @param LoggerInterface $logger The logger implementation. | |
*/ | |
private function __construct(Container $container, LoggerInterface $logger) | |
{ | |
$this->container = $container; | |
$this->logger = $logger; | |
} | |
/** | |
* Retrieve the singleton instance of the Application. | |
* | |
* If an instance does not exist, it is created using the provided container and logger. | |
* | |
* @param Container $container The dependency injection container. | |
* @param LoggerInterface $logger The logger implementation. | |
* @return self The singleton Application instance. | |
*/ | |
public static function getInstance(Container $container, LoggerInterface $logger): self | |
{ | |
if (self::$instance === null) { | |
self::$instance = new self($container, $logger); | |
} | |
return self::$instance; | |
} | |
/** | |
* Register a message provider with the application. | |
* | |
* Resolves the provider via the container, injects the logger if necessary, | |
* and appends it to the providers array. Logs the registration event. | |
* | |
* @param string $providerInterface The interface name of the provider to register. | |
* @return void | |
* | |
* @throws InvalidArgumentException If no binding is found for the provider. | |
*/ | |
public function registerProvider(string $providerInterface): void | |
{ | |
$provider = $this->container->make($providerInterface); | |
if (\in_array(LoggerTrait::class, \class_uses($provider), true)) { | |
$provider->setLogger($this->logger); | |
} | |
$this->providers[] = $provider; | |
$this->logger->log("Registered provider: " . \get_class($provider)); | |
} | |
/** | |
* Execute the main application flow. | |
* | |
* Iterates through each registered provider, formats messages if applicable, | |
* displays them, logs actions, and inspects providers via reflection. | |
* | |
* @return void | |
*/ | |
public function run(): void | |
{ | |
foreach ($this->providers as $provider) { | |
if ($provider instanceof FormatterInterface) { | |
$message = $provider->getMessage(); | |
$formattedMessage = $provider->format($message); | |
$this->display($formattedMessage); | |
$this->logger->log("Displayed formatted message: {$formattedMessage}"); | |
} else { | |
$message = $provider->getMessage(); | |
$this->display($message); | |
$this->logger->log("Displayed message: {$message}"); | |
} | |
$this->inspectProvider($provider); | |
} | |
} | |
/** | |
* Output the message to the standard output. | |
* | |
* @param string $message The message content to display. | |
* @return void | |
*/ | |
private function display(string $message): void | |
{ | |
echo $message; | |
} | |
/** | |
* Inspect the provider's class methods using reflection and log them. | |
* | |
* Provides introspection capabilities by examining the provider's | |
* methods and logging their names for auditing or debugging purposes. | |
* | |
* @param MessageProviderInterface $provider The provider to inspect. | |
* @return void | |
*/ | |
private function inspectProvider(MessageProviderInterface $provider): void | |
{ | |
$reflection = new ReflectionClass($provider); | |
$methods = $reflection->getMethods( | |
ReflectionMethod::IS_PRIVATE | ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PUBLIC | |
); | |
$methodNames = \array_map(static fn($method) => $method->getName(), $methods); | |
$this->logger->log("Provider " . \get_class($provider) . " has methods: " . \implode(', ', $methodNames)); | |
} | |
} | |
/** | |
* Class Container | |
* | |
* A rudimentary dependency injection container that manages the binding and | |
* resolution of class dependencies. Facilitates loose coupling and | |
* adherence to the Dependency Inversion Principle by abstracting | |
* instantiation logic. | |
*/ | |
class Container | |
{ | |
/** | |
* @var array<string, callable> An associative array mapping abstract types to factory callables. | |
*/ | |
private array $bindings = []; | |
/** | |
* Bind an abstract type to a concrete factory implementation. | |
* | |
* Registers a factory callable responsible for creating instances of the abstract type. | |
* | |
* @param string $abstract The abstract type or interface name. | |
* @param callable $factory The factory callable that returns an instance of the abstract type. | |
* @return void | |
*/ | |
public function bind(string $abstract, callable $factory): void | |
{ | |
$this->bindings[$abstract] = $factory; | |
} | |
/** | |
* Resolve an instance of the given abstract type. | |
* | |
* Invokes the bound factory callable to create and return an instance of the abstract type. | |
* | |
* @param string $abstract The abstract type or interface name to resolve. | |
* @return mixed An instance of the resolved type. | |
* | |
* @throws InvalidArgumentException If no binding is found for the abstract type. | |
*/ | |
public function make(string $abstract) | |
{ | |
if (isset($this->bindings[$abstract])) { | |
return $this->bindings[$abstract]($this); | |
} | |
throw new InvalidArgumentException("No binding found for {$abstract}"); | |
} | |
} | |
/** | |
* Dependency Injection Container Setup | |
* | |
* Configures the container by binding interfaces to their respective concrete | |
* implementations. This setup ensures that dependencies are resolved correctly | |
* during application runtime. | |
*/ | |
// Instantiate the container. | |
$container = new Container(); | |
// Bind LoggerInterface to SimpleLogger. | |
$container->bind(LoggerInterface::class, static function (Container $c): SimpleLogger { | |
return new SimpleLogger(); | |
}); | |
// Bind MessageProviderInterface to HelloWorldProvider. | |
$container->bind(MessageProviderInterface::class, static function (Container $c): HelloWorldProvider { | |
return new HelloWorldProvider(); | |
}); | |
// Bind FormatterInterface to the implementation resolved via MessageProviderInterface. | |
$container->bind(FormatterInterface::class, static function (Container $c): FormatterInterface { | |
return $c->make(MessageProviderInterface::class); | |
}); | |
/** | |
* Application Initialization and Execution | |
* | |
* Retrieves the necessary dependencies from the container, initializes the | |
* Application instance, registers the message provider, and executes the | |
* application flow. | |
*/ | |
// Retrieve the logger instance from the container. | |
$logger = $container->make(LoggerInterface::class); | |
// Obtain the singleton Application instance, injecting the container and logger. | |
$app = Application::getInstance($container, $logger); | |
// Register the message provider using its interface to leverage the container's binding. | |
$app->registerProvider(MessageProviderInterface::class); | |
// Execute the application, resulting in the display and logging of the "Hello World" message. | |
$app->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment