From f89f6ae77222546e3211b8d5ab37f6c94a1ddb4b Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 1 Jan 2026 17:17:48 -0500 Subject: [PATCH] gang of four --- docs/Gang_of_Four_Design_Patterns.pdf | Bin 0 -> 36067 bytes docs/Notes.txt | 3 + docs/OOP_Design_Patterns.html | 62 +++++++++++ docs/TODO.md | 8 +- src/bootstrap/errors.php | 2 +- src/bootstrap/main.php | 23 ++-- src/classes/common.php | 2 +- src/classes/services/Logger.php | 154 ++++++++++++++++++++++++++ src/classes/view.php | 8 +- 9 files changed, 248 insertions(+), 14 deletions(-) create mode 100644 docs/Gang_of_Four_Design_Patterns.pdf create mode 100644 docs/Notes.txt create mode 100644 docs/OOP_Design_Patterns.html create mode 100644 src/classes/services/Logger.php diff --git a/docs/Gang_of_Four_Design_Patterns.pdf b/docs/Gang_of_Four_Design_Patterns.pdf new file mode 100644 index 0000000000000000000000000000000000000000..117e2156600eef148db5efa57090c0ecd0b81ce8 GIT binary patch literal 36067 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 CVJJ!f literal 0 HcmV?d00001 diff --git a/docs/Notes.txt b/docs/Notes.txt new file mode 100644 index 0000000..41c83d4 --- /dev/null +++ b/docs/Notes.txt @@ -0,0 +1,3 @@ +About Data Transfer Objects, etc...: + +https://chatgpt.com/share/6956d98b-3d5c-8001-8139-a8598747b111 \ No newline at end of file diff --git a/docs/OOP_Design_Patterns.html b/docs/OOP_Design_Patterns.html new file mode 100644 index 0000000..6f61010 --- /dev/null +++ b/docs/OOP_Design_Patterns.html @@ -0,0 +1,62 @@ + + + + + + Gang of Four Design Patterns + + +

23 Classic Gang of Four Design Patterns

+ +

Generated by ChatGPT:

+ +

Creational Patterns (deal with object creation)

+
    +
  1. Singleton - Ensures a class has only one instance and provides a global point of access to it.
  2. +
  3. Factory Method - Defines an interface for creating objects but lets subclasses decide which class to instantiate.
  4. +
  5. Abstract Factory - Provides an interface for creating families of related objects without specifying their concrete classes.
  6. +
  7. Builder - Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
  8. +
  9. Prototype - Creates new objects by copying an existing object (prototype) rather than creating from scratch.
  10. +
+ +

Structural Patterns (deal with object composition)

+
    +
  1. Adapter - Converts the interface of one class into another interface that clients expect, enabling incompatible classes to work together.
  2. +
  3. Bridge - Decouples an abstraction from its implementation so they can vary independently, preventing a permanent binding between them.
  4. +
  5. Composite - Composes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.
  6. +
  7. Decorator - Attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending functionality.
  8. +
  9. Facade - Provides a unified, simplified interface to a complex subsystem, making it easier to use.
  10. +
  11. Flyweight - Minimizes memory usage by sharing as much data as possible with similar objects; used when large numbers of objects are needed.
  12. +
  13. Proxy - Provides a surrogate or placeholder for another object to control access to it, useful for lazy initialization, access control, or logging.
  14. +
+ +

Behavioral Patterns (deal with object interaction and responsibility)

+
    +
  1. Chain of Responsibility - Passes a request along a chain of handlers, where each handler decides either to process the request or pass it to the next handler.
  2. +
  3. Command - Encapsulates a request as an object, thereby allowing parameterization of clients with different requests and supporting undoable operations.
  4. +
  5. Interpreter - Defines a grammatical representation for a language and an interpreter to evaluate sentences in that language.
  6. +
  7. Iterator - Provides a way to access elements of an aggregate object sequentially without exposing its underlying representation.
  8. +
  9. Mediator - Defines an object that encapsulates how a set of objects interact, promoting loose coupling by preventing objects from referring to each other explicitly.
  10. +
  11. Memento - Captures and externalizes an object's internal state without violating encapsulation, so the object can be restored to this state later.
  12. +
  13. Observer - Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
  14. +
  15. State - Allows an object to alter its behavior when its internal state changes, appearing to change its class.
  16. +
  17. Strategy - Defines a family of algorithms, encapsulates each one, and makes them interchangeable, letting the algorithm vary independently from clients.
  18. +
  19. Template Method - Defines the skeleton of an algorithm in a method, deferring some steps to subclasses, letting subclasses redefine certain steps without changing the algorithm's structure.
  20. +
  21. Visitor - Represents an operation to be performed on elements of an object structure, letting you define new operations without changing the classes of the elements.
  22. +
+ +

These patterns provide proven solutions to common software design problems, promoting code reuse, flexibility, and maintainability in object-oriented systems.

+ +

Links to more external resources not mine...

+ +
+

I'm not affiliated with nor profit from these Books, they are just here for your and my convenience.

+

Modern PHP 8 Design Patterns Online Version: https://designpatternsphp.readthedocs.io/en/latest/

+

PDF Version: https://designpatternsphp.readthedocs.io/_/downloads/en/latest/pdf/

+

Nice Book for sale: Buy Dive Into DESIGN PATTERNS An Ebook on design patterns and the principles behind them: https://refactoring.guru/design-patterns/book

+

PHP Code from the Book above: https://refactoring.guru/design-patterns/php, If you liked it; Please support them by buying the book above...

+

"The Gang of 4 book" - Design Patterns: Elements of Reusable Object-Oriented Software By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Published Oct 31, 1994. Buy here it: https://www.informit.com/store/design-patterns-elements-of-reusable-object-oriented-9780201633610

+

More on The Addison-Wesley Professional Computing Series

+
+ + \ No newline at end of file diff --git a/docs/TODO.md b/docs/TODO.md index 43bde7d..4d1c170 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -34,7 +34,13 @@ - [ ] → Sane Config/Service file defaults - [ ] → Sane Folder Structure and Documentation - [x] → Default Routes, then load Controllers - +- [ ] → composer require psr/http-message +- [ ] → PSR Xx Compliant Standards... +- [ ] → class InvoiceException extends \Exception{ public static function missingBillingInfo(): static { return new static('Missing billing info'); } } +- [ ] → view __toString(): string{return $ this->render();} +- [ ] → view __get(string $name) {return $this->params[$name] ?? null;} +- [ ] → flash session messages +- [ ] → https://www.youtube.com/watch?v=iCKzIIE4w5E&list=PLr3d3QYzkw2xabQRUpcZ_IBk9W50M9pe-&index=66 ## Extras: - [x] → LazyCollections, LazyObjects, Money Class - [ ] → Tests diff --git a/src/bootstrap/errors.php b/src/bootstrap/errors.php index f5f653d..4b4737f 100644 --- a/src/bootstrap/errors.php +++ b/src/bootstrap/errors.php @@ -145,7 +145,7 @@ function formatMessage($message, $type = 'error') { } // Custom error handler -set_error_handler(function($errno, $errstr, $errfile, $errline) { +set_error_handler(function(int $errno, string $errstr, ?string $errfile, ?int $errline) { // Skip if error reporting is turned off if (!(error_reporting() & $errno)) { return false; diff --git a/src/bootstrap/main.php b/src/bootstrap/main.php index e115bc4..686e197 100644 --- a/src/bootstrap/main.php +++ b/src/bootstrap/main.php @@ -64,9 +64,10 @@ final class configure { private static $config = []; - protected function __construct() { - - } + private function __construct(){} + private function __clone(){} + private function __wakeup(){} + private function __destruct(){} public static function exists() { // an Alias to has return self::has(func_get_args()); @@ -195,9 +196,15 @@ final class di { ...$more ) { if ($this->has($service_name) ) { - return $this->services[$service_name]($args, $more); + $entry = $this->services[$service_name]; + + if (is_callable($entry)) { + return $entry($args, $more); + } + + $service_name = $entry; } - return $this->resolve($service_name); // Try to Auto-Wire + return $this->resolve(c); // Try to Auto-Wire } public function get_auto(string $service_name) { @@ -222,7 +229,7 @@ final class di { public function list_services_as_string(): string { return implode(',', array_keys($this->services)); } - + // Reflection API Autowiring FROM-> https://www.youtube.com/watch?v=78Vpg97rQwE&list=PLr3d3QYzkw2xabQRUpcZ_IBk9W50M9pe-&index=72 public function resolve(string $service_name) { try { $reflection_class = new \ReflectionClass($service_name); @@ -247,7 +254,7 @@ final class di { return new $service_name; } $dependencies = array_map( - function(\ReflectionParameter $param) { + function(\ReflectionParameter $param) use ($service_name) { $name = $param->getName(); $type = $param->getType(); if (! $type) { @@ -282,6 +289,8 @@ registry::get('loader')->add_namespace("Project", CodeHydrater_PROJECT); load_all::init(CodeHydrater_PROJECT); +//site_helper::load_file(CodeHydrater_FRAMEWORK . 'vendor/autoload.php'); + // Keep these blocks of code here not anywhere before it... if (! defined('ENVIRONMENT')) { if (is_live() === false) { diff --git a/src/classes/common.php b/src/classes/common.php index 1545fea..d9c2f10 100644 --- a/src/classes/common.php +++ b/src/classes/common.php @@ -150,7 +150,7 @@ final class common { return F::stripos($string, $needle, $offset); } - public static function string_sub_part(string $string, int $offset = 0, int $length = null, $encoding = null) { + public static function string_sub_part(string $string, int $offset = 0, ?int $length = null, $encoding = null) { if ($length === null) { return F::substr($string, $offset, strlen($string)); } else { diff --git a/src/classes/services/Logger.php b/src/classes/services/Logger.php new file mode 100644 index 0000000..8dbf961 --- /dev/null +++ b/src/classes/services/Logger.php @@ -0,0 +1,154 @@ + 1 && $this->getLines($file) > $maxCount) { + @unlink($file); + } + + if (file_put_contents($file, "\n", FILE_APPEND) === false) { + return; + } + + @chmod($file, 0660); + @chgrp($file, 'www-data'); + + if (!is_writable($file)) { + return; + } + + $this->handle = fopen($file, 'ab'); + } + + /* ---------------- PSR-3 Core ---------------- */ + + public function log($level, $message, array $context = []): void + { + if (!is_string($level)) { + throw new InvalidArgumentException('Log level must be a string'); + } + + if (!$this->handle || !is_resource($this->handle)) { + return; + } + + $message = $this->interpolate((string)$message, $context); + + $time = (new \DateTimeImmutable())->format('Y-m-d H:i:s'); + fwrite( + $this->handle, + sprintf("[%s] %s: %s\n", $time, strtoupper($level), $message) + ); + } + + /* ---------------- Level Methods ---------------- */ + + public function emergency($message, array $context = []): void + { + $this->log(LogLevel::EMERGENCY, $message, $context); + } + + public function alert($message, array $context = []): void + { + $this->log(LogLevel::ALERT, $message, $context); + } + + public function critical($message, array $context = []): void + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + public function error($message, array $context = []): void + { + $this->log(LogLevel::ERROR, $message, $context); + } + + public function warning($message, array $context = []): void + { + $this->log(LogLevel::WARNING, $message, $context); + } + + public function notice($message, array $context = []): void + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + public function info($message, array $context = []): void + { + $this->log(LogLevel::INFO, $message, $context); + } + + public function debug($message, array $context = []): void + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /* ---------------- Helpers ---------------- */ + + private function interpolate(string $message, array $context): string + { + $replace = []; + + foreach ($context as $key => $value) { + if (is_scalar($value) || $value instanceof \Stringable) { + $replace['{' . $key . '}'] = (string)$value; + } + } + + return strtr($message, $replace); + } + + private function getLines(string $file): int + { + if (!file_exists($file)) { + return 0; + } + + $lines = 0; + $fh = fopen($file, 'rb'); + + if (!$fh) { + return 0; + } + + while (!feof($fh)) { + $lines += substr_count(fread($fh, 8192), "\n"); + } + + fclose($fh); + return $lines; + } + + public function __destruct() + { + if ($this->handle && is_resource($this->handle)) { + fclose($this->handle); + } + } +} diff --git a/src/classes/view.php b/src/classes/view.php index 89ff29b..d43bc2a 100644 --- a/src/classes/view.php +++ b/src/classes/view.php @@ -82,7 +82,7 @@ final class view { * @param string $render_path * @throws Exception */ - public function set_view(string $view_file = null, ViewType $type = ViewType::PHP): void { + public function set_view(?string $view_file = null, ViewType $type = ViewType::PHP): void { if ($view_file === null) { return; } @@ -147,7 +147,7 @@ final class view { * @param mixed $name Name of the variable to set in the view, or an array of key/value pairs where each key is the variable and each value is the value to set. * @param mixed $value Value of the variable to set in the view. */ - public function set($name, $value = null): void { + public function set($name, mixed $value = null): void { if (is_array($name)) { foreach ($name as $var_name => $value) { $this->vars[$var_name] = $value; @@ -162,7 +162,7 @@ final class view { * @param type $local * @param string $file */ - public function render($local, string $file = null, ViewType $type = ViewType::PHP) { + public function render($local, ?string $file = null, ViewType $type = ViewType::PHP) { echo $this->fetch($local, $file, $type); } @@ -171,7 +171,7 @@ final class view { * @param type $local = $this * @param $file (optional view file) */ - public function fetch($local, string $file = null, ViewType $type = ViewType::PHP): string { + public function fetch($local, ?string $file = null, ViewType $type = ViewType::PHP): string { $page_output = ob_get_clean(); // Get echos before View bootstrap\views::ob_start(); $saved_ob_level = ob_get_level();