From 884ffd4e5df54213f806b839ccf16cbf5ef935fa Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 20 Oct 2021 23:54:14 -0400 Subject: [PATCH 01/56] Enums phase 1 --- Attributes/Param.php | 2 +- Database/Database.php | 12 ++-- Enums/CollectionSort.php | 10 +-- Enums/DatabaseType.php | 6 +- Enums/{DocTypes.php => DocType.php} | 18 ++--- Enums/LogLevelCode.php | 18 ++--- Enums/ParamType.php | 6 +- Enums/ResponseCode.php | 8 ++- Enums/ServiceContainer.php | 5 +- FeastTests/Collection/CollectionListTest.php | 4 +- .../Controllers/MaintenanceControllerTest.php | 2 +- FeastTests/Database/DatabaseFactoryTest.php | 2 +- FeastTests/Database/DatabaseTest.php | 14 ++-- .../Database/Table/TableFactoryTest.php | 4 +- FeastTests/Logger/LoggerTest.php | 2 +- FeastTests/Mocks/LoggerMock.php | 1 + FeastTests/ViewTest.php | 42 ++++++------ Interfaces/DatabaseInterface.php | 5 +- Interfaces/LoggerInterface.php | 1 + Logger/ErrorLogger.php | 2 +- Logger/Logger.php | 10 +-- Router/Router.php | 2 +- Traits/Collection.php | 8 +-- View.php | 66 +++++++++---------- composer.json | 2 +- composer.lock | 6 +- 26 files changed, 134 insertions(+), 124 deletions(-) rename Enums/{DocTypes.php => DocType.php} (61%) diff --git a/Attributes/Param.php b/Attributes/Param.php index 63b36dd..95223c4 100644 --- a/Attributes/Param.php +++ b/Attributes/Param.php @@ -31,7 +31,7 @@ public function __construct( public string $type = '', public string $name = '', public string $description = '', - public string $paramType = ParamType::PARAM + public ParamType $paramType = ParamType::PARAM ) { } diff --git a/Database/Database.php b/Database/Database.php index 24d02d6..f372dd1 100644 --- a/Database/Database.php +++ b/Database/Database.php @@ -36,7 +36,7 @@ class Database implements DatabaseInterface { private PDO $connection; - private string $databaseType; + private DatabaseType $databaseType; private string $queryClass; /** @@ -50,7 +50,11 @@ public function __construct(\stdClass $connectionDetails, string $pdoClass) { $username = (string)$connectionDetails->user; $password = (string)$connectionDetails->pass; - $this->databaseType = (string)$connectionDetails->connectionType; + /** + * @var DatabaseType + * @psalm-suppress UndefinedMethod + */ + $this->databaseType = DatabaseType::from($connectionDetails->connectionType); /** @psalm-suppress DeprecatedMethod (will be removed in 2.0) */ $this->queryClass = (string)($connectionDetails->queryClass ?? $this->getQueryClass()); $options = $this->getConfigOptions($connectionDetails); @@ -376,9 +380,9 @@ private function getConnectionString(string $database, string $hostname = 'local /** * Get Database type. * - * @return string + * @return DatabaseType */ - public function getDatabaseType(): string + public function getDatabaseType(): DatabaseType { return $this->databaseType; } diff --git a/Enums/CollectionSort.php b/Enums/CollectionSort.php index 6aeaca8..a3d4e10 100644 --- a/Enums/CollectionSort.php +++ b/Enums/CollectionSort.php @@ -20,11 +20,11 @@ namespace Feast\Enums; -class CollectionSort +enum CollectionSort: int { - public const KEY = 1; - public const KEY_REVERSE = 2; - public const VALUE = 3; - public const VALUE_REVERSE = 4; + case KEY = 1; + case KEY_REVERSE = 2; + case VALUE = 3; + case VALUE_REVERSE = 4; } diff --git a/Enums/DatabaseType.php b/Enums/DatabaseType.php index ae28ae8..3334131 100644 --- a/Enums/DatabaseType.php +++ b/Enums/DatabaseType.php @@ -20,9 +20,9 @@ namespace Feast\Enums; -class DatabaseType +enum DatabaseType: string { - public const MYSQL = 'mysql'; - public const SQLITE = 'sqlite'; + case MYSQL = 'mysql'; + case SQLITE = 'sqlite'; } diff --git a/Enums/DocTypes.php b/Enums/DocType.php similarity index 61% rename from Enums/DocTypes.php rename to Enums/DocType.php index 39bff99..5a02e78 100644 --- a/Enums/DocTypes.php +++ b/Enums/DocType.php @@ -20,15 +20,15 @@ namespace Feast\Enums; -class DocTypes +enum DocType: string { - public const HTML_4_01_FRAMESET = 'html401frame'; + case HTML_4_01_FRAMESET = 'html401frame'; - public const HTML_4_01_STRICT = 'html401strict'; - public const HTML_4_01_TRANSITIONAL = 'html401transitional'; - public const HTML_5 = 'html5'; - public const XHTML_1_0_FRAMESET = 'xhtml1frame'; - public const XHTML_1_0_STRICT = 'xhtml1strict'; - public const XHTML_1_0_TRANSITIONAL = 'xhtml1transitional'; - public const XHTML_1_1 = 'xhtml11'; + case HTML_4_01_STRICT = 'html401strict'; + case HTML_4_01_TRANSITIONAL = 'html401transitional'; + case HTML_5 = 'html5'; + case XHTML_1_0_FRAMESET = 'xhtml1frame'; + case XHTML_1_0_STRICT = 'xhtml1strict'; + case XHTML_1_0_TRANSITIONAL = 'xhtml1transitional'; + case XHTML_1_1 = 'xhtml11'; } diff --git a/Enums/LogLevelCode.php b/Enums/LogLevelCode.php index be3369a..898361a 100644 --- a/Enums/LogLevelCode.php +++ b/Enums/LogLevelCode.php @@ -20,14 +20,14 @@ namespace Feast\Enums; -class LogLevelCode +enum LogLevelCode: int { - public const DEBUG = 100; - public const INFO = 200; - public const NOTICE = 300; - public const WARNING = 400; - public const ERROR = 500; - public const CRITICAL = 600; - public const ALERT = 700; - public const EMERGENCY = 800; + case DEBUG = 100; + case INFO = 200; + case NOTICE = 300; + case WARNING = 400; + case ERROR = 500; + case CRITICAL = 600; + case ALERT = 700; + case EMERGENCY = 800; } diff --git a/Enums/ParamType.php b/Enums/ParamType.php index f1a72f3..14a0649 100644 --- a/Enums/ParamType.php +++ b/Enums/ParamType.php @@ -20,8 +20,8 @@ namespace Feast\Enums; -class ParamType +enum ParamType { - public const FLAG = 'flag'; - public const PARAM = 'param'; + case FLAG; + case PARAM; } diff --git a/Enums/ResponseCode.php b/Enums/ResponseCode.php index 3d80ed5..8df8cca 100644 --- a/Enums/ResponseCode.php +++ b/Enums/ResponseCode.php @@ -20,7 +20,8 @@ namespace Feast\Enums; -class ResponseCode { +class ResponseCode +{ public const HTTP_CODE_100 = 100; public const HTTP_CODE_101 = 101; public const HTTP_CODE_102 = 102; @@ -96,11 +97,12 @@ class ResponseCode { /** * Returns if response code is valid. - * + * * @param int $responseCode * @return bool */ - public static function isValidResponseCode(int $responseCode): bool { + public static function isValidResponseCode(int $responseCode): bool + { return defined('self::HTTP_CODE_' . (string)$responseCode); } } diff --git a/Enums/ServiceContainer.php b/Enums/ServiceContainer.php index e21535a..afb7519 100644 --- a/Enums/ServiceContainer.php +++ b/Enums/ServiceContainer.php @@ -20,8 +20,9 @@ namespace Feast\Enums; -class ServiceContainer { +enum ServiceContainer: string +{ // This flag should only be used when running unit tests and needing to add mock items. // Use at your own risk. Better yet, don't use it at all. Ever. Not even for fun. Nope. - public const CLEAR_CONTAINER = 'clear'; + case CLEAR_CONTAINER = 'clear'; } diff --git a/FeastTests/Collection/CollectionListTest.php b/FeastTests/Collection/CollectionListTest.php index 980c81c..c6f0ca3 100644 --- a/FeastTests/Collection/CollectionListTest.php +++ b/FeastTests/Collection/CollectionListTest.php @@ -142,7 +142,7 @@ public function testReverseSortValueAndPop(): void public function testSortInvalid(): void { $collection = new CollectionList('string', ['b' => 'early', 'a' => 'testing']); - $this->expectException(InvalidOptionException::class); + $this->expectException(\TypeError::class); $collection->sort(7, true); } @@ -237,7 +237,7 @@ public function testObjectSortInvalid(): void public function testObjectSortTypeInvalid(): void { $collection = new CollectionList(stdClass::class); - $this->expectException(InvalidOptionException::class); + $this->expectException(\TypeError::class); $collection->objectSort('test', 7); } diff --git a/FeastTests/Controllers/MaintenanceControllerTest.php b/FeastTests/Controllers/MaintenanceControllerTest.php index 6530e1c..e630823 100644 --- a/FeastTests/Controllers/MaintenanceControllerTest.php +++ b/FeastTests/Controllers/MaintenanceControllerTest.php @@ -22,7 +22,7 @@ use Feast\CliArguments; use Feast\Config\Config; use Feast\Controllers\MaintenanceController; -use Feast\Enums\DocTypes; +use Feast\Enums\DocType; use Feast\Interfaces\RouterInterface; use Feast\ServiceContainer\ServiceContainer; use Feast\View; diff --git a/FeastTests/Database/DatabaseFactoryTest.php b/FeastTests/Database/DatabaseFactoryTest.php index 1624995..066cefe 100644 --- a/FeastTests/Database/DatabaseFactoryTest.php +++ b/FeastTests/Database/DatabaseFactoryTest.php @@ -39,7 +39,7 @@ public function testGetConnection(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL; + $details->connectionType = DatabaseType::MYSQL->value; $config = $this->createStub(Config::class); $config->method('getSetting')->willReturnMap( [ diff --git a/FeastTests/Database/DatabaseTest.php b/FeastTests/Database/DatabaseTest.php index 9c2a017..b335d4c 100644 --- a/FeastTests/Database/DatabaseTest.php +++ b/FeastTests/Database/DatabaseTest.php @@ -35,7 +35,7 @@ class DatabaseTest extends TestCase { protected function getValidConnection( - ?string $connectionType = DatabaseType::MYSQL, + ?DatabaseType $connectionType = DatabaseType::MYSQL, ?string $queryClass = MySQLQuery::class, bool $options = false ): Database { @@ -45,7 +45,7 @@ protected function getValidConnection( $details->pass = 'test'; $details->name = 'Test'; $details->queryClass = $queryClass; - $details->connectionType = $connectionType; + $details->connectionType = $connectionType->value; if ($options) { $details->config = [ \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_OBJ, @@ -81,7 +81,7 @@ public function testInstantiationUnknownType(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = 'This Database Doesn\'t Exist'; - $this->expectException(DatabaseException::class); + $this->expectException(\ValueError::class); new Database($details, PDOMock::class); } @@ -93,7 +93,7 @@ public function testInstantiationWithUrl(): void $details->pass = 'test'; $details->name = 'Test'; $details->url = 'mysql:host=localhost;port=3306;'; - $details->connectionType = DatabaseType::MYSQL; + $details->connectionType = DatabaseType::MYSQL->value; $database = new Database($details, PDOMock::class); $this->assertTrue($database instanceof Database); } @@ -106,7 +106,7 @@ public function testInstantiationUnknownDbClass(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = DatabaseType::MYSQL; - $this->expectException(InvalidOptionException::class); + $this->expectException(\TypeError::class); new Database($details, \stdClass::class); } @@ -117,7 +117,7 @@ public function testInstantiationWithDeprecatedMethodMySQL(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL; + $details->connectionType = DatabaseType::MYSQL->value; $database = new Database($details, PDOMock::class); $this->assertTrue($database instanceof Database); } @@ -129,7 +129,7 @@ public function testInstantiationWithDeprecatedMethodSqLite(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::SQLITE; + $details->connectionType = DatabaseType::SQLITE->value; $database = new Database($details, PDOMock::class); $this->assertTrue($database instanceof Database); } diff --git a/FeastTests/Database/Table/TableFactoryTest.php b/FeastTests/Database/Table/TableFactoryTest.php index b11c8f7..b7eef01 100644 --- a/FeastTests/Database/Table/TableFactoryTest.php +++ b/FeastTests/Database/Table/TableFactoryTest.php @@ -51,11 +51,11 @@ public function testGetTableUnrecognized(): void { $dbfInterface = $this->createStub(DatabaseFactory::class); $dbInterface = $this->createStub(DatabaseInterface::class); - $dbInterface->method('getDatabaseType')->willReturn('TableTypeDoesn\'tExist'); + $dbInterface->method('getDatabaseType')->willThrowException(new \TypeError()); $dbfInterface->method('getConnection')->willReturn($dbInterface); $serviceContainer = di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER); $serviceContainer->add(DatabaseFactoryInterface::class, $dbfInterface); - $this->expectException(DatabaseException::class); + $this->expectException(\TypeError::class); TableFactory::getTable('test'); } } diff --git a/FeastTests/Logger/LoggerTest.php b/FeastTests/Logger/LoggerTest.php index 535c923..abeb021 100644 --- a/FeastTests/Logger/LoggerTest.php +++ b/FeastTests/Logger/LoggerTest.php @@ -242,7 +242,7 @@ public function testRawLogHigherLevel(): void $config = $this->createStub(Config::class); $config->method('getSetting')->willReturn(LogLevel::EMERGENCY); $logger = new Logger($config, Main::RUN_AS_CLI); - $logger->rawLog(LogLevelCode::ALERT, 'you noticing me'); + $logger->rawLog(LogLevelCode::ALERT->value, 'you noticing me'); $output = $this->getActualOutputForAssertion(); $this->assertStringNotContainsString('NOTICE: you noticing me', $output); } diff --git a/FeastTests/Mocks/LoggerMock.php b/FeastTests/Mocks/LoggerMock.php index 6c2b47e..f8c1370 100644 --- a/FeastTests/Mocks/LoggerMock.php +++ b/FeastTests/Mocks/LoggerMock.php @@ -20,6 +20,7 @@ namespace Mocks; +use Feast\Enums\LogLevelCode; use Feast\Interfaces\LoggerInterface; class LoggerMock implements LoggerInterface diff --git a/FeastTests/ViewTest.php b/FeastTests/ViewTest.php index ff3f50c..b10491e 100644 --- a/FeastTests/ViewTest.php +++ b/FeastTests/ViewTest.php @@ -18,7 +18,7 @@ declare(strict_types=1); -use Feast\Enums\DocTypes; +use Feast\Enums\DocType; use Feast\Interfaces\ConfigInterface; use Feast\View; use PHPUnit\Framework\TestCase; @@ -42,7 +42,7 @@ public function setUp(): void $this->returnValueMap( [ ['siteurl', null, 'test'], - ['html.doctype', DocTypes::HTML_5, DocTypes::HTML_4_01_STRICT] + ['html.doctype', DocType::HTML_5, DocType::HTML_4_01_STRICT] ] ) ); @@ -67,86 +67,86 @@ public function testGetDoctypeHtml401Strict(): void '', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::HTML_4_01_STRICT, $this->view->getDocType()); + $this->assertEquals(DocType::HTML_4_01_STRICT, $this->view->getDocType()); } public function testGetDoctypeHtml5(): void { - $this->view->setDoctype(DocTypes::HTML_5); + $this->view->setDoctype(DocType::HTML_5); $this->assertEquals( ' ', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::HTML_5, $this->view->getDocType()); + $this->assertEquals(DocType::HTML_5, $this->view->getDocType()); } public function testGetDoctypeXhtml10Strict(): void { - $this->view->setDoctype(DocTypes::XHTML_1_0_STRICT); + $this->view->setDoctype(DocType::XHTML_1_0_STRICT); $this->assertEquals( ' ', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::XHTML_1_0_STRICT, $this->view->getDocType()); + $this->assertEquals(DocType::XHTML_1_0_STRICT, $this->view->getDocType()); } public function testGetDoctypeXhtml11(): void { - $this->view->setDoctype(DocTypes::XHTML_1_1); + $this->view->setDoctype(DocType::XHTML_1_1); $this->assertEquals( '', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::XHTML_1_1, $this->view->getDocType()); + $this->assertEquals(DocType::XHTML_1_1, $this->view->getDocType()); } public function testGetDoctypeXhtml10Transitional(): void { - $this->view->setDoctype(DocTypes::XHTML_1_0_TRANSITIONAL); + $this->view->setDoctype(DocType::XHTML_1_0_TRANSITIONAL); $this->assertEquals( '', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::XHTML_1_0_TRANSITIONAL, $this->view->getDocType()); + $this->assertEquals(DocType::XHTML_1_0_TRANSITIONAL, $this->view->getDocType()); } public function testGetDoctypeHtml401Frameset(): void { - $this->view->setDoctype(DocTypes::HTML_4_01_FRAMESET); + $this->view->setDoctype(DocType::HTML_4_01_FRAMESET); $this->assertEquals( '', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::HTML_4_01_FRAMESET, $this->view->getDocType()); + $this->assertEquals(DocType::HTML_4_01_FRAMESET, $this->view->getDocType()); } public function testGetDoctypeXhtml10Frameset(): void { - $this->view->setDoctype(DocTypes::XHTML_1_0_FRAMESET); + $this->view->setDoctype(DocType::XHTML_1_0_FRAMESET); $this->assertEquals( '', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::XHTML_1_0_FRAMESET, $this->view->getDocType()); + $this->assertEquals(DocType::XHTML_1_0_FRAMESET, $this->view->getDocType()); } public function testGetDoctypeHtml401Transitional(): void { - $this->view->setDoctype(DocTypes::HTML_4_01_TRANSITIONAL); + $this->view->setDoctype(DocType::HTML_4_01_TRANSITIONAL); $this->assertEquals( '', trim($this->view->getDtd()) ); - $this->assertEquals(DocTypes::HTML_4_01_TRANSITIONAL, $this->view->getDocType()); + $this->assertEquals(DocType::HTML_4_01_TRANSITIONAL, $this->view->getDocType()); } public function testSetInvalidDoctype(): void { - $this->expectException(\Exception::class); + $this->expectException(\TypeError::class); $this->view->setDoctype('This is bad data.'); } @@ -220,7 +220,7 @@ public function testGetEncodingHtml(): void public function testGetEncodingXhtmlHtml(): void { - $this->view->setDoctype(DocTypes::XHTML_1_0_STRICT); + $this->view->setDoctype(DocType::XHTML_1_0_STRICT); $this->view->setEncoding('EN'); $this->assertEquals( '', @@ -231,7 +231,7 @@ public function testGetEncodingXhtmlHtml(): void public function testGetEncodingHtml5Html(): void { - $this->view->setDoctype(DocTypes::HTML_5); + $this->view->setDoctype(DocType::HTML_5); $this->view->setEncoding('EN'); $this->assertEquals( '', @@ -242,7 +242,7 @@ public function testGetEncodingHtml5Html(): void public function testSetDocTypeGibberish(): void { - $this->expectException(Exception::class); + $this->expectException(TypeError::class); $this->view->setDoctype('FeastHtml'); } diff --git a/Interfaces/DatabaseInterface.php b/Interfaces/DatabaseInterface.php index f02bc5c..842588a 100644 --- a/Interfaces/DatabaseInterface.php +++ b/Interfaces/DatabaseInterface.php @@ -23,6 +23,7 @@ use Exception; use Feast\Database\Query; use Feast\Database\TableDetails; +use Feast\Enums\DatabaseType; use PDO; /** @@ -175,7 +176,7 @@ public function rawQuery(string $query, array $bindings = [], bool $forceEmulate /** * Get Database type. * - * @return string + * @return DatabaseType */ - public function getDatabaseType(): string; + public function getDatabaseType(): DatabaseType; } diff --git a/Interfaces/LoggerInterface.php b/Interfaces/LoggerInterface.php index 870ad2b..a664d96 100644 --- a/Interfaces/LoggerInterface.php +++ b/Interfaces/LoggerInterface.php @@ -20,6 +20,7 @@ namespace Feast\Interfaces; +use Feast\Enums\LogLevelCode; use Feast\ServiceContainer\ServiceContainerItemInterface; interface LoggerInterface extends ServiceContainerItemInterface diff --git a/Logger/ErrorLogger.php b/Logger/ErrorLogger.php index 15303f0..b2b9140 100644 --- a/Logger/ErrorLogger.php +++ b/Logger/ErrorLogger.php @@ -109,7 +109,7 @@ public function exceptionHandler(Throwable $exception, bool $caught = false): bo ) . ' on line ' . (string)$exception->getLine(); $this->logger->error($errorMessage); - $this->logger->rawLog(LogLevelCode::ERROR, $exception->getTraceAsString()); + $this->logger->rawLog(LogLevelCode::ERROR->value, $exception->getTraceAsString()); $newException = new ServerFailureException( $exception->getMessage(), null, diff --git a/Logger/Logger.php b/Logger/Logger.php index b851d56..3ab41c2 100644 --- a/Logger/Logger.php +++ b/Logger/Logger.php @@ -37,7 +37,7 @@ class Logger implements LoggerInterface, ServiceContainerItemInterface, \Feast\I use DependencyInjected; private const LOG_DIR = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR; - private ?int $logLevel; + private LogLevelCode $logLevel; public function __construct(private ConfigInterface $config, private string $runAs) { @@ -160,11 +160,13 @@ public function log($level, $message, array $context = []): void $message = strtoupper($level) . ': ' . $message; $level = $this->getLevelFromString($level); } - if ($level < $this->logLevel) { + /** @psalm-suppress UndefinedPropertyFetch - False error */ + if ($level instanceof LogLevelCode === false || $level->value < $this->logLevel->value) { return; } $message = $this->interpolateContext($message, $context); - $this->rawLog((int)$level, (date('[Y-m-d H:i:s] ')) . trim($message)); + /** @psalm-suppress UndefinedPropertyFetch - False error */ + $this->rawLog((int)$level->value, (date('[Y-m-d H:i:s] ')) . trim($message)); if (isset($context['exception']) && $context['exception'] instanceof Throwable) { $exception = $context['exception']; @@ -228,7 +230,7 @@ protected function makeLogDirIfNotExists(): void } } - private function getLevelFromString(string $level): int + private function getLevelFromString(string $level): LogLevelCode { /** @noinspection PhpUnhandledExceptionInspection */ return match (strtolower($level)) { diff --git a/Router/Router.php b/Router/Router.php index 26bb528..1911fa1 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -344,7 +344,7 @@ protected function setCliArgumentsOnRequest( $instance = $parameter->newInstance(); if ($instance->paramType === ParamType::PARAM) { $parameterList[] = $instance->name; - } elseif ($instance->paramType === ParamType::FLAG) { + } else { $flagList[$instance->name] = $instance->name; } } diff --git a/Traits/Collection.php b/Traits/Collection.php index 21481c5..7bb69d5 100644 --- a/Traits/Collection.php +++ b/Traits/Collection.php @@ -98,7 +98,7 @@ protected function objectImplode(string $separator, string $key): string * @see CollectionSort * */ - public function sort(int $sortType, bool $modifyOriginal = false, int $sortOptions = SORT_REGULAR): array + public function sort(CollectionSort $sortType, bool $modifyOriginal = false, int $sortOptions = SORT_REGULAR): array { $array = $this->array; switch ($sortType) { @@ -114,8 +114,6 @@ public function sort(int $sortType, bool $modifyOriginal = false, int $sortOptio case CollectionSort::VALUE_REVERSE: arsort($array, $sortOptions); break; - default: - throw new InvalidOptionException('Invalid sort type'); } if ($modifyOriginal) { $this->array = $array; @@ -128,7 +126,7 @@ public function sort(int $sortType, bool $modifyOriginal = false, int $sortOptio * Sort named-class based collection by the value of a key or multiple keys. * * @param string|array $key - * @param int $sortType + * @param CollectionSort $sortType * @param bool $modifyOriginal * @return array * @throws InvalidOptionException @@ -136,7 +134,7 @@ public function sort(int $sortType, bool $modifyOriginal = false, int $sortOptio */ public function objectSort( string|array $key, - int $sortType = CollectionSort::VALUE, + CollectionSort $sortType = CollectionSort::VALUE, bool $modifyOriginal = false ): array { switch ($this->type) { diff --git a/View.php b/View.php index ebf5ea8..5c5c2f4 100644 --- a/View.php +++ b/View.php @@ -22,7 +22,7 @@ use Exception; use Feast\Collection\Collection; -use Feast\Enums\DocTypes; +use Feast\Enums\DocType; use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\RouterInterface; use Feast\ServiceContainer\ServiceContainerItemInterface; @@ -42,7 +42,7 @@ class View extends stdClass implements ServiceContainerItemInterface private array $css = []; private bool $outputDisabled = false; private string $content = ''; - private string $docType = DocTypes::HTML_5; + private DocType $docType = DocType::HTML_5; private string $dtd = '' . "\n" . ''; private string $encoding = 'UTF-8'; private array $postScripts = []; @@ -62,7 +62,8 @@ public function __construct(protected ConfigInterface $config, protected RouterI { $this->checkInjected(); $this->baseUrl = (string)$config->getSetting('siteurl'); - $docType = (string)$config->getSetting('html.doctype', DocTypes::HTML_5); + /** @var DocType $docType */ + $docType = $config->getSetting('html.doctype', DocType::HTML_5); $this->setDoctype($docType); } @@ -305,7 +306,7 @@ public function getCss(): string /** * Adds a css file to the page, - * + * * Feast uses the /css/ folder for the base directory for all stylesheets. * * @param string $fileName @@ -358,41 +359,39 @@ public function getTitle(bool $html = true): string /** * Sets the doc type of the page. - * + * * Also sets the doctype declaration * - * @param string $doctype + * @param DocType $doctype * @throws Exception */ - public function setDoctype(string $doctype = DocTypes::HTML_5): void + public function setDoctype(DocType $doctype = DocType::HTML_5): void { - if ($doctype == DocTypes::HTML_4_01_TRANSITIONAL) { + if ($doctype == DocType::HTML_4_01_TRANSITIONAL) { $this->dtd = ''; - } elseif ($doctype == DocTypes::HTML_4_01_STRICT) { + } elseif ($doctype == DocType::HTML_4_01_STRICT) { $this->dtd = ''; - } elseif ($doctype == DocTypes::HTML_4_01_FRAMESET) { + } elseif ($doctype == DocType::HTML_4_01_FRAMESET) { $this->dtd = ''; - } elseif ($doctype == DocTypes::HTML_5) { + } elseif ($doctype == DocType::HTML_5) { $this->dtd = '' . "\n" . ''; - } elseif ($doctype == DocTypes::XHTML_1_0_TRANSITIONAL) { + } elseif ($doctype == DocType::XHTML_1_0_TRANSITIONAL) { $this->dtd = ''; - } elseif ($doctype == DocTypes::XHTML_1_0_STRICT) { + } elseif ($doctype == DocType::XHTML_1_0_STRICT) { $this->dtd = ' '; - } elseif ($doctype == DocTypes::XHTML_1_0_FRAMESET) { + } elseif ($doctype == DocType::XHTML_1_0_FRAMESET) { $this->dtd = ''; - } elseif ($doctype == DocTypes::XHTML_1_1) { + } else { // DocType::XHTML_1_1 $this->dtd = ''; - } else { - throw new Exception('Invalid doctype'); } $this->docType = $doctype; } /** * Get the Doctype Declaration. - * + * * @return string */ public function getDtd(): string @@ -402,17 +401,17 @@ public function getDtd(): string /** * Get the Doctype. - * - * @return string + * + * @return DocType */ - public function getDocType(): string + public function getDocType(): DocType { return $this->docType; } /** * Set the encoding for the page. - * + * * Default is UTF-8. * * @param string $encoding @@ -431,15 +430,15 @@ public function setEncoding(string $encoding = 'UTF-8'): void public function getEncodingHtml(): string { return match ($this->docType) { - DocTypes::XHTML_1_0_STRICT, DocTypes::XHTML_1_0_TRANSITIONAL, DocTypes::XHTML_1_0_FRAMESET => '' . PHP_EOL, - DocTypes::HTML_4_01_TRANSITIONAL, DocTypes::HTML_4_01_FRAMESET, DocTypes::HTML_4_01_STRICT => '' . PHP_EOL, - DocTypes::HTML_5 => '' . PHP_EOL + DocType::XHTML_1_0_STRICT, DocType::XHTML_1_0_TRANSITIONAL, DocType::XHTML_1_0_FRAMESET, DocType::XHTML_1_1 => '' . PHP_EOL, + DocType::HTML_4_01_TRANSITIONAL, DocType::HTML_4_01_FRAMESET, DocType::HTML_4_01_STRICT => '' . PHP_EOL, + DocType::HTML_5 => '' . PHP_EOL }; } /** * Get the raw encoding. - * + * * @return string */ public function getEncoding(): string @@ -449,8 +448,8 @@ public function getEncoding(): string /** * Render a partial view. - * - * Any duplicate arguments will override ones in the view during the partial. + * + * Any duplicate arguments will override ones in the view during the partial. * The view itself does not change. * * @param string $file - filename to use for the view (in Views folder). @@ -463,7 +462,7 @@ public function partial(string $file, array $arguments = [], bool $includeView = } /** - * Render partial views by looping through the arguments loop. + * Render partial views by looping through the arguments loop. * * Any duplicate arguments will override ones in the view during the partial. * The view itself does not change. @@ -536,7 +535,7 @@ public function emptyView(): void /** * Get layout filename. - * + * * @return string */ public function getLayoutFile(): string @@ -545,7 +544,8 @@ public function getLayoutFile(): string } /** - * Set layout filename. + * Set layout filename. + * * @param string $file */ public function setLayoutFile(string $file = 'layout'): void @@ -555,7 +555,7 @@ public function setLayoutFile(string $file = 'layout'): void /** * Set a dynamic value on the view. - * + * * @param string $key * @param string|int|bool|float|object|array|null $value */ @@ -566,7 +566,7 @@ public function __set(string $key, string|int|bool|float|object|null|array $valu /** * Get a dynamic value from the view. - * + * * @param string $value * @return mixed */ diff --git a/composer.json b/composer.json index 97483a7..5efbcd8 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": "^8.0.0", + "php": "^8.1.0", "ext-pdo": "*" }, "suggest": { diff --git a/composer.lock b/composer.lock index d0d68e3..3ca9d9d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "624629d1572b727bf614189f9684bdb0", + "content-hash": "ee688c83ba436ed6395805eb009fcab2", "packages": [], "packages-dev": [ { @@ -49,9 +49,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.0.0", + "php": "^8.1.0", "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } From 2636d3eddc02d28c279dcbcc9d937ed495a47dff Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 21 Oct 2021 19:09:29 -0400 Subject: [PATCH 02/56] Response codes as Enum --- BaseModel.php | 5 +- Database/DatabaseFactory.php | 3 +- Email/Attachment.php | 3 +- Enums/DocType.php | 1 - Enums/ResponseCode.php | 146 +++++++++--------- Exception/Error404Exception.php | 4 +- Exception/InvalidDateException.php | 3 +- Exception/ResponseException.php | 26 ---- Exception/ServerFailureException.php | 9 +- .../Exception/ServerFailureExceptionTest.php | 19 +-- FeastTests/HttpRequest/CurlTest.php | 13 +- FeastTests/ResponseTest.php | 3 +- HttpController.php | 8 +- HttpRequest/HttpRequest.php | 14 +- HttpRequest/Response.php | 3 +- Interfaces/HttpRequestInterface.php | 5 +- Interfaces/ResponseInterface.php | 9 +- Main.php | 2 +- Response.php | 23 ++- composer.json | 5 +- 20 files changed, 145 insertions(+), 159 deletions(-) delete mode 100644 Exception/ResponseException.php diff --git a/BaseModel.php b/BaseModel.php index 8e2a3b3..59f60e4 100755 --- a/BaseModel.php +++ b/BaseModel.php @@ -20,6 +20,7 @@ namespace Feast; +use Feast\Enums\ResponseCode; use Feast\Exception\InvalidOptionException; use Feast\Exception\NotFoundException; @@ -34,12 +35,12 @@ abstract class BaseModel public function __set(string $name, mixed $value): void { - throw new InvalidOptionException('Invalid option for model', 500); + throw new InvalidOptionException('Invalid option for model', ResponseCode::HTTP_CODE_500); } public function __get(string $name): void { - throw new InvalidOptionException('Invalid option for model', 500); + throw new InvalidOptionException('Invalid option for model', ResponseCode::HTTP_CODE_500); } /** diff --git a/Database/DatabaseFactory.php b/Database/DatabaseFactory.php index 1a6162c..1baeeec 100644 --- a/Database/DatabaseFactory.php +++ b/Database/DatabaseFactory.php @@ -20,6 +20,7 @@ namespace Feast\Database; +use Feast\Enums\ResponseCode; use Feast\Exception\DatabaseException; use Feast\Exception\ServerFailureException; use Feast\Interfaces\ConfigInterface; @@ -68,7 +69,7 @@ public function getConnection(string $connection = self::DEFAULT_CONNECTION): Da return $connectionObject; } - throw new DatabaseException('Invalid Database Specified', 500, 500); + throw new DatabaseException('Invalid Database Specified', ResponseCode::HTTP_CODE_500, 500); } /** diff --git a/Email/Attachment.php b/Email/Attachment.php index 350f349..c973f67 100644 --- a/Email/Attachment.php +++ b/Email/Attachment.php @@ -20,6 +20,7 @@ namespace Feast\Email; +use Feast\Enums\ResponseCode; use Feast\Exception\NotFoundException; class Attachment @@ -41,7 +42,7 @@ class Attachment public function setContentFromFile(string $fileName): static { if (is_file($fileName) === false || is_readable($fileName) === false) { - throw new NotFoundException('Invalid file', 500); + throw new NotFoundException('Invalid file', ResponseCode::HTTP_CODE_500); } $file = file_get_contents($fileName); $this->setContent($file); diff --git a/Enums/DocType.php b/Enums/DocType.php index 5a02e78..7df6acc 100644 --- a/Enums/DocType.php +++ b/Enums/DocType.php @@ -23,7 +23,6 @@ enum DocType: string { case HTML_4_01_FRAMESET = 'html401frame'; - case HTML_4_01_STRICT = 'html401strict'; case HTML_4_01_TRANSITIONAL = 'html401transitional'; case HTML_5 = 'html5'; diff --git a/Enums/ResponseCode.php b/Enums/ResponseCode.php index 8df8cca..b9edcdf 100644 --- a/Enums/ResponseCode.php +++ b/Enums/ResponseCode.php @@ -20,80 +20,80 @@ namespace Feast\Enums; -class ResponseCode +enum ResponseCode: int { - public const HTTP_CODE_100 = 100; - public const HTTP_CODE_101 = 101; - public const HTTP_CODE_102 = 102; - public const HTTP_CODE_200 = 200; - public const HTTP_CODE_201 = 201; - public const HTTP_CODE_202 = 202; - public const HTTP_CODE_203 = 203; - public const HTTP_CODE_204 = 204; - public const HTTP_CODE_205 = 205; - public const HTTP_CODE_206 = 206; - public const HTTP_CODE_207 = 207; - public const HTTP_CODE_300 = 300; - public const HTTP_CODE_301 = 301; - public const HTTP_CODE_302 = 302; - public const HTTP_CODE_303 = 303; - public const HTTP_CODE_304 = 304; - public const HTTP_CODE_305 = 305; - public const HTTP_CODE_306 = 306; - public const HTTP_CODE_307 = 307; - public const HTTP_CODE_308 = 308; - public const HTTP_CODE_400 = 400; - public const HTTP_CODE_401 = 401; - public const HTTP_CODE_402 = 402; - public const HTTP_CODE_403 = 403; - public const HTTP_CODE_404 = 404; - public const HTTP_CODE_405 = 405; - public const HTTP_CODE_406 = 406; - public const HTTP_CODE_407 = 407; - public const HTTP_CODE_408 = 408; - public const HTTP_CODE_409 = 409; - public const HTTP_CODE_410 = 410; - public const HTTP_CODE_411 = 411; - public const HTTP_CODE_412 = 412; - public const HTTP_CODE_413 = 413; - public const HTTP_CODE_414 = 414; - public const HTTP_CODE_415 = 415; - public const HTTP_CODE_416 = 416; - public const HTTP_CODE_417 = 417; - public const HTTP_CODE_418 = 418; - public const HTTP_CODE_419 = 419; - public const HTTP_CODE_420 = 420; - public const HTTP_CODE_422 = 422; - public const HTTP_CODE_423 = 423; - public const HTTP_CODE_424 = 424; - public const HTTP_CODE_425 = 425; - public const HTTP_CODE_426 = 426; - public const HTTP_CODE_428 = 428; - public const HTTP_CODE_429 = 429; - public const HTTP_CODE_431 = 431; - public const HTTP_CODE_444 = 444; - public const HTTP_CODE_449 = 449; - public const HTTP_CODE_450 = 450; - public const HTTP_CODE_451 = 451; - public const HTTP_CODE_494 = 494; - public const HTTP_CODE_495 = 495; - public const HTTP_CODE_496 = 496; - public const HTTP_CODE_497 = 497; - public const HTTP_CODE_499 = 499; - public const HTTP_CODE_500 = 500; - public const HTTP_CODE_501 = 501; - public const HTTP_CODE_502 = 502; - public const HTTP_CODE_503 = 503; - public const HTTP_CODE_504 = 504; - public const HTTP_CODE_505 = 505; - public const HTTP_CODE_506 = 506; - public const HTTP_CODE_507 = 507; - public const HTTP_CODE_508 = 508; - public const HTTP_CODE_509 = 509; - public const HTTP_CODE_510 = 510; - public const HTTP_CODE_511 = 511; - public const HTTP_CODE_598 = 598; - public const HTTP_CODE_599 = 599; + case HTTP_CODE_100 = 100; + case HTTP_CODE_101 = 101; + case HTTP_CODE_102 = 102; + case HTTP_CODE_200 = 200; + case HTTP_CODE_201 = 201; + case HTTP_CODE_202 = 202; + case HTTP_CODE_203 = 203; + case HTTP_CODE_204 = 204; + case HTTP_CODE_205 = 205; + case HTTP_CODE_206 = 206; + case HTTP_CODE_207 = 207; + case HTTP_CODE_300 = 300; + case HTTP_CODE_301 = 301; + case HTTP_CODE_302 = 302; + case HTTP_CODE_303 = 303; + case HTTP_CODE_304 = 304; + case HTTP_CODE_305 = 305; + case HTTP_CODE_306 = 306; + case HTTP_CODE_307 = 307; + case HTTP_CODE_308 = 308; + case HTTP_CODE_400 = 400; + case HTTP_CODE_401 = 401; + case HTTP_CODE_402 = 402; + case HTTP_CODE_403 = 403; + case HTTP_CODE_404 = 404; + case HTTP_CODE_405 = 405; + case HTTP_CODE_406 = 406; + case HTTP_CODE_407 = 407; + case HTTP_CODE_408 = 408; + case HTTP_CODE_409 = 409; + case HTTP_CODE_410 = 410; + case HTTP_CODE_411 = 411; + case HTTP_CODE_412 = 412; + case HTTP_CODE_413 = 413; + case HTTP_CODE_414 = 414; + case HTTP_CODE_415 = 415; + case HTTP_CODE_416 = 416; + case HTTP_CODE_417 = 417; + case HTTP_CODE_418 = 418; + case HTTP_CODE_419 = 419; + case HTTP_CODE_420 = 420; + case HTTP_CODE_422 = 422; + case HTTP_CODE_423 = 423; + case HTTP_CODE_424 = 424; + case HTTP_CODE_425 = 425; + case HTTP_CODE_426 = 426; + case HTTP_CODE_428 = 428; + case HTTP_CODE_429 = 429; + case HTTP_CODE_431 = 431; + case HTTP_CODE_444 = 444; + case HTTP_CODE_449 = 449; + case HTTP_CODE_450 = 450; + case HTTP_CODE_451 = 451; + case HTTP_CODE_494 = 494; + case HTTP_CODE_495 = 495; + case HTTP_CODE_496 = 496; + case HTTP_CODE_497 = 497; + case HTTP_CODE_499 = 499; + case HTTP_CODE_500 = 500; + case HTTP_CODE_501 = 501; + case HTTP_CODE_502 = 502; + case HTTP_CODE_503 = 503; + case HTTP_CODE_504 = 504; + case HTTP_CODE_505 = 505; + case HTTP_CODE_506 = 506; + case HTTP_CODE_507 = 507; + case HTTP_CODE_508 = 508; + case HTTP_CODE_509 = 509; + case HTTP_CODE_510 = 510; + case HTTP_CODE_511 = 511; + case HTTP_CODE_598 = 598; + case HTTP_CODE_599 = 599; /** * Returns if response code is valid. diff --git a/Exception/Error404Exception.php b/Exception/Error404Exception.php index 4e54819..49a7b71 100644 --- a/Exception/Error404Exception.php +++ b/Exception/Error404Exception.php @@ -20,12 +20,14 @@ namespace Feast\Exception; +use Feast\Enums\ResponseCode; + class Error404Exception extends ServerFailureException { public function __construct(string $message, int $errorCode = 0, \Throwable $previousException = null) { - parent::__construct($message, 404, $errorCode, $previousException); + parent::__construct($message, ResponseCode::HTTP_CODE_404, $errorCode, $previousException); } } diff --git a/Exception/InvalidDateException.php b/Exception/InvalidDateException.php index ffc73f7..118dc8e 100644 --- a/Exception/InvalidDateException.php +++ b/Exception/InvalidDateException.php @@ -20,6 +20,7 @@ namespace Feast\Exception; +use Feast\Enums\ResponseCode; use Throwable; class InvalidDateException extends ServerFailureException @@ -27,7 +28,7 @@ class InvalidDateException extends ServerFailureException public function __construct( string $message, - ?int $responseCode = null, + ?ResponseCode $responseCode = null, int $errorCode = 0, Throwable $previousException = null ) { diff --git a/Exception/ResponseException.php b/Exception/ResponseException.php deleted file mode 100644 index 0b53857..0000000 --- a/Exception/ResponseException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -declare(strict_types=1); - -namespace Feast\Exception; - -class ResponseException extends ServerFailureException -{ - -} diff --git a/Exception/ServerFailureException.php b/Exception/ServerFailureException.php index ab29e0a..b76c86b 100644 --- a/Exception/ServerFailureException.php +++ b/Exception/ServerFailureException.php @@ -22,6 +22,7 @@ use Exception; +use Feast\Enums\ResponseCode; use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\RequestInterface; use Feast\Interfaces\ResponseInterface; @@ -43,14 +44,14 @@ class ServerFailureException extends Exception * Construct a new Exception, and include the response code. * * @param string $message - * @param int|null $responseCode + * @param ResponseCode|null $responseCode * @param int $errorCode * @param Throwable|null $previousException * @param string|null $overrideRunAs */ public function __construct( string $message, - private ?int $responseCode = null, + private ?ResponseCode $responseCode = null, int $errorCode = 0, Throwable $previousException = null, ?string $overrideRunAs = null @@ -65,9 +66,9 @@ public function __construct( /** * Get the response code * - * @return int|null + * @return ResponseCode|null */ - public function getResponseCode(): ?int + public function getResponseCode(): ?ResponseCode { return $this->responseCode; } diff --git a/FeastTests/Exception/ServerFailureExceptionTest.php b/FeastTests/Exception/ServerFailureExceptionTest.php index 0d204ec..5605085 100644 --- a/FeastTests/Exception/ServerFailureExceptionTest.php +++ b/FeastTests/Exception/ServerFailureExceptionTest.php @@ -20,6 +20,7 @@ namespace Exception; +use Feast\Enums\ResponseCode; use Feast\Exception\ServerFailureException; use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\RequestInterface; @@ -35,7 +36,7 @@ public function testPrintErrorJson(): void { $this->buildContainer('json', true); - $exception = new ServerFailureException('Test', 302, 5, null, Main::RUN_AS_CLI); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, null, Main::RUN_AS_CLI); $this->expectOutputString('{"error":{"message":"Test","code":5}}'); $exception->printError(); @@ -44,7 +45,7 @@ public function testPrintErrorJson(): void public function testPrintError(): void { $this->buildContainer(setting: true); - $exception = new ServerFailureException('Test', 302, 0, null, Main::RUN_AS_WEBAPP); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 0, null, Main::RUN_AS_WEBAPP); $this->expectOutputRegex('/Test
.*/'); $exception->printError(); @@ -53,7 +54,7 @@ public function testPrintError(): void public function testPrintParentExceptionCliNoParent(): void { $this->buildContainer('json', true); - $exception = new ServerFailureException('Test', 302, 5, null, Main::RUN_AS_CLI); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, null, Main::RUN_AS_CLI); $this->expectOutputString(''); $exception->printParentException(); } @@ -61,7 +62,7 @@ public function testPrintParentExceptionCliNoParent(): void public function testPrintParentExceptionCliWithParent(): void { $this->buildContainer('json', true); - $exception = new ServerFailureException('Test', 302, 5, new \Exception('Test'), Main::RUN_AS_CLI); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, new \Exception('Test'), Main::RUN_AS_CLI); $exception->printParentException(); $output = $this->getActualOutputForAssertion(); $this->assertStringContainsString( @@ -74,7 +75,7 @@ public function testPrintParentExceptionCliWithParent(): void public function testPrintExceptionCli(): void { $this->buildContainer(setting: true); - $exception = new ServerFailureException('Test', 302, 5, null, Main::RUN_AS_CLI); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, null, Main::RUN_AS_CLI); $exception->printError(); $output = $this->getActualOutputForAssertion(); $this->assertStringContainsString( @@ -87,7 +88,7 @@ public function testPrintExceptionCli(): void public function testPrintParentExceptionWebNoParent(): void { $this->buildContainer('json', true); - $exception = new ServerFailureException('Test', 302, 5, null, Main::RUN_AS_WEBAPP); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, null, Main::RUN_AS_WEBAPP); $this->expectOutputString(''); $exception->printParentException(); } @@ -95,7 +96,7 @@ public function testPrintParentExceptionWebNoParent(): void public function testPrintParentExceptionWebWithParent(): void { $this->buildContainer('json', true); - $exception = new ServerFailureException('Test', 302, 5, new \Exception('Test'), Main::RUN_AS_WEBAPP); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, new \Exception('Test'), Main::RUN_AS_WEBAPP); $this->expectOutputRegex('/Test
Thrown on.*printParentException(); } @@ -103,8 +104,8 @@ public function testPrintParentExceptionWebWithParent(): void public function testGetResponseCode(): void { $this->buildContainer('json', true); - $exception = new ServerFailureException('Test', 302, 5, null, Main::RUN_AS_CLI); - $this->assertEquals(302, $exception->getResponseCode()); + $exception = new ServerFailureException('Test', ResponseCode::HTTP_CODE_302, 5, null, Main::RUN_AS_CLI); + $this->assertEquals(ResponseCode::HTTP_CODE_302, $exception->getResponseCode()); } protected function buildContainer(?string $format = null, ?bool $setting = null): void diff --git a/FeastTests/HttpRequest/CurlTest.php b/FeastTests/HttpRequest/CurlTest.php index 47505c9..a115bbe 100644 --- a/FeastTests/HttpRequest/CurlTest.php +++ b/FeastTests/HttpRequest/CurlTest.php @@ -19,6 +19,7 @@ namespace HttpRequest; +use Feast\Enums\ResponseCode; use Feast\Exception\BadRequestException; use Feast\Exception\CurlException; use Feast\HttpRequest\Curl; @@ -147,7 +148,7 @@ public function testMakeRequestAndGetResponseCode(): void $request = new Curl(); $request->get('https://www.google.com/html'); $request->makeRequest(); - $this->assertEquals(200, $request->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_200, $request->getResponseCode()); $this->assertEquals( ' @@ -169,7 +170,7 @@ public function testGetResultAsXml(): void $request = new Curl(); $request->get('https://www.google.com/xml'); $request->makeRequest(); - $this->assertEquals(200, $request->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_200, $request->getResponseCode()); $this->assertEquals( ' @@ -191,7 +192,7 @@ public function testGetInvalidAsXml(): void $request = new Curl(); $request->get('https://www.google.com/json'); $request->makeRequest(); - $this->assertEquals(200, $request->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_200, $request->getResponseCode()); $this->assertNull( $request->getResponseAsXml() ); @@ -202,7 +203,7 @@ public function testGetResultAsJson(): void $request = new Curl(); $request->get('https://www.google.com/json'); $request->makeRequest(); - $this->assertEquals(200, $request->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_200, $request->getResponseCode()); $response = $request->getResponseAsJson(); $this->assertEquals('feast', $response->test); } @@ -212,7 +213,7 @@ public function testGetResultAsJsonInvalid(): void $request = new Curl(); $request->get('https://www.google.com/html'); $request->makeRequest(); - $this->assertEquals(200, $request->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_200, $request->getResponseCode()); $response = $request->getResponseAsJson(); $this->assertNull($response); } @@ -222,7 +223,7 @@ public function testGetResultCurlFailed(): void $request = new Curl(); $request->get('https://www.google.com/fail'); $request->makeRequest(); - $this->assertEquals(500, $request->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_500, $request->getResponseCode()); $response = $request->getResponseAsString(); $this->assertEquals('', $response); } diff --git a/FeastTests/ResponseTest.php b/FeastTests/ResponseTest.php index dbcfd9f..ffdd072 100644 --- a/FeastTests/ResponseTest.php +++ b/FeastTests/ResponseTest.php @@ -18,7 +18,6 @@ declare(strict_types=1); use Feast\Enums\ResponseCode; -use Feast\Exception\ResponseException; use Feast\Response; use PHPUnit\Framework\TestCase; @@ -48,7 +47,7 @@ public function testUpdateResponse(): void public function testInvalidResponse(): void { $response = new Response(); - $this->expectException(ResponseException::class); + $this->expectException(TypeError::class); $response->setResponseCode(99999); } diff --git a/HttpController.php b/HttpController.php index 0211068..67b927a 100755 --- a/HttpController.php +++ b/HttpController.php @@ -118,7 +118,7 @@ public function alwaysJson(string $actionName): bool * @param array $queryString * @param string|null $module * @param string $route - * @param int $code (30x) + * @param ResponseCode $code (30x) * @throws NotFoundException */ public function redirect( @@ -128,7 +128,7 @@ public function redirect( array $queryString = [], ?string $module = null, string $route = '', - int $code = ResponseCode::HTTP_CODE_302 + ResponseCode $code = ResponseCode::HTTP_CODE_302 ): void { $router = $this->di->get(RouterInterface::class); $response = $this->di->get(ResponseInterface::class); @@ -143,10 +143,10 @@ public function redirect( * Redirect to an external link after all post dispatch plugins finish. * * @param string $url The URL to redirect to - * @param int $code Redirect code to use (default 302) + * @param ResponseCode $code Redirect code to use (default 302) * @throws NotFoundException */ - public function externalRedirect(string $url, int $code = ResponseCode::HTTP_CODE_302): void + public function externalRedirect(string $url, ResponseCode $code = ResponseCode::HTTP_CODE_302): void { $response = $this->di->get(ResponseInterface::class); $response->redirect($url, $code); diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index 9907fd7..b31f3b8 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -48,7 +48,7 @@ abstract class HttpRequest implements HttpRequestInterface protected ?string $username = null; protected ?string $password = null; protected ?Response $response = null; - protected ?int $responseCode = null; + protected ?ResponseCode $responseCode = null; protected string $userAgent; protected ?string $referer = null; protected ?string $contentType = null; @@ -425,7 +425,13 @@ protected function parseResponseHeaders(array $responseHeaders): void return; } $status = explode(' ', $responseHeaders[0]); - $this->responseCode = !empty($status[1]) ? (int)$status[1] : ResponseCode::HTTP_CODE_500; + $responseCode = !empty($status[1]) ? (int)$status[1] : ResponseCode::HTTP_CODE_500->value; + /** + * @var ResponseCode + * @psalm-suppress UndefinedMethod + */ + $this->responseCode = ResponseCode::tryFrom($responseCode) ?? ResponseCode::HTTP_CODE_500; + foreach ($responseHeaders as $header) { $headerData = explode(': ', $header); if (strtolower($headerData[0]) === 'set-cookie') { @@ -541,9 +547,9 @@ public function getReferer(): ?string /** * Get the http response code for the request. * - * @return int|null + * @return ResponseCode|null */ - public function getResponseCode(): ?int + public function getResponseCode(): ?ResponseCode { return $this->responseCode; } diff --git a/HttpRequest/Response.php b/HttpRequest/Response.php index 51e4dcb..9b31fd0 100644 --- a/HttpRequest/Response.php +++ b/HttpRequest/Response.php @@ -21,12 +21,13 @@ namespace Feast\HttpRequest; use Exception; +use Feast\Enums\ResponseCode; use SimpleXMLElement; class Response { - public function __construct(protected string $rawResponse, protected int $responseCode) + public function __construct(protected string $rawResponse, protected ResponseCode $responseCode) { } diff --git a/Interfaces/HttpRequestInterface.php b/Interfaces/HttpRequestInterface.php index 8969d95..5323c99 100644 --- a/Interfaces/HttpRequestInterface.php +++ b/Interfaces/HttpRequestInterface.php @@ -21,6 +21,7 @@ namespace Feast\Interfaces; use CurlHandle; +use Feast\Enums\ResponseCode; use Feast\Exception\ServerFailureException; use Feast\HttpRequest\HttpRequest; use Feast\HttpRequest\Response; @@ -42,9 +43,9 @@ public function addHeader(string $header, string $value): HttpRequestInterface; /** * Get the http response code for the request. * - * @return int|null + * @return ResponseCode|null */ - public function getResponseCode(): ?int; + public function getResponseCode(): ?ResponseCode; /** * Get cookies from the request. diff --git a/Interfaces/ResponseInterface.php b/Interfaces/ResponseInterface.php index b31aeab..242c15a 100644 --- a/Interfaces/ResponseInterface.php +++ b/Interfaces/ResponseInterface.php @@ -21,6 +21,7 @@ namespace Feast\Interfaces; use Exception; +use Feast\Enums\ResponseCode; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\View; @@ -34,10 +35,10 @@ interface ResponseInterface extends ServiceContainerItemInterface /** * Set the response code. * - * @param int $responseCode + * @param ResponseCode $responseCode * @throws Exception */ - public function setResponseCode(int $responseCode): void; + public function setResponseCode(ResponseCode $responseCode): void; /** * Send http response header. @@ -71,9 +72,9 @@ public function setJson(bool $isJson = true): void; * Set redirect path. * * @param string $path - * @param int $code + * @param ResponseCode $code */ - public function redirect(string $path, int $code = 302): void; + public function redirect(string $path, ResponseCode $code = ResponseCode::HTTP_CODE_302): void; /** * Get the redirect path for a redirect. diff --git a/Main.php b/Main.php index 3528642..efa975f 100644 --- a/Main.php +++ b/Main.php @@ -454,7 +454,7 @@ protected function handle404Exception( /** @var string|null $errorUrl */ $errorUrl = $config->getSetting('error.http404.url', 'error/fourohfour'); if (is_string($errorUrl)) { - header('Location:/' . $errorUrl, true, ResponseCode::HTTP_CODE_302); + header('Location:/' . $errorUrl, true, ResponseCode::HTTP_CODE_302->value); return; } throw $exception; diff --git a/Response.php b/Response.php index 3eecd28..3549dbd 100644 --- a/Response.php +++ b/Response.php @@ -21,7 +21,6 @@ namespace Feast; use Feast\Enums\ResponseCode; -use Feast\Exception\ResponseException; use Feast\Interfaces\ResponseInterface; use Feast\Interfaces\RouterInterface; use Feast\ServiceContainer\ServiceContainerItemInterface; @@ -36,7 +35,7 @@ class Response implements ServiceContainerItemInterface, ResponseInterface { use DependencyInjected; - private int $responseCode = ResponseCode::HTTP_CODE_200; + private ResponseCode $responseCode = ResponseCode::HTTP_CODE_200; private bool $isJson = false; private object|null $jsonResponse = null; private ?string $redirectPath = null; @@ -52,16 +51,12 @@ public function __construct() /** * Set the response code. * - * @param int $responseCode - * @throws ResponseException + * @param ResponseCode $responseCode */ - public function setResponseCode(int $responseCode): void + public function setResponseCode(ResponseCode $responseCode): void { - if (ResponseCode::isValidResponseCode($responseCode)) { - $this->responseCode = $responseCode; - } else { - throw new ResponseException('Invalid response code!'); - } + $this->responseCode = $responseCode; + } /** @@ -69,7 +64,8 @@ public function setResponseCode(int $responseCode): void */ public function sendResponseCode(): void { - http_response_code($this->responseCode); + /** @psalm-suppress UndefinedPropertyFetch */ + http_response_code((int)$this->responseCode->value); } /** @@ -140,10 +136,9 @@ public function getRedirectPath(): ?string * Set redirect path. * * @param string $path - * @param int $code - * @throws ResponseException + * @param ResponseCode $code */ - public function redirect(string $path, int $code = 302): void + public function redirect(string $path, ResponseCode $code = ResponseCode::HTTP_CODE_302): void { $this->redirectPath = $path; $this->setResponseCode($code); diff --git a/composer.json b/composer.json index 5efbcd8..559f049 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,11 @@ }, "suggest": { "ext-mysql": "*", - "ext-pdo_mysql": "*" + "ext-pdo_mysql": "*", + "ext-curl": "*" }, "require-dev": { - "psalm/phar": "^4.7" + "psalm/phar": "^4.10" }, "autoload": { "psr-4": { From 62031ab48629e56c8d8b68427460f1e6d8781ed1 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 21 Oct 2021 19:22:58 -0400 Subject: [PATCH 03/56] RequestMethod enums --- Attributes/Path.php | 2 +- Enums/RequestMethod.php | 12 ++++++------ FeastTests/Attributes/PathTest.php | 17 +++++++++-------- FeastTests/HttpRequest/CurlTest.php | 18 +++++++++--------- FeastTests/HttpRequest/SimpleTest.php | 6 +++--- HttpRequest/HttpRequest.php | 10 +++++----- Interfaces/RouterInterface.php | 1 + Request.php | 10 +++++----- Router/Router.php | 5 +++-- 9 files changed, 42 insertions(+), 39 deletions(-) diff --git a/Attributes/Path.php b/Attributes/Path.php index eed982a..7d29f21 100644 --- a/Attributes/Path.php +++ b/Attributes/Path.php @@ -49,7 +49,7 @@ public function __construct( } /** - * @return array + * @return array */ public function getMethods(): array { diff --git a/Enums/RequestMethod.php b/Enums/RequestMethod.php index 0ed5461..26e2b09 100644 --- a/Enums/RequestMethod.php +++ b/Enums/RequestMethod.php @@ -20,11 +20,11 @@ namespace Feast\Enums; -class RequestMethod +enum RequestMethod: string { - public const GET = 'GET'; - public const POST = 'POST'; - public const PUT = 'PUT'; - public const PATCH = 'PATCH'; - public const DELETE = 'DELETE'; + case GET = 'GET'; + case POST = 'POST'; + case PUT = 'PUT'; + case PATCH = 'PATCH'; + case DELETE = 'DELETE'; } diff --git a/FeastTests/Attributes/PathTest.php b/FeastTests/Attributes/PathTest.php index 3264e8a..e35b88c 100644 --- a/FeastTests/Attributes/PathTest.php +++ b/FeastTests/Attributes/PathTest.php @@ -21,6 +21,7 @@ namespace Attributes; use Feast\Attributes\Path; +use Feast\Enums\RequestMethod; use PHPUnit\Framework\TestCase; class PathTest extends TestCase @@ -28,42 +29,42 @@ class PathTest extends TestCase public function testGetMethodGetOnly(): void { $path = new Path('/index', 'index', Path::METHOD_GET); - $this->assertEquals(['GET'], $path->getMethods()); + $this->assertEquals([RequestMethod::GET], $path->getMethods()); } public function testGetMethodPostOnly(): void { $path = new Path('/index', 'index', Path::METHOD_POST); - $this->assertEquals(['POST'], $path->getMethods()); + $this->assertEquals([RequestMethod::POST], $path->getMethods()); } public function testGetMethodPutOnly(): void { $path = new Path('/index', 'index', Path::METHOD_PUT); - $this->assertEquals(['PUT'], $path->getMethods()); + $this->assertEquals([RequestMethod::PUT], $path->getMethods()); } public function testGetMethodDeleteOnly(): void { $path = new Path('/index', 'index', Path::METHOD_DELETE); - $this->assertEquals(['DELETE'], $path->getMethods()); + $this->assertEquals([RequestMethod::DELETE], $path->getMethods()); } public function testGetMethodPatchOnly(): void { $path = new Path('/index', 'index', Path::METHOD_PATCH); - $this->assertEquals(['PATCH'], $path->getMethods()); + $this->assertEquals([RequestMethod::PATCH], $path->getMethods()); } public function testGetMethodGetAndPost(): void { $path = new Path('/index', 'index', Path::METHOD_GET | Path::METHOD_POST); - $this->assertEquals(['GET', 'POST'], $path->getMethods()); + $this->assertEquals([RequestMethod::GET, RequestMethod::POST], $path->getMethods()); } public function testGetMethodAll(): void { $path = new Path('/index', 'index', Path::METHOD_ALL); - $this->assertEquals(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], $path->getMethods()); + $this->assertEquals([RequestMethod::GET, RequestMethod::POST, RequestMethod::PUT, RequestMethod::DELETE, RequestMethod::PATCH], $path->getMethods()); } -} \ No newline at end of file +} diff --git a/FeastTests/HttpRequest/CurlTest.php b/FeastTests/HttpRequest/CurlTest.php index a115bbe..7e62ffe 100644 --- a/FeastTests/HttpRequest/CurlTest.php +++ b/FeastTests/HttpRequest/CurlTest.php @@ -33,7 +33,7 @@ public function testGet(): void { $request = new Curl(); $request->get('http://www.google.com/'); - $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('GET', $request->getMethod()->value); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); /** THIS METHOD IS EXPECTED TO FAIL WITH AN EXCEPTION BECAUSE WE ARE MOCKING THE CURL ITEM WITH stdClass */ $this->expectException(\TypeError::class); @@ -44,7 +44,7 @@ public function testPatch(): void { $request = new Curl(); $request->patch('http://www.google.com/'); - $this->assertEquals('PATCH', $request->getMethod()); + $this->assertEquals('PATCH', $request->getMethod()->value); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -73,7 +73,7 @@ public function testPost(): void { $request = new Curl(); $request->post('http://www.google.com/'); - $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('POST', $request->getMethod()->value); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -81,7 +81,7 @@ public function testPostJson(): void { $request = new Curl(); $request->postJson('http://www.google.com/'); - $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('POST', $request->getMethod()->value); $this->assertEquals(HttpRequest::CONTENT_TYPE_JSON, $request->getContentType()); } @@ -89,7 +89,7 @@ public function testPut(): void { $request = new Curl(); $request->put('http://www.google.com/'); - $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('PUT', $request->getMethod()->value); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -97,7 +97,7 @@ public function testDelete(): void { $request = new Curl(); $request->delete('http://www.google.com/'); - $this->assertEquals('DELETE', $request->getMethod()); + $this->assertEquals('DELETE', $request->getMethod()->value); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -362,7 +362,7 @@ public function testAddArgumentsPostJson(): void $this->assertIsArray($arguments['type']); $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); - $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('POST', $request->getMethod()->value); $request->makeRequest(); } @@ -382,7 +382,7 @@ public function testAddArgumentsPutJson(): void $this->assertIsArray($arguments['type']); $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); - $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('PUT', $request->getMethod()->value); $request->makeRequest(); } @@ -402,7 +402,7 @@ public function testAddArgumentsPatchJson(): void $this->assertIsArray($arguments['type']); $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); - $this->assertEquals('PATCH', $request->getMethod()); + $this->assertEquals('PATCH', $request->getMethod()->value); $request->makeRequest(); } diff --git a/FeastTests/HttpRequest/SimpleTest.php b/FeastTests/HttpRequest/SimpleTest.php index 28f7a8f..a9fc935 100644 --- a/FeastTests/HttpRequest/SimpleTest.php +++ b/FeastTests/HttpRequest/SimpleTest.php @@ -160,7 +160,7 @@ public function testAddArgumentsPostJson(): void $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); $request->makeRequest(); - $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('POST', $request->getMethod()->value); } public function testPut(): void @@ -175,7 +175,7 @@ public function testPut(): void ); $this->assertTrue($request->getUrl() === 'https://www.google.com/'); - $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('PUT', $request->getMethod()->value); $request->makeRequest(); } @@ -191,7 +191,7 @@ public function testPatch(): void ); $this->assertTrue($request->getUrl() === 'https://www.google.com/'); - $this->assertEquals('PATCH', $request->getMethod()); + $this->assertEquals('PATCH', $request->getMethod()->value); $request->makeRequest(); } diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index b31f3b8..6d66879 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -39,7 +39,7 @@ abstract class HttpRequest implements HttpRequestInterface public const CONTENT_TYPE_JSON = 'application/json'; public const DEFAULT_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) FeastFramework/1.0'; - protected string $method; + protected RequestMethod $method; /** @var array $cookies */ protected array $cookies; protected array $arguments; @@ -144,9 +144,9 @@ public function delete(string $url): HttpRequestInterface /** * Set request method. * - * @param string $method + * @param RequestMethod $method */ - private function setMethod(string $method): void + private function setMethod(RequestMethod $method): void { $this->contentType = null; $this->arguments = []; @@ -294,9 +294,9 @@ public function getUrl(): string /** * Get request method. * - * @return string + * @return RequestMethod */ - public function getMethod(): string + public function getMethod(): RequestMethod { return $this->method; } diff --git a/Interfaces/RouterInterface.php b/Interfaces/RouterInterface.php index 5f1f2ab..a538a9b 100644 --- a/Interfaces/RouterInterface.php +++ b/Interfaces/RouterInterface.php @@ -22,6 +22,7 @@ use Exception; use Feast\Attributes\Path; +use Feast\Enums\RequestMethod; use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; diff --git a/Request.php b/Request.php index 66f60cc..66bdabc 100755 --- a/Request.php +++ b/Request.php @@ -206,7 +206,7 @@ public function getAllArguments(): \stdClass */ public function isPost(): bool { - return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::POST; + return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::POST->value; } /** @@ -216,7 +216,7 @@ public function isPost(): bool */ public function isGet(): bool { - return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::GET; + return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::GET->value; } /** @@ -226,7 +226,7 @@ public function isGet(): bool */ public function isDelete(): bool { - return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::DELETE; + return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::DELETE->value; } /** @@ -236,7 +236,7 @@ public function isDelete(): bool */ public function isPut(): bool { - return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::PUT; + return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::PUT->value; } /** @@ -246,7 +246,7 @@ public function isPut(): bool */ public function isPatch(): bool { - return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::PATCH; + return !empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === RequestMethod::PATCH->value; } /** diff --git a/Router/Router.php b/Router/Router.php index 1911fa1..1628a43 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -862,13 +862,14 @@ private function analyzeMethodForRoute(ReflectionMethod $method, string $classNa /** @var Path $pathAttribute */ $pathAttribute = $attribute->newInstance(); foreach ($pathAttribute->getMethods() as $methodType) { + /** @psalm-suppress UndefinedPropertyFetch */ $this->addRoute( $pathAttribute->path, $className, $methodName, $pathAttribute->name, $pathAttribute->defaults, - $methodType, + (string)$methodType->value, $module ); } @@ -897,7 +898,7 @@ private function getFilteredMethodName(string $methodName): string */ protected function getCurrentRequestMethod(): string { - return !empty($_SERVER['REQUEST_METHOD']) ? (string)$_SERVER['REQUEST_METHOD'] : RequestMethod::GET; + return !empty($_SERVER['REQUEST_METHOD']) ? (string)$_SERVER['REQUEST_METHOD'] : RequestMethod::GET->value; } } From a461c245e2255c0905faf4b2b245fd55068821c5 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 21 Oct 2021 19:41:06 -0400 Subject: [PATCH 04/56] Update tests and fix bugs - 100% code coverage --- FeastTests/Database/DatabaseTest.php | 4 ++-- FeastTests/Logger/LoggerTest.php | 2 +- FeastTests/ResponseTest.php | 9 +++++++++ Logger/Logger.php | 3 ++- Traits/Collection.php | 8 +------- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/FeastTests/Database/DatabaseTest.php b/FeastTests/Database/DatabaseTest.php index b335d4c..db7a501 100644 --- a/FeastTests/Database/DatabaseTest.php +++ b/FeastTests/Database/DatabaseTest.php @@ -105,8 +105,8 @@ public function testInstantiationUnknownDbClass(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL; - $this->expectException(\TypeError::class); + $details->connectionType = DatabaseType::MYSQL->value; + $this->expectException(InvalidOptionException::class); new Database($details, \stdClass::class); } diff --git a/FeastTests/Logger/LoggerTest.php b/FeastTests/Logger/LoggerTest.php index abeb021..66855a2 100644 --- a/FeastTests/Logger/LoggerTest.php +++ b/FeastTests/Logger/LoggerTest.php @@ -244,6 +244,6 @@ public function testRawLogHigherLevel(): void $logger = new Logger($config, Main::RUN_AS_CLI); $logger->rawLog(LogLevelCode::ALERT->value, 'you noticing me'); $output = $this->getActualOutputForAssertion(); - $this->assertStringNotContainsString('NOTICE: you noticing me', $output); + $this->assertStringNotContainsString('you noticing me', $output); } } diff --git a/FeastTests/ResponseTest.php b/FeastTests/ResponseTest.php index ffdd072..d5c9aba 100644 --- a/FeastTests/ResponseTest.php +++ b/FeastTests/ResponseTest.php @@ -141,4 +141,13 @@ public function testRedirect(): void $this->assertEquals('/redirecting', $response->getRedirectPath()); } + public function testIsValidResponseCodeTrue(): void + { + $this->assertTrue(ResponseCode::isValidResponseCode(200)); + } + + public function testIsValidResponseCodeFalse(): void + { + $this->assertFalse(ResponseCode::isValidResponseCode(2000)); + } } diff --git a/Logger/Logger.php b/Logger/Logger.php index 3ab41c2..3775d86 100644 --- a/Logger/Logger.php +++ b/Logger/Logger.php @@ -204,7 +204,8 @@ protected function interpolateContext(Stringable|string $message, array $context */ public function rawLog(int $level, string $message): void { - if ($level < $this->logLevel) { + /** @psalm-suppress UndefinedPropertyFetch */ + if ($level < $this->logLevel->value) { return; } $fileName = self::LOG_DIR . 'feast.log'; diff --git a/Traits/Collection.php b/Traits/Collection.php index 7bb69d5..49498e4 100644 --- a/Traits/Collection.php +++ b/Traits/Collection.php @@ -149,13 +149,7 @@ public function objectSort( 'Collection must contain objects of a named class in order to use objectSort' ); } - switch ($sortType) { - case CollectionSort::VALUE: - case CollectionSort::VALUE_REVERSE: - break; - default: - throw new InvalidOptionException('Invalid sort option'); - } + /** @var array $array */ $array = $this->array; usort( From d66d796d81148673d2ee1a3e2a7b9898c1617271 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 21 Oct 2021 20:50:36 -0400 Subject: [PATCH 05/56] Property initializer in constructor call, minor refactors --- Binary.php | 10 +++++----- CliArguments.php | 2 +- CliController.php | 4 +--- Config/Config.php | 3 ++- Database/DatabaseDetails.php | 6 ++---- FeastTests/IdentityTest.php | 6 +++--- Install/container.php | 2 +- Session/Identity.php | 2 +- Terminal.php | 2 +- 9 files changed, 17 insertions(+), 20 deletions(-) diff --git a/Binary.php b/Binary.php index 3e66fa8..650ba5a 100644 --- a/Binary.php +++ b/Binary.php @@ -237,19 +237,19 @@ private function processCliMethods(\ReflectionMethod $method): bool /** * Process custom actions built into Feast. Defaults to all classes. * - * @param array|null $classes + * @param array $classes * @throws ReflectionException */ - private function analyzeFeast(array $classes = null): void - { - $classes ??= [ + private function analyzeFeast( + array $classes = [ CreateController::class, MigrationController::class, CacheController::class, JobController::class, MaintenanceController::class, ServeController::class - ]; + ] + ): void { /** @var class-string $class */ foreach ($classes as $class) { $this->processCliClass(new \ReflectionClass($class)); diff --git a/CliArguments.php b/CliArguments.php index 7a9295b..5241283 100644 --- a/CliArguments.php +++ b/CliArguments.php @@ -25,7 +25,7 @@ class CliArguments implements ServiceContainerItemInterface { - public function __construct(protected array $arguments) + public function __construct(protected array $arguments = []) { } diff --git a/CliController.php b/CliController.php index e0188a4..e59c681 100755 --- a/CliController.php +++ b/CliController.php @@ -27,15 +27,13 @@ abstract class CliController implements ControllerInterface { protected Terminal $terminal; - protected CliArguments $cliArguments; public function __construct( ServiceContainer $di, ?ConfigInterface $config = null, - ?CliArguments $cliArguments = null + protected CliArguments $cliArguments = new CliArguments() ) { $config ??= $di->get(ConfigInterface::INTERFACE_NAME); - $this->cliArguments = $cliArguments ?? new CliArguments([]); /** @var bool|null $setting */ $setting = $config->getSetting('ttycolor', null); $this->terminal = new Terminal($setting); diff --git a/Config/Config.php b/Config/Config.php index 645872f..b6b2356 100755 --- a/Config/Config.php +++ b/Config/Config.php @@ -24,6 +24,7 @@ use Feast\Exception\ServerFailureException; use Feast\Interfaces\ConfigInterface; use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; use stdClass; @@ -44,7 +45,7 @@ class Config implements ServiceContainerItemInterface, ConfigInterface * * @param bool $pullFromContainer - True to check if already in service container * @param string|null $overriddenEnvironment - if set, the environment will be the one passed in - * @throws ServerFailureException|ContainerException + * @throws ServerFailureException|ContainerException|NotFoundException */ public function __construct(bool $pullFromContainer = true, string $overriddenEnvironment = null) { diff --git a/Database/DatabaseDetails.php b/Database/DatabaseDetails.php index 9c1417f..91ffd05 100644 --- a/Database/DatabaseDetails.php +++ b/Database/DatabaseDetails.php @@ -32,11 +32,9 @@ class DatabaseDetails implements ServiceContainerItemInterface, DatabaseDetailsI /** @var array> */ protected array $databaseDataTypes = []; - protected ?DatabaseFactoryInterface $dbFactory; - public function __construct(DatabaseFactoryInterface $dbFactory) + public function __construct(protected DatabaseFactoryInterface $dbFactory) { - $this->dbFactory = $dbFactory; $this->checkInjected(); } @@ -79,7 +77,7 @@ public function getDataTypesForTable(string $table, string $connection = 'Defaul protected function buildDetailsForTable(string $table, string $connection): void { - $dbFactory = $this->dbFactory ?? di(DatabaseFactoryInterface::class); + $dbFactory = $this->dbFactory; $connection = $dbFactory->getConnection($connection); $details = $connection->getDescribedTable($table); $tableType = []; diff --git a/FeastTests/IdentityTest.php b/FeastTests/IdentityTest.php index 2543628..312b2d2 100644 --- a/FeastTests/IdentityTest.php +++ b/FeastTests/IdentityTest.php @@ -40,7 +40,7 @@ public function testGetUser(): void $config = $this->createStub(ConfigInterface::class); $config->method('getSetting')->willReturn('test'); - $identity = new Identity($config, $session); + $identity = new Identity($session); $user = $identity->getUser(); $this->assertEquals('testUser', $user->user); } @@ -58,7 +58,7 @@ public function testSaveUser(): void $testUser = new MockUser(); $testUser->user = 'testUser'; $namespace->identity = $testUser; - $identity = new Identity($config, $session); + $identity = new Identity($session); $user = $identity->getUser(); $this->assertEquals(null, $user); @@ -80,7 +80,7 @@ public function testDestroyUser(): void $testUser = new MockUser(); $testUser->user = 'testUser'; - $identity = new Identity($config, $session); + $identity = new Identity($session); $identity->saveUser($testUser); $user = $identity->getUser(); diff --git a/Install/container.php b/Install/container.php index 1cc88a2..3d54f20 100644 --- a/Install/container.php +++ b/Install/container.php @@ -137,7 +137,7 @@ if (RUN_AS === Main::RUN_AS_WEBAPP) { $session = new Session($config); $container->add(Session::class, $session); - $container->add(Identity::class, new Identity($config, $session)); + $container->add(Identity::class, new Identity($session)); } else { $container->add(CliArguments::class, new CliArguments($argv)); } diff --git a/Session/Identity.php b/Session/Identity.php index c3a6b6e..931ed99 100755 --- a/Session/Identity.php +++ b/Session/Identity.php @@ -34,7 +34,7 @@ class Identity implements ServiceContainerItemInterface protected \stdClass $me; - public function __construct(ConfigInterface $config, protected Session $session) + public function __construct(protected Session $session) { $this->checkInjected(); $this->me = $session->getNamespace('Feast_Login'); diff --git a/Terminal.php b/Terminal.php index 1d1d7b2..9390b47 100644 --- a/Terminal.php +++ b/Terminal.php @@ -32,7 +32,7 @@ class Terminal public function __construct(?bool $useColor = null) { if ($useColor === null) { - $this->isTty = (function_exists('posix_isatty') ? posix_isatty(STDOUT) : false); + $this->isTty = (function_exists('posix_isatty') && posix_isatty(STDOUT)); } else { $this->isTty = $useColor; } From 6d31dcdf1726c88b5429b921ba3757008d96795a Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 21 Oct 2021 21:02:58 -0400 Subject: [PATCH 06/56] Final constants --- Attributes/Path.php | 12 ++--- BaseMapper.php | 2 +- Controllers/MigrationController.php | 2 +- Date.php | 58 ++++++++++++------------- Form/Field.php | 4 +- Form/Form.php | 4 -- Form/Label.php | 4 +- Interfaces/ConfigInterface.php | 2 +- Interfaces/DatabaseDetailsInterface.php | 2 +- Interfaces/DatabaseFactoryInterface.php | 4 +- Interfaces/LoggerInterface.php | 2 +- Interfaces/ProfilerInterface.php | 2 +- Interfaces/RequestInterface.php | 2 +- Interfaces/ResponseInterface.php | 2 +- Interfaces/RouterInterface.php | 2 +- Jobs/QueueableJob.php | 8 ++-- Logger/Logger.php | 4 +- Main.php | 4 +- Psr/Log/LogLevel.php | 16 +++---- 19 files changed, 66 insertions(+), 70 deletions(-) diff --git a/Attributes/Path.php b/Attributes/Path.php index 7d29f21..930ddb6 100644 --- a/Attributes/Path.php +++ b/Attributes/Path.php @@ -26,12 +26,12 @@ #[Attribute(Attribute::TARGET_METHOD)] class Path { - public const METHOD_GET = 1; - public const METHOD_POST = 2; - public const METHOD_PUT = 4; - public const METHOD_DELETE = 8; - public const METHOD_PATCH = 16; - public const METHOD_ALL = self::METHOD_GET | self::METHOD_POST | self::METHOD_PUT | self::METHOD_DELETE | self::METHOD_PATCH; + final public const METHOD_GET = 1; + final public const METHOD_POST = 2; + final public const METHOD_PUT = 4; + final public const METHOD_DELETE = 8; + final public const METHOD_PATCH = 16; + final public const METHOD_ALL = self::METHOD_GET | self::METHOD_POST | self::METHOD_PUT | self::METHOD_DELETE | self::METHOD_PATCH; /** * Path constructor. diff --git a/BaseMapper.php b/BaseMapper.php index c522347..85b12aa 100644 --- a/BaseMapper.php +++ b/BaseMapper.php @@ -39,7 +39,7 @@ abstract class BaseMapper protected const PRIMARY_KEY = null; protected const OBJECT_NAME = null; public const CONNECTION = 'default'; - public const NOT_NULL = 'not_null'; + final public const NOT_NULL = 'not_null'; protected DatabaseInterface $connection; public function __construct() diff --git a/Controllers/MigrationController.php b/Controllers/MigrationController.php index 89ea436..f95241c 100644 --- a/Controllers/MigrationController.php +++ b/Controllers/MigrationController.php @@ -31,7 +31,7 @@ class MigrationController extends CliController { - protected const MIGRATION_TABLE_MIGRATION = '1_migrations'; + final protected const MIGRATION_TABLE_MIGRATION = '1_migrations'; /** @var array $migrationsByName */ protected array $migrationsByName = []; /** @var array $migrationsByNumber */ diff --git a/Date.php b/Date.php index 31e699e..6876778 100755 --- a/Date.php +++ b/Date.php @@ -29,35 +29,35 @@ class Date { - public const FORMAT_YMD_WITH_SLASHES = 'Y/m/d'; - public const FORMAT_YMD_HMS_WITH_SLASHES = 'Y/m/d H:i:s'; - public const FORMAT_YMD_HM_WITH_SLASHES = 'Y/m/d H:i'; - public const FORMAT_YMD_WITH_DASHES = 'Y-m-d'; - public const FORMAT_YMD_HMS_WITH_DASHES = 'Y-m-d H:i:s'; - public const FORMAT_YMD_HM_WITH_DASHES = 'Y-m-d H:i'; - public const FORMAT_MDY_WITH_SLASHES = 'm/d/Y'; - public const FORMAT_MDY_HMS_WITH_SLASHES = 'm/d/Y H:i:s'; - public const FORMAT_MDY_HM_WITH_SLASHES = 'm/d/Y H:i'; - public const FORMAT_MDY_WITH_DASHES = 'm-d-Y'; - public const FORMAT_MDY_HMS_WITH_DASHES = 'm-d-Y H:i:s'; - public const FORMAT_MDY_HM_WITH_DASHES = 'm-d-y H:i'; - public const FORMAT_MDY_TEXT_FORMAT_WITHOUT_SUFFIX = 'F j, Y'; - public const FORMAT_MDY_TEXT_FORMAT_WITH_SUFFIX = 'F jS, Y'; - public const FORMAT_HMS = 'H:i:s'; - public const FORMAT_HM = 'H:i'; - public const FORMAT_UNIXTIMESTAMP = 'U'; - public const ATOM = "Y-m-d\TH:i:sP"; - public const COOKIE = "l, d-M-Y H:i:s T"; - public const ISO8601 = "Y-m-d\TH:i:sO"; - public const RFC822 = "D, d M y H:i:s O"; - public const RFC850 = "l, d-M-y H:i:s T"; - public const RFC1036 = "D, d M y H:i:s O"; - public const RFC1123 = "D, d M Y H:i:s O"; - public const RFC2822 = "D, d M Y H:i:s O"; - public const RFC3339 = "Y-m-d\TH:i:sP"; - public const RFC3339_EXTENDED = "Y-m-d\TH:i:s.vP"; - public const RSS = "D, d M Y H:i:s O"; - public const W3C = "Y-m-d\TH:i:sP"; + final public const FORMAT_YMD_WITH_SLASHES = 'Y/m/d'; + final public const FORMAT_YMD_HMS_WITH_SLASHES = 'Y/m/d H:i:s'; + final public const FORMAT_YMD_HM_WITH_SLASHES = 'Y/m/d H:i'; + final public const FORMAT_YMD_WITH_DASHES = 'Y-m-d'; + final public const FORMAT_YMD_HMS_WITH_DASHES = 'Y-m-d H:i:s'; + final public const FORMAT_YMD_HM_WITH_DASHES = 'Y-m-d H:i'; + final public const FORMAT_MDY_WITH_SLASHES = 'm/d/Y'; + final public const FORMAT_MDY_HMS_WITH_SLASHES = 'm/d/Y H:i:s'; + final public const FORMAT_MDY_HM_WITH_SLASHES = 'm/d/Y H:i'; + final public const FORMAT_MDY_WITH_DASHES = 'm-d-Y'; + final public const FORMAT_MDY_HMS_WITH_DASHES = 'm-d-Y H:i:s'; + final public const FORMAT_MDY_HM_WITH_DASHES = 'm-d-y H:i'; + final public const FORMAT_MDY_TEXT_FORMAT_WITHOUT_SUFFIX = 'F j, Y'; + final public const FORMAT_MDY_TEXT_FORMAT_WITH_SUFFIX = 'F jS, Y'; + final public const FORMAT_HMS = 'H:i:s'; + final public const FORMAT_HM = 'H:i'; + final public const FORMAT_UNIXTIMESTAMP = 'U'; + final public const ATOM = "Y-m-d\TH:i:sP"; + final public const COOKIE = "l, d-M-Y H:i:s T"; + final public const ISO8601 = "Y-m-d\TH:i:sO"; + final public const RFC822 = "D, d M y H:i:s O"; + final public const RFC850 = "l, d-M-y H:i:s T"; + final public const RFC1036 = "D, d M y H:i:s O"; + final public const RFC1123 = "D, d M Y H:i:s O"; + final public const RFC2822 = "D, d M Y H:i:s O"; + final public const RFC3339 = "Y-m-d\TH:i:sP"; + final public const RFC3339_EXTENDED = "Y-m-d\TH:i:s.vP"; + final public const RSS = "D, d M Y H:i:s O"; + final public const W3C = "Y-m-d\TH:i:sP"; private int $timestamp; protected ?string $timezone = null; diff --git a/Form/Field.php b/Form/Field.php index 90126d6..0a86346 100644 --- a/Form/Field.php +++ b/Form/Field.php @@ -35,8 +35,8 @@ abstract class Field { - public const LABEL_POSITION_FIRST = 'first'; - public const LABEL_POSITION_LAST = 'last'; + final public const LABEL_POSITION_FIRST = 'first'; + final public const LABEL_POSITION_LAST = 'last'; public string $class = ''; public ?string $default = null; diff --git a/Form/Form.php b/Form/Form.php index 06b7bf3..67ac115 100644 --- a/Form/Form.php +++ b/Form/Form.php @@ -35,10 +35,6 @@ abstract class Form public const ERROR_NOT_SET = 'Required'; - public const FIELD_SELECT = 'select'; - public const FIELD_RADIO = 'radio'; - public const FIELD_CHECKBOX = 'checkbox'; - public const FIELD_TEXTAREA = 'textarea'; /** * @param string $name diff --git a/Form/Label.php b/Form/Label.php index 689e3ab..91df1e0 100755 --- a/Form/Label.php +++ b/Form/Label.php @@ -23,8 +23,8 @@ class Label { - public const LABEL_POSITION_FIRST = 'first'; - public const LABEL_POSITION_LAST = 'last'; + final public const LABEL_POSITION_FIRST = 'first'; + final public const LABEL_POSITION_LAST = 'last'; public function __construct( public string $text = '', diff --git a/Interfaces/ConfigInterface.php b/Interfaces/ConfigInterface.php index 2936019..30aab73 100644 --- a/Interfaces/ConfigInterface.php +++ b/Interfaces/ConfigInterface.php @@ -28,7 +28,7 @@ */ interface ConfigInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; /** * Cache the config and store on disk diff --git a/Interfaces/DatabaseDetailsInterface.php b/Interfaces/DatabaseDetailsInterface.php index 5b5e5d5..a4a3ce8 100644 --- a/Interfaces/DatabaseDetailsInterface.php +++ b/Interfaces/DatabaseDetailsInterface.php @@ -22,7 +22,7 @@ interface DatabaseDetailsInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; public function cache(): void; diff --git a/Interfaces/DatabaseFactoryInterface.php b/Interfaces/DatabaseFactoryInterface.php index ed52b81..8a62003 100644 --- a/Interfaces/DatabaseFactoryInterface.php +++ b/Interfaces/DatabaseFactoryInterface.php @@ -30,9 +30,9 @@ */ interface DatabaseFactoryInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; - public const DEFAULT_CONNECTION = 'default'; + final public const DEFAULT_CONNECTION = 'default'; /** * Get the specified connection diff --git a/Interfaces/LoggerInterface.php b/Interfaces/LoggerInterface.php index a664d96..ef0d36a 100644 --- a/Interfaces/LoggerInterface.php +++ b/Interfaces/LoggerInterface.php @@ -25,7 +25,7 @@ interface LoggerInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; /** * System is unusable. diff --git a/Interfaces/ProfilerInterface.php b/Interfaces/ProfilerInterface.php index 09f093e..4e2f18c 100644 --- a/Interfaces/ProfilerInterface.php +++ b/Interfaces/ProfilerInterface.php @@ -29,7 +29,7 @@ */ interface ProfilerInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; /** * Return the total time of execution up to this point. diff --git a/Interfaces/RequestInterface.php b/Interfaces/RequestInterface.php index f91492f..1fae22b 100644 --- a/Interfaces/RequestInterface.php +++ b/Interfaces/RequestInterface.php @@ -25,7 +25,7 @@ interface RequestInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; /** * Clear all request arguments. diff --git a/Interfaces/ResponseInterface.php b/Interfaces/ResponseInterface.php index 242c15a..2730c91 100644 --- a/Interfaces/ResponseInterface.php +++ b/Interfaces/ResponseInterface.php @@ -30,7 +30,7 @@ */ interface ResponseInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; /** * Set the response code. diff --git a/Interfaces/RouterInterface.php b/Interfaces/RouterInterface.php index a538a9b..355cd0c 100644 --- a/Interfaces/RouterInterface.php +++ b/Interfaces/RouterInterface.php @@ -28,7 +28,7 @@ interface RouterInterface extends ServiceContainerItemInterface { - public const INTERFACE_NAME = self::class; + final public const INTERFACE_NAME = self::class; /** * Update the "runAs". Needed to properly route in case of cached router. diff --git a/Jobs/QueueableJob.php b/Jobs/QueueableJob.php index 2202556..3d4dc48 100644 --- a/Jobs/QueueableJob.php +++ b/Jobs/QueueableJob.php @@ -28,10 +28,10 @@ abstract class QueueableJob implements JobInterface { - public const JOB_STATUS_PENDING = 'pending'; - public const JOB_STATUS_RUNNING = 'running'; - public const JOB_STATUS_COMPLETE = 'complete'; - public const JOB_STATUS_FAILED = 'failed'; + final public const JOB_STATUS_PENDING = 'pending'; + final public const JOB_STATUS_RUNNING = 'running'; + final public const JOB_STATUS_COMPLETE = 'complete'; + final public const JOB_STATUS_FAILED = 'failed'; protected int $maxTries = 3; protected string $queueName = 'default'; diff --git a/Logger/Logger.php b/Logger/Logger.php index 3775d86..a641794 100644 --- a/Logger/Logger.php +++ b/Logger/Logger.php @@ -36,8 +36,8 @@ class Logger implements LoggerInterface, ServiceContainerItemInterface, \Feast\I use DependencyInjected; - private const LOG_DIR = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR; - private LogLevelCode $logLevel; + protected const LOG_DIR = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR; + protected LogLevelCode $logLevel; public function __construct(private ConfigInterface $config, private string $runAs) { diff --git a/Main.php b/Main.php index efa975f..b9e1ee4 100644 --- a/Main.php +++ b/Main.php @@ -44,8 +44,8 @@ class Main implements MainInterface { - public const RUN_AS_WEBAPP = 'webapp'; - public const RUN_AS_CLI = 'cli'; + final public const RUN_AS_WEBAPP = 'webapp'; + final public const RUN_AS_CLI = 'cli'; private array $plugins = []; private ErrorLoggerInterface $logger; diff --git a/Psr/Log/LogLevel.php b/Psr/Log/LogLevel.php index 3dd5173..212afde 100644 --- a/Psr/Log/LogLevel.php +++ b/Psr/Log/LogLevel.php @@ -8,11 +8,11 @@ class LogLevel { public const EMERGENCY = 'emergency'; - public const ALERT = 'alert'; - public const CRITICAL = 'critical'; - public const ERROR = 'error'; - public const WARNING = 'warning'; - public const NOTICE = 'notice'; - public const INFO = 'info'; - public const DEBUG = 'debug'; -} \ No newline at end of file + public const ALERT = 'alert'; + public const CRITICAL = 'critical'; + public const ERROR = 'error'; + public const WARNING = 'warning'; + public const NOTICE = 'notice'; + public const INFO = 'info'; + public const DEBUG = 'debug'; +} From 18217f54a2b544fa0213c4ec6315ca988d3a5441 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 22 Oct 2021 12:38:48 -0400 Subject: [PATCH 07/56] Remove deprecated method --- Database/Database.php | 35 ++++++--------- FeastTests/Database/DatabaseFactoryTest.php | 2 + FeastTests/Database/DatabaseTest.php | 48 ++++++++++++++------- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/Database/Database.php b/Database/Database.php index f372dd1..258ef17 100644 --- a/Database/Database.php +++ b/Database/Database.php @@ -55,8 +55,19 @@ public function __construct(\stdClass $connectionDetails, string $pdoClass) * @psalm-suppress UndefinedMethod */ $this->databaseType = DatabaseType::from($connectionDetails->connectionType); - /** @psalm-suppress DeprecatedMethod (will be removed in 2.0) */ - $this->queryClass = (string)($connectionDetails->queryClass ?? $this->getQueryClass()); + $queryClass = (string)($connectionDetails->queryClass ?? ''); + if ($queryClass === '') { + throw new InvalidOptionException( + 'queryClass not passed in. Expected a class that inherits \Feast\Database\Query ' + ); + } + if (!class_exists($queryClass)) { + throw new InvalidOptionException('queryClass ' . $queryClass . ' not found.'); + } + if (!is_subclass_of($queryClass, Query::class)) { + throw new InvalidOptionException('queryClass ' . $queryClass . ' does not extend \Feast\Database\Query.'); + } + $this->queryClass = $queryClass; $options = $this->getConfigOptions($connectionDetails); // Get connection string @@ -176,26 +187,6 @@ private function startQuery(): Query return new ($this->queryClass)($this->connection); } - /** - * Get Query class from DatabaseType (Deprecated) - * - * @return string - * @throws DatabaseException - * @deprecated - */ - public function getQueryClass(): string - { - trigger_error( - 'The method ' . self::class . '::getQueryClass is deprecated. Set the queryClass option in your database config.', - E_USER_DEPRECATED - ); - return match ($this->databaseType) { - DatabaseType::MYSQL => MySQLQuery::class, - DatabaseType::SQLITE => SQLiteQuery::class, - default => throw new DatabaseException('Invalid Database Type') - }; - } - /** * Return true if transaction running, otherwise false. * diff --git a/FeastTests/Database/DatabaseFactoryTest.php b/FeastTests/Database/DatabaseFactoryTest.php index 066cefe..3e06218 100644 --- a/FeastTests/Database/DatabaseFactoryTest.php +++ b/FeastTests/Database/DatabaseFactoryTest.php @@ -23,6 +23,7 @@ use Feast\Config\Config; use Feast\Database\Database; use Feast\Database\DatabaseFactory; +use Feast\Database\MySQLQuery; use Feast\Enums\DatabaseType; use Feast\Exception\DatabaseException; use Feast\ServiceContainer\ServiceContainer; @@ -40,6 +41,7 @@ public function testGetConnection(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = DatabaseType::MYSQL->value; + $details->queryClass = MySQLQuery::class; $config = $this->createStub(Config::class); $config->method('getSetting')->willReturnMap( [ diff --git a/FeastTests/Database/DatabaseTest.php b/FeastTests/Database/DatabaseTest.php index db7a501..1978ef7 100644 --- a/FeastTests/Database/DatabaseTest.php +++ b/FeastTests/Database/DatabaseTest.php @@ -94,6 +94,7 @@ public function testInstantiationWithUrl(): void $details->name = 'Test'; $details->url = 'mysql:host=localhost;port=3306;'; $details->connectionType = DatabaseType::MYSQL->value; + $details->queryClass = MySQLQuery::class; $database = new Database($details, PDOMock::class); $this->assertTrue($database instanceof Database); } @@ -106,11 +107,12 @@ public function testInstantiationUnknownDbClass(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = DatabaseType::MYSQL->value; + $details->queryClass = MySQLQuery::class; $this->expectException(InvalidOptionException::class); new Database($details, \stdClass::class); } - public function testInstantiationWithDeprecatedMethodMySQL(): void + public function testInstantiationInvalidDbClass(): void { $details = new \stdClass(); $details->host = 'localhost'; @@ -118,11 +120,37 @@ public function testInstantiationWithDeprecatedMethodMySQL(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = DatabaseType::MYSQL->value; + $details->queryClass = 'completegibberishdefinitelynotaclass'; + $this->expectException(InvalidOptionException::class); + new Database($details, \stdClass::class); + } + + public function testInstantiationNonExtendedDbClass(): void + { + $details = new \stdClass(); + $details->host = 'localhost'; + $details->user = 'root'; + $details->pass = 'test'; + $details->name = 'Test'; + $details->connectionType = DatabaseType::MYSQL->value; + $details->queryClass = \stdClass::class; + $this->expectException(InvalidOptionException::class); + new Database($details, \stdClass::class); + } + + public function testInstantiationWithRemovedFormerlyDeprecatedMethodMySQL(): void + { + $details = new \stdClass(); + $details->host = 'localhost'; + $details->user = 'root'; + $details->pass = 'test'; + $details->name = 'Test'; + $details->connectionType = DatabaseType::MYSQL->value; + $this->expectException(InvalidOptionException::class); $database = new Database($details, PDOMock::class); - $this->assertTrue($database instanceof Database); } - public function testInstantiationWithDeprecatedMethodSqLite(): void + public function testInstantiationWithRemovedFormerlyDeprecatedMethodSqLite(): void { $details = new \stdClass(); $details->host = 'localhost'; @@ -130,8 +158,8 @@ public function testInstantiationWithDeprecatedMethodSqLite(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = DatabaseType::SQLITE->value; + $this->expectException(InvalidOptionException::class); $database = new Database($details, PDOMock::class); - $this->assertTrue($database instanceof Database); } public function testInsert(): void @@ -203,24 +231,12 @@ public function testGetDatabaseTypeMySQL(): void $this->assertEquals(DatabaseType::MYSQL, $database->getDatabaseType()); } - public function testGetQueryClassMySQL(): void - { - $database = $this->getValidConnection(); - $this->assertEquals(MySQLQuery::class, $database->getQueryClass()); - } - public function testGetDatabaseTypeSqlite(): void { $database = $this->getValidConnection(DatabaseType::SQLITE, SQLiteQuery::class); $this->assertEquals(DatabaseType::SQLITE, $database->getDatabaseType()); } - public function testGetQueryClassSqlite(): void - { - $database = $this->getValidConnection(DatabaseType::SQLITE, SQLiteQuery::class); - $this->assertEquals(SQLiteQuery::class, $database->getQueryClass()); - } - public function testDelete(): void { $database = $this->getValidConnection(); From c8c1bed906f0156abdd63ba10dbcb19a6c89dc1b Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 22 Oct 2021 12:39:06 -0400 Subject: [PATCH 08/56] Revert and fix config parameter on identity --- FeastTests/IdentityTest.php | 6 +++--- Install/container.php | 2 +- Session/Identity.php | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/FeastTests/IdentityTest.php b/FeastTests/IdentityTest.php index 312b2d2..0ab5e5c 100644 --- a/FeastTests/IdentityTest.php +++ b/FeastTests/IdentityTest.php @@ -40,7 +40,7 @@ public function testGetUser(): void $config = $this->createStub(ConfigInterface::class); $config->method('getSetting')->willReturn('test'); - $identity = new Identity($session); + $identity = new Identity($config,$session); $user = $identity->getUser(); $this->assertEquals('testUser', $user->user); } @@ -58,7 +58,7 @@ public function testSaveUser(): void $testUser = new MockUser(); $testUser->user = 'testUser'; $namespace->identity = $testUser; - $identity = new Identity($session); + $identity = new Identity($config,$session); $user = $identity->getUser(); $this->assertEquals(null, $user); @@ -80,7 +80,7 @@ public function testDestroyUser(): void $testUser = new MockUser(); $testUser->user = 'testUser'; - $identity = new Identity($session); + $identity = new Identity($config,$session); $identity->saveUser($testUser); $user = $identity->getUser(); diff --git a/Install/container.php b/Install/container.php index 3d54f20..1c0e81f 100644 --- a/Install/container.php +++ b/Install/container.php @@ -137,7 +137,7 @@ if (RUN_AS === Main::RUN_AS_WEBAPP) { $session = new Session($config); $container->add(Session::class, $session); - $container->add(Identity::class, new Identity($session)); + $container->add(Identity::class, new Identity($config,$session)); } else { $container->add(CliArguments::class, new CliArguments($argv)); } diff --git a/Session/Identity.php b/Session/Identity.php index 931ed99..7ac171c 100755 --- a/Session/Identity.php +++ b/Session/Identity.php @@ -33,11 +33,12 @@ class Identity implements ServiceContainerItemInterface use DependencyInjected; protected \stdClass $me; - - public function __construct(protected Session $session) + + public function __construct(protected ConfigInterface $config,protected Session $session,) { $this->checkInjected(); $this->me = $session->getNamespace('Feast_Login'); + $this->config = $config; } /** From 65689c97b10801f5ebef5728cfe2befe59384f96 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 22 Oct 2021 12:52:10 -0400 Subject: [PATCH 09/56] Github workflows for 8.1 --- .github/workflows/PhpUnit.yaml | 2 +- .github/workflows/Psalm.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/PhpUnit.yaml b/.github/workflows/PhpUnit.yaml index 977e6b4..e364621 100644 --- a/.github/workflows/PhpUnit.yaml +++ b/.github/workflows/PhpUnit.yaml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' extensions: bcmath coverage: xdebug tools: phpunit diff --git a/.github/workflows/Psalm.yaml b/.github/workflows/Psalm.yaml index 16f235e..29859ce 100644 --- a/.github/workflows/Psalm.yaml +++ b/.github/workflows/Psalm.yaml @@ -10,7 +10,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' tools: psalm - name: Checkout project uses: actions/checkout@v2 From 96e12cc091fcdefbc5296bae80add4193a241ac5 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 22 Oct 2021 13:00:01 -0400 Subject: [PATCH 10/56] Mark security upload as continue on error --- .github/workflows/Psalm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Psalm.yaml b/.github/workflows/Psalm.yaml index 29859ce..a4226b5 100644 --- a/.github/workflows/Psalm.yaml +++ b/.github/workflows/Psalm.yaml @@ -33,6 +33,7 @@ jobs: run: psalm --show-info=true --no-diff --report=results.sarif - name: Upload Security Analysis results to GitHub + continue-on-error: true uses: github/codeql-action/upload-sarif@v1 with: sarif_file: results.sarif \ No newline at end of file From a0fbe9dfa99877b0e2264337b119fb492a54a133 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 22 Oct 2021 17:16:39 -0400 Subject: [PATCH 11/56] Code cleanup --- Attributes/Action.php | 2 -- Attributes/Param.php | 2 +- Binary.php | 4 +-- CliController.php | 2 +- Controllers/JobController.php | 2 +- Database/Database.php | 4 +-- Form/Field.php | 1 - Form/Field/Radio.php | 2 +- Form/Field/Select.php | 4 +-- Form/Field/Text.php | 2 +- Form/Field/Textarea.php | 2 +- Form/Form.php | 1 - Form/Validator/Url.php | 8 ++--- Help.php | 4 +-- HttpRequest/HttpRequest.php | 8 ++--- Install/famine | 10 +++---- Install/public/index.php | 12 ++++---- Interfaces/LoggerInterface.php | 1 - Interfaces/RouterInterface.php | 1 - Jobs/CronJob.php | 55 +++++++++++++++++----------------- Jobs/QueueableJob.php | 5 ++-- Main.php | 1 - PsalmLoader.php | 8 ++--- Router/Router.php | 5 ++-- Session/Identity.php | 3 +- Session/Session.php | 1 - Traits/Collection.php | 14 ++++----- View.php | 4 +-- bootstrap.php | 1 - 29 files changed, 71 insertions(+), 98 deletions(-) diff --git a/Attributes/Action.php b/Attributes/Action.php index 36f2ad0..13f1579 100644 --- a/Attributes/Action.php +++ b/Attributes/Action.php @@ -25,8 +25,6 @@ #[Attribute(Attribute::TARGET_METHOD)] class Action { - protected array $params = []; - public function __construct(public string $usage = '', public string $description = '') { } diff --git a/Attributes/Param.php b/Attributes/Param.php index 95223c4..c9d098f 100644 --- a/Attributes/Param.php +++ b/Attributes/Param.php @@ -44,7 +44,7 @@ public function __construct( public function getParamText(Terminal $terminal): string { $name = $this->paramType == ParamType::FLAG ? '--' . $this->name . '=' : '{' . $this->name . '} '; - $name = $terminal->commandText(str_pad($name . $this->type . '', 19)); + $name = $terminal->commandText(str_pad($name . $this->type, 19)); $name .= ' ' . $terminal->messageText($this->description); return trim($name); diff --git a/Binary.php b/Binary.php index 650ba5a..41a2e9f 100644 --- a/Binary.php +++ b/Binary.php @@ -210,9 +210,8 @@ private function help(string $command): void * Process description attribute and return whether any methods were found. * * @param \ReflectionMethod $method - * @return bool */ - private function processCliMethods(\ReflectionMethod $method): bool + private function processCliMethods(\ReflectionMethod $method): void { $name = NameHelper::getMethodNameAsCallableAction($method->getName()); $class = NameHelper::getControllerClassName($method->getDeclaringClass()); @@ -231,7 +230,6 @@ private function processCliMethods(\ReflectionMethod $method): bool $this->terminal->message($actionItem->description); } - return true; } /** diff --git a/CliController.php b/CliController.php index e59c681..24cf7c6 100755 --- a/CliController.php +++ b/CliController.php @@ -35,7 +35,7 @@ public function __construct( ) { $config ??= $di->get(ConfigInterface::INTERFACE_NAME); /** @var bool|null $setting */ - $setting = $config->getSetting('ttycolor', null); + $setting = $config->getSetting('ttycolor'); $this->terminal = new Terminal($setting); } diff --git a/Controllers/JobController.php b/Controllers/JobController.php index fecbef7..a6dd2e0 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -149,7 +149,7 @@ public function runCronItemGet( $jobProcess->stopRun($now); } - protected function runJob(\Model\Job $job, LoggerInterface $logger, JobMapper $jobMapper,): bool + protected function runJob(\Model\Job $job, LoggerInterface $logger, JobMapper $jobMapper): bool { $canRun = $jobMapper->markJobPendingIfAble($job); if ($canRun === false) { diff --git a/Database/Database.php b/Database/Database.php index 258ef17..1abe110 100644 --- a/Database/Database.php +++ b/Database/Database.php @@ -362,9 +362,7 @@ private function getConnectionString(string $database, string $hostname = 'local DatabaseType::MYSQL => sprintf('mysql:host=%s;port=%s;dbname=%s', $hostname, $port, $database), DatabaseType::SQLITE => - sprintf('sqlite:%s', $database), - default => - throw new DatabaseException('Invalid Database type') + sprintf('sqlite:%s', $database) }; } diff --git a/Form/Field.php b/Form/Field.php index 0a86346..6a2917b 100644 --- a/Form/Field.php +++ b/Form/Field.php @@ -27,7 +27,6 @@ use Feast\Form\Field\Value; use Feast\Form\Filter\Filter; use Feast\Form\Validator\Validator; -use stdClass; /** * This class is used to create form fields. diff --git a/Form/Field/Radio.php b/Form/Field/Radio.php index f84e76d..b184b19 100755 --- a/Form/Field/Radio.php +++ b/Form/Field/Radio.php @@ -39,7 +39,7 @@ public function __construct(string $name, ?string $formName = null, ?string $id */ public function setValue(string $value, bool $overwrite = true): static { - return parent::setValue($value,true); + return parent::setValue($value); } } diff --git a/Form/Field/Select.php b/Form/Field/Select.php index 7ca5c9b..4a4e3dd 100755 --- a/Form/Field/Select.php +++ b/Form/Field/Select.php @@ -118,9 +118,9 @@ public function setValue(string $value, bool $overwrite = true): static * @var string $key * @var Value $val */ - foreach ($values as $key => $val) { + foreach ($values as $val) { /** @var Value $valueItem */ - $valueItem = $values[$key]; + $valueItem = $val; if ($val->value === $value) { $valueItem->selected = true; } elseif ($overwrite) { diff --git a/Form/Field/Text.php b/Form/Field/Text.php index 32ac576..cdfa630 100644 --- a/Form/Field/Text.php +++ b/Form/Field/Text.php @@ -48,7 +48,7 @@ public function toString(bool $showLabel = true, string $value = ''): string $output = $this->showLabel($showLabel, $this->label, Label::LABEL_POSITION_FIRST) ? $label : ''; $output .= 'default || $this->value) { - $output .= ' value="' . ($this->value ? $this->value : $this->default) . '"'; + $output .= ' value="' . ($this->value ?: $this->default) . '"'; } $output .= $this->buildMetaData(); /** diff --git a/Form/Field/Textarea.php b/Form/Field/Textarea.php index 8d204cf..abeadac 100755 --- a/Form/Field/Textarea.php +++ b/Form/Field/Textarea.php @@ -78,7 +78,7 @@ public function toString( } $output .= '>'; if ($this->default || $this->value) { - $output .= ($this->value ? $this->value : $this->default); + $output .= ($this->value ?: $this->default); } $output .= ''; if ($this->showLabel($showLabel, $this->label, Label::LABEL_POSITION_LAST)) { diff --git a/Form/Form.php b/Form/Form.php index 67ac115..15ee7ae 100644 --- a/Form/Form.php +++ b/Form/Form.php @@ -121,7 +121,6 @@ public function isValid(bool $validatePartial = false): bool if ($this->isRequiredAndMissing($validatePartial, $formField, $field)) { $errors[] = [$key, self::ERROR_NOT_SET]; $formValid = false; - continue; } elseif ($this->notRequiredAndIsEmpty($field, $formField)) { continue; } elseif (is_string($field)) { diff --git a/Form/Validator/Url.php b/Form/Validator/Url.php index b3a4217..cea6eb9 100644 --- a/Form/Validator/Url.php +++ b/Form/Validator/Url.php @@ -47,16 +47,12 @@ public static function validate( array &$errors, bool $valid ): bool { - $value = substr( - $value, - 0, - 4 - ) != 'http' ? 'http://' . $value : $value; + $value = !str_starts_with($value, 'http') ? 'http://' . $value : $value; if (!filter_var($value, FILTER_VALIDATE_URL)) { $errors[] = [$key, 'URL']; $valid = false; } - + return $valid; } diff --git a/Help.php b/Help.php index c7cf1ae..78b8534 100644 --- a/Help.php +++ b/Help.php @@ -107,9 +107,7 @@ protected function analyzeClass(string $controllerClass, string $command): void /** @var Action $actionItem */ $actionItem = $actionAttribute->newInstance(); - $longestMethod = $longestMethod < strlen($callable) ? strlen( - $callable - ) : $longestMethod; + $longestMethod = max($longestMethod, strlen($callable)); $descriptions[$callable] = $actionItem->description; $count++; } diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index 6d66879..6c5328b 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -258,10 +258,9 @@ public function getArguments(): array * Set the url for the request. * * @param string $url - * @return HttpRequest - * @throws ServerFailureException + * @throws BadRequestException */ - private function setUrl(string $url): HttpRequestInterface + private function setUrl(string $url): void { $this->url = $url; if (!str_contains($url, 'http://') && !str_contains($url, 'https://')) { @@ -277,8 +276,6 @@ private function setUrl(string $url): HttpRequestInterface } else { $this->baseUrl = $url; } - - return $this; } /** @@ -504,6 +501,7 @@ public function getResponseAsXml(): ?SimpleXMLElement /** * Get the \Feast\Response object for a finished request. + * * @return Response|null */ public function getResponse(): ?Response diff --git a/Install/famine b/Install/famine index 0f1233d..650c0f1 100755 --- a/Install/famine +++ b/Install/famine @@ -21,11 +21,11 @@ declare(strict_types=1); chdir(__DIR__); if ( file_exists('vendor/autoload.php')) { require_once('vendor/autoload.php'); -}; +} // Set up framework -define('APPLICATION_ROOT', __DIR__ . DIRECTORY_SEPARATOR); -define('CONTROLLERS_FOLDER', 'Controllers'); -define('PLUGINS_FOLDER', 'Plugins'); +const APPLICATION_ROOT = __DIR__ . DIRECTORY_SEPARATOR; +const CONTROLLERS_FOLDER = 'Controllers'; +const PLUGINS_FOLDER = 'Plugins'; // Initialize autoloader if (file_exists(APPLICATION_ROOT . 'Feast/Autoloader.php')) { @@ -34,7 +34,7 @@ if (file_exists(APPLICATION_ROOT . 'Feast/Autoloader.php')) { $autoLoader = new Feast\Autoloader(); $autoLoader->register(); $autoLoader->addPathMapping('Psr', ['/Feast/Psr']); -define('RUN_AS', \Feast\Main::RUN_AS_CLI); +const RUN_AS = \Feast\Main::RUN_AS_CLI; require_once(APPLICATION_ROOT . 'container.php'); /** @var \Feast\ServiceContainer\ServiceContainer $container */ $container->add(\Feast\Autoloader::class, $autoLoader); diff --git a/Install/public/index.php b/Install/public/index.php index 2c33bec..e238ecb 100644 --- a/Install/public/index.php +++ b/Install/public/index.php @@ -28,10 +28,10 @@ // Application start time $startTime = microtime(true); -define('APPLICATION_ROOT', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); -define('CONTROLLERS_FOLDER', 'Controllers'); -define('HANDLERS_FOLDER', 'Handlers'); -define('PLUGINS_FOLDER', 'Plugins'); +const APPLICATION_ROOT = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; +const CONTROLLERS_FOLDER = 'Controllers'; +const HANDLERS_FOLDER = 'Handlers'; +const PLUGINS_FOLDER = 'Plugins'; if (file_exists(APPLICATION_ROOT . 'maintenance.txt')) { http_response_code(503); @@ -41,7 +41,7 @@ } if ( file_exists(APPLICATION_ROOT . 'vendor/autoload.php')) { require_once(APPLICATION_ROOT . 'vendor/autoload.php'); -}; +} // Initialize autoloader if ( file_exists(APPLICATION_ROOT . '/Feast/Autoloader.php')) { require_once(APPLICATION_ROOT . '/Feast/Autoloader.php'); @@ -51,7 +51,7 @@ $autoLoader->addPathMapping('Psr', ['/Feast/Psr']); -define('RUN_AS', Main::RUN_AS_WEBAPP); +const RUN_AS = Main::RUN_AS_WEBAPP; require_once(APPLICATION_ROOT . 'container.php'); /** @var \Feast\ServiceContainer\ServiceContainer $container */ $container->add(Autoloader::class, $autoLoader); diff --git a/Interfaces/LoggerInterface.php b/Interfaces/LoggerInterface.php index ef0d36a..58bd3b0 100644 --- a/Interfaces/LoggerInterface.php +++ b/Interfaces/LoggerInterface.php @@ -20,7 +20,6 @@ namespace Feast\Interfaces; -use Feast\Enums\LogLevelCode; use Feast\ServiceContainer\ServiceContainerItemInterface; interface LoggerInterface extends ServiceContainerItemInterface diff --git a/Interfaces/RouterInterface.php b/Interfaces/RouterInterface.php index 355cd0c..16501d9 100644 --- a/Interfaces/RouterInterface.php +++ b/Interfaces/RouterInterface.php @@ -22,7 +22,6 @@ use Exception; use Feast\Attributes\Path; -use Feast\Enums\RequestMethod; use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; diff --git a/Jobs/CronJob.php b/Jobs/CronJob.php index 5844879..a31f2f8 100644 --- a/Jobs/CronJob.php +++ b/Jobs/CronJob.php @@ -163,7 +163,7 @@ public function cron(string $cronString): static */ public function everyMinute(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $this->cronString = implode(' ', ['*', $hour, $dayOfMonth, $month, $dayOfWeek]); return $this; } @@ -175,7 +175,7 @@ public function everyMinute(): static */ public function everyOddMinute(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $this->cronString = implode(' ', ['1-59/2', $hour, $dayOfMonth, $month, $dayOfWeek]); return $this; } @@ -268,7 +268,7 @@ public function everyThirtyMinutes(): static */ protected function everyXDivisibleMinutes(int $x): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $this->cronString = implode(' ', ['*/' . (string)$x, $hour, $dayOfMonth, $month, $dayOfWeek]); return $this; } @@ -280,7 +280,7 @@ protected function everyXDivisibleMinutes(int $x): static */ public function hourly(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, , $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $this->cronString = implode(' ', [$minute, '*', $dayOfMonth, $month, $dayOfWeek]); return $this; @@ -294,7 +294,7 @@ public function hourly(): static */ public function hourlyAt(int $minuteNew): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [, , $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minuteNew, '*', $dayOfMonth, $month, $dayOfWeek]); return $this; } @@ -306,7 +306,7 @@ public function hourlyAt(int $minuteNew): static */ public function hourlyOnOddHours(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, , $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $this->cronString = implode(' ', [$minute, '1-23/2', $dayOfMonth, $month, $dayOfWeek]); return $this; @@ -380,7 +380,7 @@ public function everyTwelveHours(): static */ protected function everyXDivisibleHours(int $x): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, , $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $this->cronString = implode(' ', [$minute, '*/' . $x, $dayOfMonth, $month, $dayOfWeek]); return $this; @@ -408,7 +408,7 @@ public function daily(): static */ public function dailyAt(string $time): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [, , $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); [$hour, $minute] = explode(':', $time); $this->cronString = implode(' ', [(string)(int)$minute, (string)(int)$hour, $dayOfMonth, $month, $dayOfWeek]); @@ -424,7 +424,7 @@ public function dailyAt(string $time): static */ public function twiceDaily(int $hour1, int $hour2): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, , $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = implode(',', [$hour1, $hour2]); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, $dayOfWeek]); @@ -438,7 +438,7 @@ public function twiceDaily(int $hour1, int $hour2): static */ public function weekly(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '0']); @@ -454,7 +454,7 @@ public function weekly(): static */ public function weeklyOn(int $dayOfWeekNew, ?string $time = null): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, $dayOfWeekNew]); @@ -471,7 +471,7 @@ public function weeklyOn(int $dayOfWeekNew, ?string $time = null): static */ public function monthly(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, , $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $this->cronString = implode(' ', [$minute, $hour, '1', $month, $dayOfWeek]); @@ -487,7 +487,7 @@ public function monthly(): static */ public function monthlyOn(int $dayOfMonthNew, ?string $time = null): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, , $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $this->cronString = implode(' ', [$minute, $hour, $dayOfMonthNew, $month, $dayOfWeek]); @@ -507,7 +507,7 @@ public function monthlyOn(int $dayOfMonthNew, ?string $time = null): static */ public function twiceMonthly(int $dayOfMonthOne, int $dayOfMonthTwo, ?string $time = null): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, , $month, $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $this->cronString = implode(' ', [$minute, $hour, $dayOfMonthOne . ',' . $dayOfMonthTwo, $month, $dayOfWeek]); @@ -526,7 +526,7 @@ public function twiceMonthly(int $dayOfMonthOne, int $dayOfMonthTwo, ?string $ti */ public function quarterly(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, , $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $dayOfMonth = $dayOfMonth === '*' ? '1' : $dayOfMonth; @@ -541,7 +541,7 @@ public function quarterly(): static */ public function yearly(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, , $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $dayOfMonth = $dayOfMonth === '*' ? '1' : $dayOfMonth; @@ -559,7 +559,7 @@ public function yearly(): static */ public function yearlyOn(int $dayOfMonthNew, int $monthNew, ?string $time = null): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, , , $dayOfWeek] = explode(' ', $this->cronString); $minute = $minute === '*' ? '0' : $minute; $hour = $hour === '*' ? '0' : $hour; $this->cronString = implode(' ', [$minute, $hour, $dayOfMonthNew, $monthNew, $dayOfWeek]); @@ -576,7 +576,7 @@ public function yearlyOn(int $dayOfMonthNew, int $monthNew, ?string $time = null */ public function weekdays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '1,2,3,4,5']); return $this; } @@ -588,7 +588,7 @@ public function weekdays(): static */ public function weekends(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '0,6']); return $this; } @@ -600,7 +600,7 @@ public function weekends(): static */ public function sundays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '0']); return $this; } @@ -612,7 +612,7 @@ public function sundays(): static */ public function mondays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '1']); return $this; } @@ -624,7 +624,7 @@ public function mondays(): static */ public function tuesdays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '2']); return $this; } @@ -636,7 +636,7 @@ public function tuesdays(): static */ public function wednesdays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '3']); return $this; } @@ -648,7 +648,7 @@ public function wednesdays(): static */ public function thursdays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '4']); return $this; } @@ -660,7 +660,7 @@ public function thursdays(): static */ public function fridays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '5']); return $this; } @@ -672,7 +672,7 @@ public function fridays(): static */ public function saturdays(): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, '6']); return $this; } @@ -687,7 +687,7 @@ public function saturdays(): static */ public function days(array $days): static { - [$minute, $hour, $dayOfMonth, $month, $dayOfWeek] = explode(' ', $this->cronString); + [$minute, $hour, $dayOfMonth, $month,] = explode(' ', $this->cronString); $this->cronString = implode(' ', [$minute, $hour, $dayOfMonth, $month, implode(',', $days)]); return $this; } @@ -897,7 +897,6 @@ protected function checkSubPiece(string $field, int $value): bool $divisor = null; $rulesPassed = true; $start = null; - $end = null; if (str_contains($field, '/')) { [$field, $divisor] = explode('/', $field, 2); } diff --git a/Jobs/QueueableJob.php b/Jobs/QueueableJob.php index 3d4dc48..10fb4b4 100644 --- a/Jobs/QueueableJob.php +++ b/Jobs/QueueableJob.php @@ -40,13 +40,12 @@ abstract class QueueableJob implements JobInterface /** * Store job in the database for the queue to pick up. * - * @param ?JobMapper $jobMapper + * @param JobMapper $jobMapper * @return Job * @throws InvalidDateException */ - public function store(JobMapper $jobMapper = null): Job + public function store(JobMapper $jobMapper = new JobMapper()): Job { - $jobMapper ??= new JobMapper(); $model = new Job(); $model->job_id = $this->generateUuid(); $model->job_name = $this->jobName; diff --git a/Main.php b/Main.php index b9e1ee4..bfd1f09 100644 --- a/Main.php +++ b/Main.php @@ -276,7 +276,6 @@ protected function buildArgument( if (is_subclass_of($argumentType, BaseModel::class)) { $this->buildBaseModelArgument($argumentType, $request, $argument, $return); - return; } elseif (is_subclass_of($argumentType, BaseMapper::class)) { $mapper = new $argumentType(); $return[] = $mapper; diff --git a/PsalmLoader.php b/PsalmLoader.php index ec12ba2..357ed60 100644 --- a/PsalmLoader.php +++ b/PsalmLoader.php @@ -36,9 +36,9 @@ use Feast\Router\Router; use Feast\View; -define('APPLICATION_ROOT', __DIR__ . '/'); -define('CONTROLLERS_FOLDER', 'Controllers'); -define('PLUGINS_FOLDER', 'Plugins'); +const APPLICATION_ROOT = __DIR__ . '/'; +const CONTROLLERS_FOLDER = 'Controllers'; +const PLUGINS_FOLDER = 'Plugins'; // Initialize autoloader require_once(APPLICATION_ROOT . 'Autoloader.php'); @@ -69,5 +69,5 @@ $container->add(ErrorLoggerInterface::class, new ErrorLogger($logger)); $container->add(ResponseInterface::class, new Response()); -define('RUN_AS', \Feast\Main::RUN_AS_CLI); +const RUN_AS = \Feast\Main::RUN_AS_CLI; diff --git a/Router/Router.php b/Router/Router.php index 1628a43..985fa01 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -239,7 +239,7 @@ protected function getPathFromArguments( if (str_ends_with($path, 'index')) { $path = substr($path, 0, -5); } - if (str_ends_with($path, '/',)) { + if (str_ends_with($path, '/')) { $path = substr($path, 0, -1); } return $path; @@ -320,8 +320,7 @@ protected function getNamedRouteForUrlPath(string $requestMethod, string $checkS $routeGroup = $this->routes->$requestMethod; /** @var RouteData $routeData */ foreach ((array)$routeGroup as $routeData) { - $arguments = []; - if (preg_match_all($routeData->pattern, $checkString, $arguments)) { + if (preg_match_all($routeData->pattern, $checkString)) { return $routeData; } } diff --git a/Session/Identity.php b/Session/Identity.php index 7ac171c..273db32 100755 --- a/Session/Identity.php +++ b/Session/Identity.php @@ -34,11 +34,10 @@ class Identity implements ServiceContainerItemInterface protected \stdClass $me; - public function __construct(protected ConfigInterface $config,protected Session $session,) + public function __construct(protected ConfigInterface $config,protected Session $session) { $this->checkInjected(); $this->me = $session->getNamespace('Feast_Login'); - $this->config = $config; } /** diff --git a/Session/Session.php b/Session/Session.php index 3006d0b..27144c0 100755 --- a/Session/Session.php +++ b/Session/Session.php @@ -22,7 +22,6 @@ use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\ResponseInterface; -use Feast\Interfaces\RouterInterface; use Feast\ServiceContainer; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; diff --git a/Traits/Collection.php b/Traits/Collection.php index 49498e4..93a90fa 100644 --- a/Traits/Collection.php +++ b/Traits/Collection.php @@ -90,13 +90,11 @@ protected function objectImplode(string $separator, string $key): string /** * Sort the collection by different sort options. * - * @param $sortType + * @param CollectionSort $sortType * @param bool $modifyOriginal * @param int $sortOptions * @return array - * @throws InvalidOptionException * @see CollectionSort - * */ public function sort(CollectionSort $sortType, bool $modifyOriginal = false, int $sortOptions = SORT_REGULAR): array { @@ -265,7 +263,7 @@ public function clear(): void /** * Check if item exists in collection. * - * @param $value + * @param mixed $value * @param bool $strictMatch * @return bool */ @@ -273,7 +271,7 @@ public function contains( mixed $value, bool $strictMatch = true ): bool { - return array_search($value, $this->array, $strictMatch) !== false; + return in_array($value, $this->array, $strictMatch); } /** @@ -300,7 +298,7 @@ public function containsAll( /** * Find the first index of an item in the collection. * - * @param $value + * @param mixed $value * @param bool $strictMatch * @return null|int|string */ @@ -319,7 +317,7 @@ public function indexOf( /** * Find the last index of an item in the collection. * - * @param $value + * @param mixed $value * @param bool $strictMatch * @return array-key|null */ @@ -348,7 +346,7 @@ public function size(): int /** * Remove an item from the collection. * - * @param $valueMatch + * @param mixed $valueMatch * @param bool $strictMatch */ public function remove( diff --git a/View.php b/View.php index 5c5c2f4..33ce7f4 100644 --- a/View.php +++ b/View.php @@ -173,7 +173,7 @@ public function getPreScripts(): string $allScripts = ''; /** @var string $script */ foreach ($this->preScripts as $script) { - $filename = substr($script, 0, 4) === 'http' ? $script : '/js/' . $script; + $filename = str_starts_with($script, 'http') ? $script : '/js/' . $script; $allScripts .= '' . PHP_EOL; } /** @var string $script */ @@ -236,7 +236,7 @@ public function getPostScripts(): string $allScripts = ''; /** @var string $script */ foreach ($this->postScripts as $script) { - $filename = substr($script, 0, 4) == 'http' ? $script : '/js/' . $script; + $filename = str_starts_with($script, 'http') ? $script : '/js/' . $script; $allScripts .= '' . PHP_EOL; } /** @var string $script */ diff --git a/bootstrap.php b/bootstrap.php index 813f3bc..71c395a 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -23,7 +23,6 @@ # DO NOT MODIFY THIS FILE AT ALL # ########################################################## # -use Feast\ServiceContainer\ServiceContainer; if (file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php')) { /** @psalm-suppress MissingFile */ From e4c83aca9ed63bf38314f2dc175cce48b951940b Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sat, 23 Oct 2021 19:51:43 -0400 Subject: [PATCH 12/56] Add Template controller --- Binary.php | 10 +- Controllers/CreateController.php | 62 +++-- Controllers/MigrationController.php | 8 +- Controllers/TemplateController.php | 137 ++++++++++ Controllers/WriteTemplateController.php | 36 +++ FeastTests/BaseModelTest.php | 1 - FeastTests/BinaryTest.php | 18 ++ .../Controllers/CreateControllerTest.php | 18 +- .../Controllers/TemplateControllerTest.php | 252 ++++++++++++++++++ FeastTests/bin/templates/Action.php.txt | 5 - FeastTests/bin/templates/CliAction.php.txt | 6 - FeastTests/bin/templates/Controller.php.txt | 11 - FeastTests/bin/templates/CronJob.php.txt | 17 -- FeastTests/bin/templates/Filter.php.txt | 15 -- FeastTests/bin/templates/Form.php.txt | 16 -- FeastTests/bin/templates/Mapper.php.txt | 53 ---- FeastTests/bin/templates/Migration.php.txt | 27 -- FeastTests/bin/templates/Model.php.txt | 10 - .../bin/templates/ModelGenerated.php.txt | 15 -- FeastTests/bin/templates/Plugin.php.txt | 22 -- FeastTests/bin/templates/QueueableJob.php.txt | 17 -- FeastTests/bin/templates/Service.php.txt | 17 -- FeastTests/bin/templates/Validator.php.txt | 25 -- FeastTests/bin/templates/layout.phtml | 40 --- Install/install.php | 1 - Main.php | 1 + 26 files changed, 508 insertions(+), 332 deletions(-) create mode 100644 Controllers/TemplateController.php create mode 100644 Controllers/WriteTemplateController.php create mode 100644 FeastTests/Controllers/TemplateControllerTest.php delete mode 100644 FeastTests/bin/templates/Action.php.txt delete mode 100644 FeastTests/bin/templates/CliAction.php.txt delete mode 100644 FeastTests/bin/templates/Controller.php.txt delete mode 100644 FeastTests/bin/templates/CronJob.php.txt delete mode 100644 FeastTests/bin/templates/Filter.php.txt delete mode 100644 FeastTests/bin/templates/Form.php.txt delete mode 100644 FeastTests/bin/templates/Mapper.php.txt delete mode 100644 FeastTests/bin/templates/Migration.php.txt delete mode 100644 FeastTests/bin/templates/Model.php.txt delete mode 100644 FeastTests/bin/templates/ModelGenerated.php.txt delete mode 100644 FeastTests/bin/templates/Plugin.php.txt delete mode 100644 FeastTests/bin/templates/QueueableJob.php.txt delete mode 100644 FeastTests/bin/templates/Service.php.txt delete mode 100644 FeastTests/bin/templates/Validator.php.txt delete mode 100644 FeastTests/bin/templates/layout.phtml diff --git a/Binary.php b/Binary.php index 41a2e9f..14aac77 100644 --- a/Binary.php +++ b/Binary.php @@ -27,6 +27,7 @@ use Feast\Controllers\MaintenanceController; use Feast\Controllers\MigrationController; use Feast\Controllers\ServeController; +use Feast\Controllers\TemplateController; use Feast\Exception\NotFoundException; use Feast\Interfaces\MainInterface; use ReflectionException; @@ -122,6 +123,12 @@ public function run(array $rawArguments, array $arguments): void return; } + if ($rawArguments[1] === 'feast:template') { + $this->printUsage($rawArguments[1]); + $this->analyzeFeast([TemplateController::class]); + return; + } + if (str_starts_with($rawArguments[1], 'feast') === false && str_contains($rawArguments[1], ':') === false) { $this->help($rawArguments[1]); return; @@ -245,7 +252,8 @@ private function analyzeFeast( CacheController::class, JobController::class, MaintenanceController::class, - ServeController::class + ServeController::class, + TemplateController::class ] ): void { /** @var class-string $class */ diff --git a/Controllers/CreateController.php b/Controllers/CreateController.php index b2d57ea..3c05921 100644 --- a/Controllers/CreateController.php +++ b/Controllers/CreateController.php @@ -29,7 +29,7 @@ use Feast\Interfaces\DatabaseFactoryInterface; use Feast\NameHelper; -class CreateController extends CliController +class CreateController extends WriteTemplateController { #[Action(usage: '--type={(get-post-put-delete-patch)} --module={module} --noview={true|false} {controller} {action}', description: 'Create a new controller action from the template file.')] @@ -213,7 +213,9 @@ public function pluginGet( $file = APPLICATION_ROOT . 'Plugins' . DIRECTORY_SEPARATOR . ucfirst($name) . '.php'; $this->writeSimpleTemplate($name, 'Plugin', $file); - $this->terminal->message('To enable add the line below to your configs/config.php in the appropriate environment' . "\n"); + $this->terminal->message( + 'To enable add the line below to your configs/config.php in the appropriate environment' . "\n" + ); $this->terminal->command('\'plugin.' . strtolower($name) . '\' => \\Plugins\\' . ucfirst($name) . '::class,'); } @@ -268,9 +270,8 @@ protected function writeSimpleTemplate(string $name, string $type, string $file) $this->terminal->error('File ' . $file . ' already exists.'); return; } - $contents = file_get_contents( - APPLICATION_ROOT . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $type . '.php.txt' - ); + $templateFile = $this->getTemplateFilePath($type); + $contents = file_get_contents($templateFile); $contents = str_replace( [ '{name}' @@ -299,9 +300,9 @@ protected function writeModelFile(TableDetails $tableInfo, string $class): void $fields .= $field->getModelField(); } - $this->writeTemplateFile('Model.php.txt', 'Model', false, $class, '', $fields); + $this->writeTemplateFile('Model', 'Model', false, $class, '', $fields); $this->writeTemplateFile( - 'ModelGenerated.php.txt', + 'ModelGenerated', 'Model' . DIRECTORY_SEPARATOR . 'Generated', true, $class, @@ -334,14 +335,14 @@ protected function writeMapperfile( $this->terminal->message('Cannot generate mapper - Compound primary key'); } elseif (!empty($primaryKey) && !empty($primaryKeyType)) { $this->writeTemplateFile( - 'Mapper.php.txt', - 'Mapper', - $overwrite, - $class, - 'Mapper', - table: $table, - connection: $connection, - primaryKey: $primaryKey, + 'Mapper', + 'Mapper', + $overwrite, + $class, + 'Mapper', + table: $table, + connection: $connection, + primaryKey: $primaryKey, primaryType: $primaryKeyType ); $this->terminal->message('Mapper class created'); @@ -362,17 +363,26 @@ protected function writeTemplateFile( string $primaryKey = '', string $primaryType = '' ): void { - $template = file_get_contents( - APPLICATION_ROOT . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $templateFile - ); - if ( !file_exists(APPLICATION_ROOT . $path . DIRECTORY_SEPARATOR) ) { + $file = $this->getTemplateFilePath($templateFile); + $template = file_get_contents($file); + if (!file_exists(APPLICATION_ROOT . $path . DIRECTORY_SEPARATOR)) { mkdir(APPLICATION_ROOT . $path . DIRECTORY_SEPARATOR); } if ($overwrite || !file_exists(APPLICATION_ROOT . $path . DIRECTORY_SEPARATOR . $class . '.php')) { file_put_contents( APPLICATION_ROOT . $path . DIRECTORY_SEPARATOR . $class . $classExtra . '.php', str_replace( - ['onSave({name}','onDelete({name}','{name}', '{classExtra}', '{map}', '{connection}', '{primaryKey}', '{table}', '{primaryType}'], + [ + 'onSave({name}', + 'onDelete({name}', + '{name}', + '{classExtra}', + '{map}', + '{connection}', + '{primaryKey}', + '{table}', + '{primaryType}' + ], [ 'onSave(\\' . BaseModel::class . '|' . $class, 'onDelete(\\' . BaseModel::class . '|' . $class, @@ -406,9 +416,8 @@ protected function createControllerFileIfNotExists( ?string $module ): void { if (file_exists($path) === false) { - $contents = file_get_contents( - APPLICATION_ROOT . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Controller.php.txt' - ); + $file = $this->getTemplateFilePath('Controller'); + $contents = file_get_contents($file); $actionUse = $module === 'CLI' ? 'use Feast\Attributes\Action;' . "\n" : ''; $cliUse = $module === 'CLI' ? 'Cli' : 'Http'; @@ -436,10 +445,9 @@ protected function buildAction( string $types ): void { $types = explode('-', $types); - $templateFile = $module === 'CLI' ? 'CliAction.php.txt' : 'Action.php.txt'; - $template = file_get_contents( - APPLICATION_ROOT . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $templateFile - ); + $templateFile = $module === 'CLI' ? 'CliAction' : 'Action'; + $file = $this->getTemplateFilePath($templateFile); + $template = file_get_contents($file); $actionName = lcfirst(NameHelper::getName($action)); foreach ($types as $type) { diff --git a/Controllers/MigrationController.php b/Controllers/MigrationController.php index f95241c..556b309 100644 --- a/Controllers/MigrationController.php +++ b/Controllers/MigrationController.php @@ -22,13 +22,12 @@ use Feast\Attributes\Action; use Feast\Attributes\Param; -use Feast\CliController; use Feast\Enums\ParamType; use Feast\Interfaces\DatabaseDetailsInterface; use Mapper\MigrationMapper; use Model\Migration; -class MigrationController extends CliController +class MigrationController extends WriteTemplateController { final protected const MIGRATION_TABLE_MIGRATION = '1_migrations'; @@ -217,9 +216,8 @@ protected function buildMigrationListFromDatabase(): void protected function writeMigrationFile(string $fullName, string $name): void { - $contents = file_get_contents( - APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Migration.php.txt' - ); + $file = $this->getTemplateFilePath('Migration'); + $contents = file_get_contents($file); $contents = str_replace( ['{number}', '{name}'], [$fullName, $name], diff --git a/Controllers/TemplateController.php b/Controllers/TemplateController.php new file mode 100644 index 0000000..b7b67e5 --- /dev/null +++ b/Controllers/TemplateController.php @@ -0,0 +1,137 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare(strict_types=1); + +namespace Feast\Controllers; + +use Feast\Attributes\Action; +use Feast\CliController; +use Feast\Main; + +class TemplateController extends CliController +{ + + #[Action(description: 'Copy "Action" template to bin/templates folder')] + public function installActionGet(): void + { + $this->installFile('Action.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Action.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "CliAction" template to bin/templates folder')] + public function installCliActionGet(): void + { + $this->installFile('CliAction.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('CliAction.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Controller" template to bin/templates folder')] + public function installControllerGet(): void + { + $this->installFile('Controller.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Controller.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "CronJob" template to bin/templates folder')] + public function installCronJobGet(): void + { + $this->installFile('CronJob.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('CronJob.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Filter" template to bin/templates folder')] + public function installFilterGet(): void + { + $this->installFile('Filter.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Filter.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Form" template to bin/templates folder')] + public function installFormGet(): void + { + $this->installFile('Form.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Form.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Mapper" template to bin/templates folder')] + public function installMapperGet(): void + { + $this->installFile('Mapper.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Mapper.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Migration" template to bin/templates folder')] + public function installMigrationGet(): void + { + $this->installFile('Migration.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Migration.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Model" template to bin/templates folder')] + public function installModelGet(): void + { + $this->installFile('Model.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Model.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "ModelGenerated" template to bin/templates folder')] + public function installModelGeneratedGet(): void + { + $this->installFile('ModelGenerated.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('ModelGenerated.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Plugin" template to bin/templates folder')] + public function installPluginGet(): void + { + $this->installFile('Plugin.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Plugin.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "QueueableJob" template to bin/templates folder')] + public function installQueueableJobGet(): void + { + $this->installFile('QueueableJob.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('QueueableJob.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Service" template to bin/templates folder')] + public function installServiceGet(): void + { + $this->installFile('Service.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Service.php.txt') . ' has been copied to bin/templates.'); + } + + #[Action(description: 'Copy "Validator" template to bin/templates folder')] + public function installValidatorGet(): void + { + $this->installFile('Validator.php.txt'); + $this->terminal->message('File ' . $this->terminal->commandText('Validator.php.txt') . ' has been copied to bin/templates.'); + } + + protected function installFile(string $file): void + { + file_put_contents( + APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $file, + file_get_contents( + Main::FRAMEWORK_ROOT . DIRECTORY_SEPARATOR . 'Install' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $file + ) + ); + } +} diff --git a/Controllers/WriteTemplateController.php b/Controllers/WriteTemplateController.php new file mode 100644 index 0000000..d1b7c63 --- /dev/null +++ b/Controllers/WriteTemplateController.php @@ -0,0 +1,36 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare(strict_types=1); + +namespace Feast\Controllers; + +use Feast\CliController; +use Feast\Main; + +abstract class WriteTemplateController extends CliController { + + protected function getTemplateFilePath(string $type): string + { + $basePath = DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $type . '.php.txt'; + if ( file_exists(APPLICATION_ROOT . $basePath) ) { + return APPLICATION_ROOT . $basePath; + } + return Main::FRAMEWORK_ROOT . DIRECTORY_SEPARATOR . 'Install' . $basePath; + } +} diff --git a/FeastTests/BaseModelTest.php b/FeastTests/BaseModelTest.php index 89dd1aa..3ddd1ab 100644 --- a/FeastTests/BaseModelTest.php +++ b/FeastTests/BaseModelTest.php @@ -21,7 +21,6 @@ use Feast\Exception\InvalidOptionException; use Feast\Exception\NotFoundException; use Feast\Interfaces\DatabaseFactoryInterface; -use Feast\ServiceContainer\ServiceContainer; use PHPUnit\Framework\TestCase; class BaseModelTest extends TestCase diff --git a/FeastTests/BinaryTest.php b/FeastTests/BinaryTest.php index b265af8..e5ea7b8 100644 --- a/FeastTests/BinaryTest.php +++ b/FeastTests/BinaryTest.php @@ -71,6 +71,16 @@ public function testMainHelpFeastCreate(): void ); } + public function testMainHelpFeastTemplate(): void + { + $this->binary->run(['famine', 'help', 'feast:template'], ['famine', 'help', 'feast:template']); + $output = $this->getActualOutputForAssertion(); + $this->assertStringStartsWith( + 'Usage: php famine command options' . "\n" . 'Available feast:template commands', + trim($output) + ); + } + public function testMainHelpFeastJobs(): void { $this->binary->run(['famine', 'help', 'feast:job'], ['famine', 'help', 'feast:job']); @@ -193,6 +203,14 @@ public function testMainMigration(): void $this->assertStringContainsString('Available feast:migration commands:', $output); } + public function testMainTemplate(): void + { + $this->binary->run(['famine', 'feast:template'], ['famine', 'feast:template']); + $output = $this->getActualOutputForAssertion(); + $this->assertStringStartsWith('Usage: php famine command options', trim($output)); + $this->assertStringContainsString('Available feast:template commands:', $output); + } + public function testMainCache(): void { $this->binary->run(['famine', 'feast:cache'], ['famine', 'feast:cache']); diff --git a/FeastTests/Controllers/CreateControllerTest.php b/FeastTests/Controllers/CreateControllerTest.php index ba787e5..f98196f 100644 --- a/FeastTests/Controllers/CreateControllerTest.php +++ b/FeastTests/Controllers/CreateControllerTest.php @@ -30,6 +30,7 @@ use Feast\Enums\ServiceContainer; use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\DatabaseInterface; +use Feast\Main; use PHPUnit\Framework\TestCase; use stdClass; @@ -433,6 +434,21 @@ public function testPluginGetCreated(): void $this->assertStringContainsString('Plugin file NewService.php created.', trim($output)); } + public function testPluginGetCreatedFromTemplateDir(): void + { + $this->writeTempFile(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Plugin.php.txt', file_get_contents(Main::FRAMEWORK_ROOT . DIRECTORY_SEPARATOR . 'Install' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Plugin.php.txt')); + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturnOnConsecutiveCalls(false); + $controller = new CreateController( + di(null, ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:create:plugin']) + ); + $controller->pluginGet('NewService'); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Plugin file NewService.php created.', trim($output)); + } + public function testActionGetNoName(): void { $config = $this->createStub(Config::class); @@ -667,7 +683,7 @@ protected function writeTempController( bool $mangled = false ): void { $contents = file_get_contents( - APPLICATION_ROOT . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Controller.php.txt' + Main::FRAMEWORK_ROOT . DIRECTORY_SEPARATOR . 'Install' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Controller.php.txt' ); $actionUse = $namespace === '\\Modules\\CLI' ? 'use Feast\Attributes\Action;' . "\n" : ''; $cliUse = $namespace === '\\Modules\\CLI' ? 'Cli' : ''; diff --git a/FeastTests/Controllers/TemplateControllerTest.php b/FeastTests/Controllers/TemplateControllerTest.php new file mode 100644 index 0000000..cd4a946 --- /dev/null +++ b/FeastTests/Controllers/TemplateControllerTest.php @@ -0,0 +1,252 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +declare(strict_types=1); + +namespace Controllers; + +use Feast\CliArguments; +use Feast\Config\Config; +use Feast\Controllers\TemplateController; +use PHPUnit\Framework\TestCase; + +class TemplateControllerTest extends TestCase +{ + public function testInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-action']) + ); + $controller->installActionGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Action.php.txt',$output); + $this->assertStringContainsString('{action}{type}',$output); + } + + public function testCliActionInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-cli-action']) + ); + $controller->installCliActionGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('CliAction.php.txt',$output); + $this->assertStringContainsString('{action}{type}',$output); + } + + public function testControllerInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-controller']) + ); + $controller->installControllerGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Controller.php.txt',$output); + $this->assertStringContainsString('class {name}Controller extends {cli}Controller',$output); + } + + public function testCronJobAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-cron-job']) + ); + $controller->installCronJobGet();; + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('CronJob.php.txt',$output); + $this->assertStringContainsString('public function run(): bool',$output); + } + + public function testFilterInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-filter']) + ); + $controller->installFilterGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Filter.php.txt',$output); + $this->assertStringContainsString('public static function filter',$output); + } + + public function testFormInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-form']) + ); + $controller->installFormGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Form.php.txt',$output); + $this->assertStringContainsString('parent::__construct(\'name\', \'url\', \'post\');',$output); + } + + public function testMapperInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-mapper']) + ); + $controller->installMapperGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Mapper.php.txt',$output); + $this->assertStringContainsString('protected const OBJECT_NAME',$output); + } + + public function testMigrationInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-migration']) + ); + $controller->installMigrationGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Migration.php.txt',$output); + $this->assertStringContainsString('protected const NAME',$output); + } + + public function testModelInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-model']) + ); + $controller->installModelGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Model.php.txt',$output); + $this->assertStringContainsString('// PLACE CUSTOM MODEL CODE HERE',$output); + } + + public function testModelGeneratedInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-model-generated']) + ); + $controller->installModelGeneratedGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('ModelGenerated.php.txt',$output); + $this->assertStringContainsString('extends BaseModel',$output); + } + + public function testPluginInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-plugin']) + ); + $controller->installPluginGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Plugin.php.txt',$output); + $this->assertStringContainsString('public function preDispatch()',$output); + } + + public function testQueueableJobInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-queueable-job']) + ); + $controller->installQueueableJobGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('QueueableJob.php.txt',$output); + $this->assertStringContainsString('public function run(): bool',$output); + } + + public function testServiceInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-service']) + ); + $controller->installServiceGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Service.php.txt',$output); + $this->assertStringContainsString('extends Service',$output); + } + + public function testValidatorInstallAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-validator']) + ); + $controller->installValidatorGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Validator.php.txt',$output); + $this->assertStringContainsString('public static function validate',$output); + } +} diff --git a/FeastTests/bin/templates/Action.php.txt b/FeastTests/bin/templates/Action.php.txt deleted file mode 100644 index 2de56a2..0000000 --- a/FeastTests/bin/templates/Action.php.txt +++ /dev/null @@ -1,5 +0,0 @@ - - public function {action}{type}(): void - { - // TODO Add Content - } diff --git a/FeastTests/bin/templates/CliAction.php.txt b/FeastTests/bin/templates/CliAction.php.txt deleted file mode 100644 index c256e27..0000000 --- a/FeastTests/bin/templates/CliAction.php.txt +++ /dev/null @@ -1,6 +0,0 @@ - - #[Action(description: '')] - public function {action}{type}(): void - { - // TODO Add Content - } diff --git a/FeastTests/bin/templates/Controller.php.txt b/FeastTests/bin/templates/Controller.php.txt deleted file mode 100644 index 547bb29..0000000 --- a/FeastTests/bin/templates/Controller.php.txt +++ /dev/null @@ -1,11 +0,0 @@ -connection->rawQuery('select 1'); - parent::up(); - } - - public function down() : void - { - /** @todo Create down query */ - $this->connection->rawQuery('select 1'); - parent::down(); - } -} \ No newline at end of file diff --git a/FeastTests/bin/templates/Model.php.txt b/FeastTests/bin/templates/Model.php.txt deleted file mode 100644 index 31cb1cc..0000000 --- a/FeastTests/bin/templates/Model.php.txt +++ /dev/null @@ -1,10 +0,0 @@ - - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* @var $this \Feast\View */ -echo $this->getDtd(); -?> - - - getEncodingHtml(); ?> - getTitle(); ?> - getCss(); ?> - getPreScripts(); ?> - - - -getPostScripts(); ?> -getSetting('profiler')): ?> -

- Total memory: getReadableMemoryUsage(); ?>
- Peak memory: getReadablePeakMemoryUsage(); ?>
- Page executed in getTotalTime(); ?> seconds. -

- - - diff --git a/Install/install.php b/Install/install.php index 76e23ad..5642c9e 100644 --- a/Install/install.php +++ b/Install/install.php @@ -62,7 +62,6 @@ } $directories = [ - 'bin' . DIRECTORY_SEPARATOR . 'templates', 'bin', 'configs', 'Controllers', diff --git a/Main.php b/Main.php index bfd1f09..34d13a4 100644 --- a/Main.php +++ b/Main.php @@ -46,6 +46,7 @@ class Main implements MainInterface final public const RUN_AS_WEBAPP = 'webapp'; final public const RUN_AS_CLI = 'cli'; + final public const FRAMEWORK_ROOT = __DIR__; private array $plugins = []; private ErrorLoggerInterface $logger; From bd4969a09d7202ae58c1f324f9de3bc7565178d0 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sat, 23 Oct 2021 20:15:23 -0400 Subject: [PATCH 13/56] Fix false positive bug in latest psalm --- Router/Router.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Router/Router.php b/Router/Router.php index 985fa01..d19e720 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -761,6 +761,7 @@ public function assignArguments( $request->setArgument($key, $params[$i] ?? $val); $i++; } + /** @psalm-suppress TypeDoesNotContainType */ if ($allowVariadic === false || !isset($key) || $i <= 1 || count($params) === $i) { return; } From 9bd87d2772c634643fe440c9242934507f07159f Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sat, 23 Oct 2021 23:25:25 -0400 Subject: [PATCH 14/56] Update template controller docs, add install-all --- Controllers/TemplateController.php | 77 ++++- .../Controllers/TemplateControllerTest.php | 84 +++-- docs/cli.md | 295 ++++++++++++++---- 3 files changed, 355 insertions(+), 101 deletions(-) diff --git a/Controllers/TemplateController.php b/Controllers/TemplateController.php index b7b67e5..85fc9df 100644 --- a/Controllers/TemplateController.php +++ b/Controllers/TemplateController.php @@ -31,100 +31,147 @@ class TemplateController extends CliController public function installActionGet(): void { $this->installFile('Action.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Action.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Action.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "CliAction" template to bin/templates folder')] public function installCliActionGet(): void { $this->installFile('CliAction.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('CliAction.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('CliAction.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Controller" template to bin/templates folder')] public function installControllerGet(): void { $this->installFile('Controller.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Controller.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Controller.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "CronJob" template to bin/templates folder')] public function installCronJobGet(): void { $this->installFile('CronJob.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('CronJob.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('CronJob.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Filter" template to bin/templates folder')] public function installFilterGet(): void { $this->installFile('Filter.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Filter.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Filter.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Form" template to bin/templates folder')] public function installFormGet(): void { $this->installFile('Form.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Form.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Form.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Mapper" template to bin/templates folder')] public function installMapperGet(): void { $this->installFile('Mapper.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Mapper.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Mapper.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Migration" template to bin/templates folder')] public function installMigrationGet(): void { $this->installFile('Migration.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Migration.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Migration.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Model" template to bin/templates folder')] public function installModelGet(): void { $this->installFile('Model.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Model.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Model.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "ModelGenerated" template to bin/templates folder')] public function installModelGeneratedGet(): void { $this->installFile('ModelGenerated.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('ModelGenerated.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('ModelGenerated.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Plugin" template to bin/templates folder')] public function installPluginGet(): void { $this->installFile('Plugin.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Plugin.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Plugin.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "QueueableJob" template to bin/templates folder')] public function installQueueableJobGet(): void { $this->installFile('QueueableJob.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('QueueableJob.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('QueueableJob.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Service" template to bin/templates folder')] public function installServiceGet(): void { $this->installFile('Service.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Service.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Service.php.txt') . ' has been copied to bin/templates.' + ); } #[Action(description: 'Copy "Validator" template to bin/templates folder')] public function installValidatorGet(): void { $this->installFile('Validator.php.txt'); - $this->terminal->message('File ' . $this->terminal->commandText('Validator.php.txt') . ' has been copied to bin/templates.'); + $this->terminal->message( + 'File ' . $this->terminal->commandText('Validator.php.txt') . ' has been copied to bin/templates.' + ); } - + + #[Action(description: 'Copy all template to bin/templates folder')] + public function installAllGet(): void + { + $this->installActionGet(); + $this->installCliActionGet(); + $this->installControllerGet(); + $this->installCronJobGet(); + $this->installFilterGet(); + $this->installFormGet(); + $this->installMapperGet(); + $this->installMigrationGet(); + $this->installModelGet(); + $this->installModelGeneratedGet(); + $this->installPluginGet(); + $this->installQueueableJobGet(); + $this->installServiceGet(); + $this->installValidatorGet(); + } + protected function installFile(string $file): void { file_put_contents( diff --git a/FeastTests/Controllers/TemplateControllerTest.php b/FeastTests/Controllers/TemplateControllerTest.php index cd4a946..0b0a470 100644 --- a/FeastTests/Controllers/TemplateControllerTest.php +++ b/FeastTests/Controllers/TemplateControllerTest.php @@ -38,8 +38,8 @@ public function testInstallAction(): void ); $controller->installActionGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Action.php.txt',$output); - $this->assertStringContainsString('{action}{type}',$output); + $this->assertStringContainsString('Action.php.txt', $output); + $this->assertStringContainsString('{action}{type}', $output); } public function testCliActionInstallAction(): void @@ -54,8 +54,8 @@ public function testCliActionInstallAction(): void ); $controller->installCliActionGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('CliAction.php.txt',$output); - $this->assertStringContainsString('{action}{type}',$output); + $this->assertStringContainsString('CliAction.php.txt', $output); + $this->assertStringContainsString('{action}{type}', $output); } public function testControllerInstallAction(): void @@ -70,8 +70,8 @@ public function testControllerInstallAction(): void ); $controller->installControllerGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Controller.php.txt',$output); - $this->assertStringContainsString('class {name}Controller extends {cli}Controller',$output); + $this->assertStringContainsString('Controller.php.txt', $output); + $this->assertStringContainsString('class {name}Controller extends {cli}Controller', $output); } public function testCronJobAction(): void @@ -86,8 +86,8 @@ public function testCronJobAction(): void ); $controller->installCronJobGet();; $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('CronJob.php.txt',$output); - $this->assertStringContainsString('public function run(): bool',$output); + $this->assertStringContainsString('CronJob.php.txt', $output); + $this->assertStringContainsString('public function run(): bool', $output); } public function testFilterInstallAction(): void @@ -102,8 +102,8 @@ public function testFilterInstallAction(): void ); $controller->installFilterGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Filter.php.txt',$output); - $this->assertStringContainsString('public static function filter',$output); + $this->assertStringContainsString('Filter.php.txt', $output); + $this->assertStringContainsString('public static function filter', $output); } public function testFormInstallAction(): void @@ -118,8 +118,8 @@ public function testFormInstallAction(): void ); $controller->installFormGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Form.php.txt',$output); - $this->assertStringContainsString('parent::__construct(\'name\', \'url\', \'post\');',$output); + $this->assertStringContainsString('Form.php.txt', $output); + $this->assertStringContainsString('parent::__construct(\'name\', \'url\', \'post\');', $output); } public function testMapperInstallAction(): void @@ -134,8 +134,8 @@ public function testMapperInstallAction(): void ); $controller->installMapperGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Mapper.php.txt',$output); - $this->assertStringContainsString('protected const OBJECT_NAME',$output); + $this->assertStringContainsString('Mapper.php.txt', $output); + $this->assertStringContainsString('protected const OBJECT_NAME', $output); } public function testMigrationInstallAction(): void @@ -150,8 +150,8 @@ public function testMigrationInstallAction(): void ); $controller->installMigrationGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Migration.php.txt',$output); - $this->assertStringContainsString('protected const NAME',$output); + $this->assertStringContainsString('Migration.php.txt', $output); + $this->assertStringContainsString('protected const NAME', $output); } public function testModelInstallAction(): void @@ -166,8 +166,8 @@ public function testModelInstallAction(): void ); $controller->installModelGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Model.php.txt',$output); - $this->assertStringContainsString('// PLACE CUSTOM MODEL CODE HERE',$output); + $this->assertStringContainsString('Model.php.txt', $output); + $this->assertStringContainsString('// PLACE CUSTOM MODEL CODE HERE', $output); } public function testModelGeneratedInstallAction(): void @@ -182,8 +182,8 @@ public function testModelGeneratedInstallAction(): void ); $controller->installModelGeneratedGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('ModelGenerated.php.txt',$output); - $this->assertStringContainsString('extends BaseModel',$output); + $this->assertStringContainsString('ModelGenerated.php.txt', $output); + $this->assertStringContainsString('extends BaseModel', $output); } public function testPluginInstallAction(): void @@ -198,8 +198,8 @@ public function testPluginInstallAction(): void ); $controller->installPluginGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Plugin.php.txt',$output); - $this->assertStringContainsString('public function preDispatch()',$output); + $this->assertStringContainsString('Plugin.php.txt', $output); + $this->assertStringContainsString('public function preDispatch()', $output); } public function testQueueableJobInstallAction(): void @@ -214,8 +214,8 @@ public function testQueueableJobInstallAction(): void ); $controller->installQueueableJobGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('QueueableJob.php.txt',$output); - $this->assertStringContainsString('public function run(): bool',$output); + $this->assertStringContainsString('QueueableJob.php.txt', $output); + $this->assertStringContainsString('public function run(): bool', $output); } public function testServiceInstallAction(): void @@ -230,8 +230,8 @@ public function testServiceInstallAction(): void ); $controller->installServiceGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Service.php.txt',$output); - $this->assertStringContainsString('extends Service',$output); + $this->assertStringContainsString('Service.php.txt', $output); + $this->assertStringContainsString('extends Service', $output); } public function testValidatorInstallAction(): void @@ -246,7 +246,35 @@ public function testValidatorInstallAction(): void ); $controller->installValidatorGet(); $output = $this->getActualOutputForAssertion(); - $this->assertStringContainsString('Validator.php.txt',$output); - $this->assertStringContainsString('public static function validate',$output); + $this->assertStringContainsString('Validator.php.txt', $output); + $this->assertStringContainsString('public static function validate', $output); + } + + public function testInstallAllAction(): void + { + $config = $this->createStub(Config::class); + $config->method('getSetting')->willReturn(false); + + $controller = new TemplateController( + di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER), + $config, + new CliArguments(['famine', 'feast:template:install-validator']) + ); + $controller->installAllGet(); + $output = $this->getActualOutputForAssertion(); + $this->assertStringContainsString('Action.php.txt', $output); + $this->assertStringContainsString('CliAction.php.txt', $output); + $this->assertStringContainsString('Controller.php.txt', $output); + $this->assertStringContainsString('CronJob.php.txt', $output); + $this->assertStringContainsString('Filter.php.txt', $output); + $this->assertStringContainsString('Form.php.txt', $output); + $this->assertStringContainsString('Mapper.php.txt', $output); + $this->assertStringContainsString('Migration.php.txt', $output); + $this->assertStringContainsString('Model.php.txt', $output); + $this->assertStringContainsString('ModelGenerated.php.txt', $output); + $this->assertStringContainsString('Plugin.php.txt', $output); + $this->assertStringContainsString('QueueableJob.php.txt', $output); + $this->assertStringContainsString('Service.php.txt', $output); + $this->assertStringContainsString('Validator.php.txt', $output); } } diff --git a/docs/cli.md b/docs/cli.md index 9278609..5011e4d 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -2,9 +2,8 @@ # The FEAST CLI -FEAST has many built in powerful tools for working with your application. -These tools range from file generation to running jobs to setting maintenance mode. -Below is a list of all the tools, their options, and their usage. +FEAST has many built in powerful tools for working with your application. These tools range from file generation to +running jobs to setting maintenance mode. Below is a list of all the tools, their options, and their usage. [feast:create](#feastcreate) @@ -12,6 +11,8 @@ Below is a list of all the tools, their options, and their usage. [feast:cache](#feastcache) +[feast:template](#feasttemplate) + [feast:job](#feastjob) [feast:maintenance](#feastmaintenance) @@ -20,23 +21,23 @@ Below is a list of all the tools, their options, and their usage. [help](#help) - ## feast:create -The `feast:create` CLI group allows you to easily create many different types of classes -automatically. This saves time on both file creation and bug fixing. When you -install FEAST, predefined templates are stored in `bin/templates`. You may -modify these templates for any custom boilerplate code. + +The `feast:create` CLI group allows you to easily create many different types of classes automatically. This saves time +on both file creation and bug fixing. When you install FEAST, predefined templates are stored in `bin/templates`. You +may modify these templates for any custom boilerplate code. ### feast:create:action + ``` php famine feast:create:action --type={(get-post-put-delete-patch)} --module={module} --noview={true|false} {controller} {action} ``` -The `feast:create:action` command allows you to create a Controller/Action/View file -in a single command. The controller name must be a valid class name. If a module is -passed in, the module must be a valid namespace (match against valid class names). -The action can be passed in with dashes in all lowercase or with camelcase. -The following naming rules will be applied: +The `feast:create:action` command allows you to create a Controller/Action/View file in a single command. The controller +name must be a valid class name. If a module is passed in, the module must be a valid namespace (match against valid +class names). + +The action can be passed in with dashes in all lowercase or with camelcase. The following naming rules will be applied: * View file name: the action name with a dash between words and all lowercase. * Action method: the action name in camelcase concatenated with the HTTP verb. @@ -45,6 +46,7 @@ The following naming rules will be applied: If a module is passed in, the url will be prefixed with /modulename Example: + ``` Controller name: TestIng Action name: sunny-day @@ -68,113 +70,146 @@ url: /testIng/sunny-day ``` ### feast:create:cli-action + ```php php famine feast:create:cli-action {controller} {action} ``` -The `feast:create:cli-action` is a shortcut for `feast:create:action --module=CLI --noview=true --type=get` See + +The `feast:create:cli-action` is a shortcut for `feast:create:action --module=CLI --noview=true --type=get` See [feast:create:action](#feastcreateaction) for more info. ### feast:create:cron-job + ``` php famine feast:create:cron-job {name} ``` -Create a Cron Job class at `/Jobs/Cron/{name}`. The name must be a valid PHP classname. -This cron job can be scheduled in the file `scheduled_jobs.php` + +Create a Cron Job class at `/Jobs/Cron/{name}`. The name must be a valid PHP classname. This cron job can be scheduled +in the file `scheduled_jobs.php` + ### feast:create:form + ``` php famine feast:create:form {name} ``` -Create a Form class at `/Form/{name}`. The name must be a valid PHP classname. -See [Forms](forms.md) for more information. + +Create a Form class at `/Form/{name}`. The name must be a valid PHP classname. See [Forms](forms.md) for more +information. + ### feast:create:form-filter + ``` php famine feast:create:form-filter {name} ``` + Create a Form Filter class at `/Form/Filter/{name}`. The name must be a valid PHP classname. Form Filters allow you to modify incoming input on forms. Several built in filters are provided in `/Feast/Form/Filter` See [Forms](forms.md) for more information. + ### feast:create:form-validator + ``` php famine feast:create:form-validator {name} ``` + Create a Form Validator class at `/Form/Validator/{name}`. The name must be a valid PHP classname. -Form Validators allow you to validate incoming input on forms. Several built in validators are provided in `/Feast/Form/Validator` +Form Validators allow you to validate incoming input on forms. Several built in validators are provided +in `/Feast/Form/Validator` See [Forms](forms.md) for more information. + ### feast:create:model + ``` php famine feast:create:model --connection={connection} --model={model} --overwrite={true|false} {table-name} ``` + Create a Model and corresponding mapper via database introspection. Three classes are created. -1. `Model\Generated\{Model}` - This class extends from `Feast\BaseModel` and contains all the properties from the database and should not be modified directly. -2. `Model\{Model}` - This class extends from the generated model. Any custom logic should go here, and will not be overwritten -even if the table is re-inspected. -3. `Mapper\{Model}Mapper` - This class extends from `Feast\BaseMapper` and contains Primary Key and database connection information -to allow simple dynamic SQL through the FEAST Query engine. - -If a model name is not passed in, the table name becomes the base name of the model class. By default, the `Default` connection is used. -If overwrite is not explicitly set to true, and the Mapper exists, it will not be overwritten. -The generated model will always be overwritten. The extended model will never be overwritten. +1. `Model\Generated\{Model}` - This class extends from `Feast\BaseModel` and contains all the properties from the + database and should not be modified directly. +2. `Model\{Model}` - This class extends from the generated model. Any custom logic should go here, and will not be + overwritten even if the table is re-inspected. +3. `Mapper\{Model}Mapper` - This class extends from `Feast\BaseMapper` and contains Primary Key and database connection + information to allow simple dynamic SQL through the FEAST Query engine. + +If a model name is not passed in, the table name becomes the base name of the model class. By default, the `Default` +connection is used. + +If overwrite is not explicitly set to true, and the Mapper exists, it will not be overwritten. The generated model will +always be overwritten. The extended model will never be overwritten. See [Working with Databases](models.md) for more information. + ### feast:create:plugin + ``` php famine feast:create:plugin {name} ``` + Create a Plugin class at `/Plugins`. The name must be a valid PHP classname. Plugins allow writing Pre and post dispatch hooks. See [Plugins](plugin.md) for more information. ### feast:create:queueable-job + ``` php famine feast:create:queueable-job {name} ``` -Create a Queueable Job class at `/Jobs/Queueable/{name}`. The name must be a valid PHP classname. -These jobs can be queued and run on a queue worker. See [Queues](queues.md) for more information. + +Create a Queueable Job class at `/Jobs/Queueable/{name}`. The name must be a valid PHP classname. These jobs can be +queued and run on a queue worker. See [Queues](queues.md) for more information. ### feast:create:service + ``` php famine feast:create:service {name} ``` -Create a Service class at `/Services/{name}`. The name must be a valid PHP classname. -Service classes allow working with HTTP requests in a simple, object-oriented manner. See [Services](services.md) for more information. + +Create a Service class at `/Services/{name}`. The name must be a valid PHP classname. Service classes allow working with +HTTP requests in a simple, object-oriented manner. See [Services](services.md) for more information. [Back to Top](#the-feast-cli) ## feast:migration -The `feast:migration` CLI group allows you to work with database migrations. These migrations allow for consistent -databases both across environments and across deployments. + +The `feast:migration` CLI group allows you to work with database migrations. These migrations allow for consistent +databases both across environments and across deployments. + ### feast:migration:create + ``` php famine feast:migration:create --file={file-suffix} migration-name ``` -Create a Migration file. The file will begin with `Migration` followed by a -generated sequential number, followed by either your supplied file suffix or a random string. -See [Working with Migrations](models.md#working-with-migrations) for more information. +Create a Migration file. The file will begin with `Migration` followed by a generated sequential number, followed by +either your supplied file suffix or a random string. See [Working with Migrations](models.md#working-with-migrations) +for more information. ### feast:migration:up + ``` php famine feast:migration:up {name} ``` -Run the specified migration. The name includes the numeric prefix but does not include `migration`. -For example, to run `migration1_migrations.php`, the command is `php famine feast:migration:up 1_migrations`. +Run the specified migration. The name includes the numeric prefix but does not include `migration`. For example, to +run `migration1_migrations.php`, the command is `php famine feast:migration:up 1_migrations`. See [Working with Migrations](models.md#working-with-migrations) for more information. ### feast:migration:down + ``` php famine feast:migration:down {name} ``` -Reverse the specified migration. The name includes the numeric prefix but does not include `migration`. -For example, to run `migration1_migrations.php`, the command is `php famine feast:migration:down 1_migrations`. -To reverse a migration, the migration must define the correct behavior in the `down` method. +Reverse the specified migration. The name includes the numeric prefix but does not include `migration`. For example, to +run `migration1_migrations.php`, the command is `php famine feast:migration:down 1_migrations`. To reverse a migration, +the migration must define the correct behavior in the `down` method. See [Working with Migrations](models.md#working-with-migrations) for more information. ### feast:migration:run-all + ``` php famine feast:migration:run-all ``` @@ -185,81 +220,215 @@ See [Working with Migrations](models.md#working-with-migrations) for more inform [Back to Top](#the-feast-cli) ## feast:cache -The `feast:cache` CLI group allows caching certain configuration details to speed up requests by reducing pre-processing. -Currently, config, routing, and basic database introspection information are supported. -It is **strongly** encouraged to use the cache in production but not required in development. +The `feast:cache` CLI group allows caching certain configuration details to speed up requests by reducing +pre-processing. Currently, config, routing, and basic database introspection information are supported. + +It is **strongly** encouraged to use the cache in production but not required in development. ### feast:cache:config-clear + ``` php famine feast:cache:config-clear ``` + This command will delete the config cache and resume building on each request. ### feast:cache:config-generate + ``` php famine feast:cache:config-generate ``` + This command will re-generate the config cache. No changes to the configuration files will take effect unless the cache is cleared or re-generated again. ### feast:cache:route-clear + ``` php famine feast:cache:route-clear ``` + This command will delete the routing cache and resume building on each request. ### feast:cache:routing-generate + ``` php famine feast:cache:routing-generate ``` -This command will re-generate the routing cache. No changes to the URL routing information will take effect unless the cache -is cleared or re-generated again. + +This command will re-generate the routing cache. No changes to the URL routing information will take effect unless the +cache is cleared or re-generated again. ### feast:cache:dbinfo-clear + ``` php famine feast:cache:dbinfo-clear ``` + This command will delete the database info cache, instead enabling table introspection on each request. ### feast:cache:dbinfo-generate + ``` php famine feast:cache:dbinfo-generate ``` -This command will re-generate the database info cache. No changes to the database schema will be detected -unless the cache is cleared or regenerated again. If you run any migrations, you MUST regenerate if using -the cache. + +This command will re-generate the database info cache. No changes to the database schema will be detected unless the +cache is cleared or regenerated again. If you run any migrations, you MUST regenerate if using the cache. [Back to Top](#the-feast-cli) +## feast:template + +FEAST comes with several built in templates that are used to generate code. These files are not installed by default, +but are available to the CLI tool at all times. If you wish to modify any of the templates, you should first install +them, rather than editing directly. The `feast:template` group allows installation of the built in template files FEAST +uses with the [feast:create](#feastcreate) and [feast:migration:create](#feastmigrationcreate). The following actions +are available. + +### feast:template:install-action + +``` +php famine feast:template:install-action +``` + +This command installs the `action` template file to `bin/templates/Action.php.txt`. + +### feast:template:install-cli-action +``` +php famine feast:template:install-cli-action +``` + +This command installs the `cliAction` template file to `bin/templates/CliAction.php.txt`. + +### feast:template:install-controller +``` +php famine feast:template:install-controller +``` + +This command installs the `controller` template file to `bin/templates/Controller.php.txt`. + +### feast:template:install-cron-job +``` +php famine feast:template:install-cron-job +``` + +This command installs the `cronJob` template file to `bin/templates/CronJob.php.txt`. + +### feast:template:install-filter +``` +php famine feast:template:install-filter +``` + +This command installs the `filter` template file to `bin/templates/Filter.php.txt`. + +### feast:template:install-form +``` +php famine feast:template:install-form +``` + +This command installs the `form` template file to `bin/templates/Form.php.txt`. + +### feast:template:install-mapper +``` +php famine feast:template:install-mapper +``` + +This command installs the `mapper` template file to `bin/templates/Mapper.php.txt`. + +### feast:template:install-migration +``` +php famine feast:template:install-migration +``` + +This command installs the `migration` template file to `bin/templates/Migration.php.txt`. + +### feast:template:install-model +``` +php famine feast:template:install-model +``` + +This command installs the `model` template file to `bin/templates/Model.php.txt`. + +### feast:template:install-model-generated +``` +php famine feast:template:install-action +``` + +This command installs the `modelGenerated` template file to `bin/templates/ModelGenerated.php.txt`. + +### feast:template:install-plugin +``` +php famine feast:template:install-plugin +``` + +This command installs the `plugin` template file to `bin/templates/Plugin.php.txt`. + +### feast:template:install-queueable-job +``` +php famine feast:template:install-queueable-job +``` + +This command installs the `queueableJob` template file to `bin/templates/QueueableJob.php.txt`. + +### feast:template:install-service +``` +php famine feast:template:install-service +``` + +This command installs the `service` template file to `bin/templates/Service.php.txt`. + +### feast:template:install-validator +``` +php famine feast:template:install-validator +``` + +This command installs the `action` template file to `bin/templates/Action.php.txt`. + +### feast:template:install-all +``` +php famine feast:template:install-all +``` + +This command installs all template files. + ## feast:job + The `feast:job` group allows interacting with both scheduled (or cron) jobs and queueable jobs. ### feast:job:listen + ``` php famine feast:job:listen --keepalive={true|false} {queues} ``` -Listen for available jobs on the specified queues. Queues are separated by the pipe character. -Keep Alive defaults to true. If no jobs are available, the worker will sleep for 10 seconds before checking again. -All jobs are processed by the worker in a first-in, first-out manner. If a job fails, it is placed back into the queue -and retried up to the maximum number of tries. + +Listen for available jobs on the specified queues. Queues are separated by the pipe character. Keep Alive defaults to +true. If no jobs are available, the worker will sleep for 10 seconds before checking again. All jobs are processed by +the worker in a first-in, first-out manner. If a job fails, it is placed back into the queue and retried up to the +maximum number of tries. ### feast:job:run-one + ``` php famine feast:job:run-one {job} ``` -Manually run the specified job by Job ID. The job will re-run even if the maximum try count has been reached. This allows -debugging a job manually to look for what errors occurred. + +Manually run the specified job by Job ID. The job will re-run even if the maximum try count has been reached. This +allows debugging a job manually to look for what errors occurred. ### feast:job:run-cron + ``` php famine feast:job:run-cron ``` + Run all scheduled job items. See [Queues](queues.md) for more information. ``` php famine feast:job:run-cron-tem {job} ``` + Run the specified job item. The job will run even if it is not inside its assigned schedule. See [Queues](queues.md) for more information. @@ -267,38 +436,48 @@ See [Queues](queues.md) for more information. [Back to Top](#the-feast-cli) ## feast:maintenance + The `feast:maintenance` CLI group allows taking your website offline quickly in the event of needed maintenance. ### feast:maintenance:start + ``` php famine feast:maintenance:start ``` + Start maintenance mode. CLI tools will still work, but scheduled jobs will be halted and cron jobs will only run if `evenInMaintenanceMode()` has been specified for the job. The Maintenance view will generate a static file that will be served to all requests. ### feast:maintenance:start + ``` php famine feast:maintenance:stop ``` -Stop maintenance mode. Scheduled jobs and cron jobs will resume, and the maintenance static file will be replaced with -a javascript based redirect to the home page. + +Stop maintenance mode. Scheduled jobs and cron jobs will resume, and the maintenance static file will be replaced with a +javascript based redirect to the home page. [Back to Top](#the-feast-cli) ## feast:serve + ### feast:serve:serve + ``` php famine feast:serve:serve --hostname={hostname} --port={port-number} --workers={worker-count} ``` + Run PHP's built-in development server. By default, listens on localhost, port 8000, with a single worker. [Back to Top](#the-feast-cli) ## help + ``` php famine help {command} ``` + View usage info and parameter information for a command. [Back to Top](#the-feast-cli) \ No newline at end of file From 12d9d02b63c31f36b397bec2598ee48af2ebe564 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sat, 23 Oct 2021 23:27:10 -0400 Subject: [PATCH 15/56] Consistent cli output --- Controllers/CacheController.php | 12 ++++++------ Controllers/TemplateController.php | 30 +++++++++++++++--------------- FeastTests/HelpTest.php | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Controllers/CacheController.php b/Controllers/CacheController.php index 051cd7e..236d94b 100644 --- a/Controllers/CacheController.php +++ b/Controllers/CacheController.php @@ -32,7 +32,7 @@ class CacheController extends CliController protected const ROUTER_FILE_NAME = 'router.cache'; protected const DATABASE_FILE_NAME = 'database.cache'; - #[Action(description: 'Clear config cache file')] + #[Action(description: 'Clear config cache file.')] public function configClearGet(): void { $cachePath = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; @@ -42,7 +42,7 @@ public function configClearGet(): void $this->terminal->command('Config cache cleared!'); } - #[Action(description: 'Clear config cache file (if any) and regenerate')] + #[Action(description: 'Clear config cache file (if any) and regenerate.')] public function configGenerateGet(): void { $config = new Config(false); @@ -50,7 +50,7 @@ public function configGenerateGet(): void $this->terminal->command('Config cached!'); } - #[Action(description: 'Clear router cache file')] + #[Action(description: 'Clear router cache file.')] public function routerClearGet(): void { $cachePath = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; @@ -60,7 +60,7 @@ public function routerClearGet(): void $this->terminal->command('Router cache cleared!'); } - #[Action(description: 'Clear router cache file (if any) and regenerate')] + #[Action(description: 'Clear router cache file (if any) and regenerate.')] public function routerGenerateGet( RouterInterface $router ): void { @@ -68,7 +68,7 @@ public function routerGenerateGet( $this->terminal->command('Router cached!'); } - #[Action(description: 'Clear database info cache file')] + #[Action(description: 'Clear database info cache file.')] public function dbinfoClearGet(): void { $cachePath = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; @@ -78,7 +78,7 @@ public function dbinfoClearGet(): void $this->terminal->command('Database info cache cleared!'); } - #[Action(description: 'Clear database info cache file (if any) and regenerate')] + #[Action(description: 'Clear database info cache file (if any) and regenerate.')] public function dbinfoGenerateGet( DatabaseDetailsInterface $databaseDetails ): void { diff --git a/Controllers/TemplateController.php b/Controllers/TemplateController.php index 85fc9df..c02ce57 100644 --- a/Controllers/TemplateController.php +++ b/Controllers/TemplateController.php @@ -27,7 +27,7 @@ class TemplateController extends CliController { - #[Action(description: 'Copy "Action" template to bin/templates folder')] + #[Action(description: 'Copy "Action" template to bin/templates folder.')] public function installActionGet(): void { $this->installFile('Action.php.txt'); @@ -36,7 +36,7 @@ public function installActionGet(): void ); } - #[Action(description: 'Copy "CliAction" template to bin/templates folder')] + #[Action(description: 'Copy "CliAction" template to bin/templates folder.')] public function installCliActionGet(): void { $this->installFile('CliAction.php.txt'); @@ -45,7 +45,7 @@ public function installCliActionGet(): void ); } - #[Action(description: 'Copy "Controller" template to bin/templates folder')] + #[Action(description: 'Copy "Controller" template to bin/templates folder.')] public function installControllerGet(): void { $this->installFile('Controller.php.txt'); @@ -54,7 +54,7 @@ public function installControllerGet(): void ); } - #[Action(description: 'Copy "CronJob" template to bin/templates folder')] + #[Action(description: 'Copy "CronJob" template to bin/templates folder.')] public function installCronJobGet(): void { $this->installFile('CronJob.php.txt'); @@ -63,7 +63,7 @@ public function installCronJobGet(): void ); } - #[Action(description: 'Copy "Filter" template to bin/templates folder')] + #[Action(description: 'Copy "Filter" template to bin/templates folder.')] public function installFilterGet(): void { $this->installFile('Filter.php.txt'); @@ -72,7 +72,7 @@ public function installFilterGet(): void ); } - #[Action(description: 'Copy "Form" template to bin/templates folder')] + #[Action(description: 'Copy "Form" template to bin/templates folder.')] public function installFormGet(): void { $this->installFile('Form.php.txt'); @@ -81,7 +81,7 @@ public function installFormGet(): void ); } - #[Action(description: 'Copy "Mapper" template to bin/templates folder')] + #[Action(description: 'Copy "Mapper" template to bin/templates folder.')] public function installMapperGet(): void { $this->installFile('Mapper.php.txt'); @@ -90,7 +90,7 @@ public function installMapperGet(): void ); } - #[Action(description: 'Copy "Migration" template to bin/templates folder')] + #[Action(description: 'Copy "Migration" template to bin/templates folder.')] public function installMigrationGet(): void { $this->installFile('Migration.php.txt'); @@ -99,7 +99,7 @@ public function installMigrationGet(): void ); } - #[Action(description: 'Copy "Model" template to bin/templates folder')] + #[Action(description: 'Copy "Model" template to bin/templates folder.')] public function installModelGet(): void { $this->installFile('Model.php.txt'); @@ -108,7 +108,7 @@ public function installModelGet(): void ); } - #[Action(description: 'Copy "ModelGenerated" template to bin/templates folder')] + #[Action(description: 'Copy "ModelGenerated" template to bin/templates folder.')] public function installModelGeneratedGet(): void { $this->installFile('ModelGenerated.php.txt'); @@ -117,7 +117,7 @@ public function installModelGeneratedGet(): void ); } - #[Action(description: 'Copy "Plugin" template to bin/templates folder')] + #[Action(description: 'Copy "Plugin" template to bin/templates folder.')] public function installPluginGet(): void { $this->installFile('Plugin.php.txt'); @@ -126,7 +126,7 @@ public function installPluginGet(): void ); } - #[Action(description: 'Copy "QueueableJob" template to bin/templates folder')] + #[Action(description: 'Copy "QueueableJob" template to bin/templates folder.')] public function installQueueableJobGet(): void { $this->installFile('QueueableJob.php.txt'); @@ -135,7 +135,7 @@ public function installQueueableJobGet(): void ); } - #[Action(description: 'Copy "Service" template to bin/templates folder')] + #[Action(description: 'Copy "Service" template to bin/templates folder.')] public function installServiceGet(): void { $this->installFile('Service.php.txt'); @@ -144,7 +144,7 @@ public function installServiceGet(): void ); } - #[Action(description: 'Copy "Validator" template to bin/templates folder')] + #[Action(description: 'Copy "Validator" template to bin/templates folder.')] public function installValidatorGet(): void { $this->installFile('Validator.php.txt'); @@ -153,7 +153,7 @@ public function installValidatorGet(): void ); } - #[Action(description: 'Copy all template to bin/templates folder')] + #[Action(description: 'Copy all template to bin/templates folder.')] public function installAllGet(): void { $this->installActionGet(); diff --git a/FeastTests/HelpTest.php b/FeastTests/HelpTest.php index c716d69..a3787e0 100644 --- a/FeastTests/HelpTest.php +++ b/FeastTests/HelpTest.php @@ -67,7 +67,7 @@ public function testPrintCliMethodHelpWithoutParams(): void ' Usage: php famine feast:cache:config-generate -Clear config cache file (if any) and regenerate +Clear config cache file (if any) and regenerate. ' ); From aa82711d54c4544c16a116ac217cae0d04b919bb Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 24 Oct 2021 12:10:58 -0400 Subject: [PATCH 16/56] Minor tweaks, no functional changes --- Router/Router.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Router/Router.php b/Router/Router.php index d19e720..5ea0aeb 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -406,9 +406,8 @@ public function getControllerFullyQualifiedName(): string $controllerNamespace = '\Feast\Controllers\\'; } - /** @var class-string $classString */ - $classString = $controllerNamespace . $this->getControllerClass(); - return $classString; + /** @var class-string */ + return $controllerNamespace . $this->getControllerClass(); } /** @@ -419,7 +418,6 @@ public function getControllerFullyQualifiedName(): string */ public function getActionMethodName(): string { - /** @var class-string $controllerName */ $controllerName = $this->getControllerFullyQualifiedName(); $actionName = substr($this->getAction(), 0, -6); $request = di(RequestInterface::class); From 905413faf32423bd00500ac3ea9a93ccb5c3dab7 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 24 Oct 2021 12:12:41 -0400 Subject: [PATCH 17/56] Doc update for 8.1 --- docs/index.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index a4aa32c..44600fa 100644 --- a/docs/index.md +++ b/docs/index.md @@ -67,8 +67,10 @@ up with the following principles always in mind. ## Requirements -The current release of FEAST requires PHP 8.0 and (if you wish to use the Curl service classes) PHP 8.0-curl. In -addition, PHP 8.0-bcmath is recommended. +The current release of FEAST requires >=PHP 8.1 and (if you wish to use the Curl service classes) PHP 8.1-curl. In +addition, PHP 8.1-bcmath is recommended. + +The v1.x line of FEAST requires >=PHP 8.0. The same recommendations above apply The release plan for FEAST can be found [here](release-schedule.md). From ce30f16449bd65ab3e007a55c7070be74c955709 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 24 Oct 2021 12:12:51 -0400 Subject: [PATCH 18/56] Fix php badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6083917..6f6dbf6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ![Psalm Static analysis](https://github.com/FeastFramework/framework/workflows/Psalm%20Static%20analysis/badge.svg?branch=master) [![codecov](https://codecov.io/gh/FeastFramework/framework/branch/master/graph/badge.svg?token=FBP2AKLJB3)](https://codecov.io/gh/FeastFramework/framework) -![PHP Version](https://img.shields.io/packagist/php-v/feast/json) +![PHP Version](https://img.shields.io/packagist/php-v/feast/framework) [![Packagist](https://img.shields.io/packagist/v/feast/framework)](https://packagist.org/packages/feast/framework) ![License](https://img.shields.io/packagist/l/feast/framework.svg) [![Docs](https://img.shields.io/badge/docs-quickstart-green.svg)](https://docs.feast-framework.com) From 9dcc853f557424051546ecd1bf1c2176cf9393d3 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 24 Oct 2021 15:09:38 -0400 Subject: [PATCH 19/56] Update for docblocks --- BaseMapper.php | 21 +++++---- BaseModel.php | 6 +++ Binary.php | 16 ++++--- CliController.php | 4 ++ Collection/CollectionList.php | 14 +++--- Collection/Set.php | 26 ++++++------ Config/Config.php | 38 ++++++++--------- Controllers/CreateController.php | 5 ++- Controllers/JobController.php | 21 +++++++-- Controllers/MaintenanceController.php | 4 ++ Controllers/MigrationController.php | 20 +++++++-- Csv/CsvReader.php | 5 ++- Database/Database.php | 19 +++++---- Database/DatabaseDetails.php | 16 ++++++- Database/DatabaseFactory.php | 13 +++--- Database/MySQLQuery.php | 10 +++-- Database/Query.php | 11 ++++- Date.php | 25 ++++++++--- Email/Email.php | 20 ++++----- Exception/Error404Exception.php | 3 +- Exception/ServerFailureException.php | 9 +++- Exception/ThrottleException.php | 3 +- Form/Field.php | 6 ++- Form/Form.php | 14 +++--- Help.php | 20 +++++---- HttpController.php | 7 ++- HttpRequest/Curl.php | 11 ++++- HttpRequest/HttpRequest.php | 5 ++- HttpRequest/Response.php | 8 ++-- Install/bootstrap.php | 4 +- Install/container.php | 4 +- Install/famine | 9 ++-- Install/public/index.php | 10 +++-- Interfaces/ErrorLoggerInterface.php | 6 ++- Interfaces/HttpRequestInterface.php | 5 ++- Interfaces/RequestInterface.php | 3 +- Jobs/CronJob.php | 4 ++ Jobs/QueueableJob.php | 21 +++++---- Json.php | 31 ++++++++------ Logger/ErrorLogger.php | 6 ++- Logger/Logger.php | 5 +++ Main.php | 61 ++++++++++++++++++++------- NameHelper.php | 12 +++--- Profiler/Profiler.php | 7 ++- PsalmLoader.php | 8 ++-- Request.php | 24 +++++++++-- Response.php | 5 ++- Router/Router.php | 35 +++++++++++---- Service.php | 11 ++++- ServiceContainer/ServiceContainer.php | 3 ++ Session/Identity.php | 12 ++++-- Session/Session.php | 15 ++++--- Traits/Collection.php | 5 ++- Traits/DependencyInjected.php | 5 ++- View.php | 1 + bootstrap.php | 7 ++- composer.json | 3 +- 57 files changed, 467 insertions(+), 235 deletions(-) diff --git a/BaseMapper.php b/BaseMapper.php index 85b12aa..c0393b6 100644 --- a/BaseMapper.php +++ b/BaseMapper.php @@ -28,6 +28,7 @@ use Feast\Interfaces\DatabaseFactoryInterface; use Feast\Interfaces\DatabaseInterface; use PDO; +use stdClass; /** * @psalm-consistent-constructor @@ -42,6 +43,10 @@ abstract class BaseMapper final public const NOT_NULL = 'not_null'; protected DatabaseInterface $connection; + /** + * @throws ServiceContainer\NotFoundException + * @throws ServerFailureException + */ public function __construct() { $this->connection = di(DatabaseFactoryInterface::class)->getConnection((string)static::CONNECTION); @@ -72,7 +77,7 @@ protected function map(array $row, array $dataTypes): BaseModel } $return->$key = match ($dataTypes[$key]) { Date::class => Date::createFromString((string)$val), - \stdClass::class => (object)json_decode( + stdClass::class => (object)json_decode( (string)$val ), 'int' => (int)$val, @@ -221,10 +226,8 @@ public function fetchOne(Query $select = null): ?BaseModel $select = $select ?? $this->getQueryBase(); $select->limit(1); - /** @var BaseModel|null $return */ - $return = $this->fetchAll($select)->first(); - - return $return; + /** @var BaseModel|null */ + return $this->fetchAll($select)->first(); } /** @@ -232,7 +235,7 @@ public function fetchOne(Query $select = null): ?BaseModel * * @param Query|null $select * @return Set - * @throws ServerFailureException|ServiceContainer\NotFoundException + * @throws ServerFailureException|ServiceContainer\NotFoundException|Exception */ public function fetchAll(Query $select = null): Set { @@ -313,7 +316,7 @@ protected function buildFieldDataForSave(BaseModel|null $originalObject, array $ $return = []; /** * @var string $field - * @var int|string|Date|null|\stdClass|array $val + * @var int|string|Date|null|stdClass|array $val */ foreach ($fields as $field => $val) { if ($originalObject !== null && $originalObject->$field === $val) { @@ -322,7 +325,7 @@ protected function buildFieldDataForSave(BaseModel|null $originalObject, array $ if ($val instanceof Date) { $val = $val->getFormattedDate(); } - if ($val instanceof \stdClass || is_array($val)) { + if ($val instanceof stdClass || is_array($val)) { $val = json_encode($val); } $return[$field] = $val; @@ -398,7 +401,7 @@ public function delete(BaseModel $record): int $recordPrimaryKey ); $statement = $update->execute(); - + $this->onDelete($record); return $statement->rowCount(); } diff --git a/BaseModel.php b/BaseModel.php index 59f60e4..5a705ac 100755 --- a/BaseModel.php +++ b/BaseModel.php @@ -33,11 +33,17 @@ abstract class BaseModel protected const MAPPER_NAME = null; protected ?BaseModel $originalModel = null; + /** + * @throws InvalidOptionException + */ public function __set(string $name, mixed $value): void { throw new InvalidOptionException('Invalid option for model', ResponseCode::HTTP_CODE_500); } + /** + * @throws InvalidOptionException + */ public function __get(string $name): void { throw new InvalidOptionException('Invalid option for model', ResponseCode::HTTP_CODE_500); diff --git a/Binary.php b/Binary.php index 14aac77..f90f883 100644 --- a/Binary.php +++ b/Binary.php @@ -30,7 +30,9 @@ use Feast\Controllers\TemplateController; use Feast\Exception\NotFoundException; use Feast\Interfaces\MainInterface; +use ReflectionClass; use ReflectionException; +use ReflectionMethod; class Binary { @@ -216,9 +218,9 @@ private function help(string $command): void /** * Process description attribute and return whether any methods were found. * - * @param \ReflectionMethod $method + * @param ReflectionMethod $method */ - private function processCliMethods(\ReflectionMethod $method): void + private function processCliMethods(ReflectionMethod $method): void { $name = NameHelper::getMethodNameAsCallableAction($method->getName()); $class = NameHelper::getControllerClassName($method->getDeclaringClass()); @@ -258,12 +260,14 @@ private function analyzeFeast( ): void { /** @var class-string $class */ foreach ($classes as $class) { - $this->processCliClass(new \ReflectionClass($class)); + $this->processCliClass(new ReflectionClass($class)); } } /** * Process all custom actions in the CLI module. + * + * @throws ReflectionException */ private function analyzeCli(): void { @@ -277,7 +281,7 @@ private function analyzeCli(): void // Load class info /** @var class-string $className */ $className = '\\Modules\\CLI\\Controllers\\' . substr($classFile, 0, -4); - $class = new \ReflectionClass($className); + $class = new ReflectionClass($className); // Parse class and methods $this->processCliClass($class); @@ -288,9 +292,9 @@ private function analyzeCli(): void /** * Parse all descriptions for a class and return if any methods had a description. * - * @param \ReflectionClass $class + * @param ReflectionClass $class */ - private function processCliClass(\ReflectionClass $class): void + private function processCliClass(ReflectionClass $class): void { $className = NameHelper::getControllerClassName($class); $this->terminal->command($className); diff --git a/CliController.php b/CliController.php index 24cf7c6..5332eb8 100755 --- a/CliController.php +++ b/CliController.php @@ -22,12 +22,16 @@ use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\ControllerInterface; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainer; abstract class CliController implements ControllerInterface { protected Terminal $terminal; + /** + * @throws NotFoundException + */ public function __construct( ServiceContainer $di, ?ConfigInterface $config = null, diff --git a/Collection/CollectionList.php b/Collection/CollectionList.php index ba6667f..b946a7c 100755 --- a/Collection/CollectionList.php +++ b/Collection/CollectionList.php @@ -20,6 +20,7 @@ namespace Feast\Collection; +use ArrayAccess; use Feast\Exception\ServerFailureException; /** @@ -27,7 +28,7 @@ * * @package Feast\Collection */ -class CollectionList implements \ArrayAccess,Collection +class CollectionList implements ArrayAccess, Collection { use \Feast\Traits\Collection; @@ -48,7 +49,6 @@ public function __construct(protected string $type = 'mixed', array $values = [] $this->addAll($values); } - /** * Add or replace an element to/in the collection * @@ -117,13 +117,10 @@ public function offsetExists($offset): bool */ public function offsetGet($offset): string|int|bool|float|object|array|null { - /** @var string|int|bool|float|object|array|null $var */ - $var = $this->array[$offset] ?? null; - - return $var; + /** @var string|int|bool|float|object|array|null */ + return $this->array[$offset] ?? null; } - /** * Set element by offset * @@ -138,7 +135,7 @@ public function offsetSet($offset, $value): void /** * Unset element (if it exists) by offset - * + * * @param string|int $offset */ public function offsetUnset($offset): void @@ -146,5 +143,4 @@ public function offsetUnset($offset): void unset($this->array[$offset]); } - } diff --git a/Collection/Set.php b/Collection/Set.php index a18b487..99a4d2a 100755 --- a/Collection/Set.php +++ b/Collection/Set.php @@ -20,6 +20,8 @@ namespace Feast\Collection; +use ArrayAccess; +use Countable; use Feast\Exception\InvalidOptionException; use Feast\Exception\ServerFailureException; use Feast\Exception\InvalidArgumentException; @@ -30,7 +32,7 @@ * * @package Feast\Set */ -class Set implements Iterator, Collection, \ArrayAccess, \Countable +class Set implements Iterator, Collection, ArrayAccess, Countable { use \Feast\Traits\Collection; @@ -55,7 +57,7 @@ public function __construct( } $this->addAll($values); } - + /** * Add a value to the set * @@ -85,10 +87,10 @@ public function addAll(array $values): void $this->add($value); } } - + /** * Set element by offset (disabled) - * + * * @param string|int $offset * @param mixed $value * @throws ServerFailureException @@ -111,9 +113,9 @@ public function offsetUnset($offset): void /** * Merge two sets together - * + * * For a merge to be allowed, the type for the two sets MUST be the same. - * + * * @param Set $dataToMerge * @return static * @throws InvalidArgumentException @@ -133,12 +135,12 @@ public function merge(Set $dataToMerge): static /** * Get minimum value from set. - * + * * If key is passed and the collection type is not a scalar, then the value used is for the specified * key on the objects. If key is not passed, this will operate on float/int sets only. If key is passed - * and the collection is not an object type, or no key is passed and it is an object type, an + * and the collection is not an object type, or no key is passed and it is an object type, an * InvalidOptionException is thrown. - * + * * @param string|null $key * @return int|float|null * @throws InvalidOptionException @@ -265,10 +267,8 @@ public function product(?string $key = null): float */ public function offsetGet($offset): string|int|bool|float|object|array|null { - /** @var string|int|bool|float|object|array|null $var */ - $var = $this->array[$offset] ?? null; - - return $var; + /** @var string|int|bool|float|object|array|null */ + return $this->array[$offset] ?? null; } /** diff --git a/Config/Config.php b/Config/Config.php index b6b2356..feb87ee 100755 --- a/Config/Config.php +++ b/Config/Config.php @@ -80,10 +80,10 @@ public function getEnvironmentName(): string /** * Get config setting. Returns default if setting not found. - * + * * The Config key can be a parent value or nested via "." separation * If a "." is in the key, the settings will be fetched recursively. - * The default will be returned if any key in the path is not found. + * The default will be returned if any key in the path is not found. * * @param string $key * @param mixed $default @@ -108,7 +108,7 @@ public function getSetting(string $key, mixed $default = null): mixed /** * Builds out the config file in order, one section at a time - * + * * @param array $config * @return stdClass */ @@ -129,7 +129,7 @@ protected function buildConfigData(array $config): stdClass /** * Build config environment, running through inheritance rules. - * + * * @param stdClass $configData * @param string $environmentName * @param array $section @@ -148,7 +148,7 @@ protected function buildEnvironment(stdClass $configData, string $environmentNam /** * Recursively merge config settings from one environment to another. - * + * * @param stdClass $currentEnvironment * @param array $parentEnvironment * @return array @@ -157,7 +157,7 @@ protected function configMergeRecursive(stdClass $currentEnvironment, array $par { /** @var array> $baselineConfig */ $baselineConfig = $this->objectToArray($currentEnvironment); - + /** * @var string $keyBase * @var string|int|bool|float|array $val @@ -166,7 +166,7 @@ protected function configMergeRecursive(stdClass $currentEnvironment, array $par /** @var array $keyList */ $key = explode('.', $keyBase); $lastKey = array_pop($key); - + // Config item is assigned via reference for nesting buildout. $configItem = &$baselineConfig; foreach ($key as $currentKey) { @@ -176,7 +176,7 @@ protected function configMergeRecursive(stdClass $currentEnvironment, array $par /** @var array $configItem */ $configItem = &$configItem[$currentKey]; } - + // If an array, recursively merge the next level if (is_array($val)) { if (!isset($configItem[$lastKey]) || !is_array($configItem[$lastKey])) { @@ -218,7 +218,7 @@ private function buildConfigFromFile(): stdClass /** * Determine the correct env for the current running application. - * + * * Environment is determined in the following order: * 1. Web server/process ENV setting * 2. .appenv file (contains just env name) @@ -241,13 +241,13 @@ private function getEnvironmentForConfig(): string return trim($environment); } } - + return 'production'; } /** * Build environment config by name. - * + * * Names are colon separation based inheritance. Example: * 1. "production" - all settings are standalone * 2. "production : development" - production is cloned, then development settings are applied. @@ -269,7 +269,9 @@ private function buildInheritedEnvironments(stdClass $config, string &$environme * @var string|int|bool|stdClass $val */ foreach ($config->{$parentEnvironment} as $key => $val) { - $config->$environmentName->$key = $val instanceof stdClass ? $this->cloneObjectOrArrayAsObject($val) : $val; + $config->$environmentName->$key = $val instanceof stdClass ? $this->cloneObjectOrArrayAsObject( + $val + ) : $val; } } } @@ -292,18 +294,14 @@ private function addLocalConfig(): void private function cloneObjectOrArrayAsObject(stdClass|array $settings): stdClass { - /** @var stdClass $config */ - $config = json_decode(json_encode($settings)); - - return $config; + /** @var stdClass */ + return json_decode(json_encode($settings)); } private function objectToArray(stdClass $object): array { - /** @var array $config */ - $config = json_decode(json_encode($object), true); - - return $config; + /** @var array */ + return json_decode(json_encode($object), true); } } diff --git a/Controllers/CreateController.php b/Controllers/CreateController.php index 3c05921..dbc9469 100644 --- a/Controllers/CreateController.php +++ b/Controllers/CreateController.php @@ -23,9 +23,9 @@ use Feast\Attributes\Action; use Feast\Attributes\Param; use Feast\BaseModel; -use Feast\CliController; use Feast\Database\TableDetails; use Feast\Enums\ParamType; +use Feast\Exception\ServerFailureException; use Feast\Interfaces\DatabaseFactoryInterface; use Feast\NameHelper; @@ -167,6 +167,9 @@ public function formValidatorGet( $this->writeSimpleTemplate($name, 'Validator', $file); } + /** + * @throws ServerFailureException + */ #[Action(usage: '--connection={connection} --model={model} --overwrite={true|false} {table-name}', description: 'Create a model and mapper for a database table.')] #[Param(type: 'string', name: 'table', description: 'Name of table to build model from')] #[Param(type: 'string', name: 'connection', description: 'Name of connection to use for database connection. Defaults to "default"', paramType: ParamType::FLAG)] diff --git a/Controllers/JobController.php b/Controllers/JobController.php index a6dd2e0..245048d 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -20,6 +20,7 @@ namespace Feast\Controllers; +use Exception; use Feast\Attributes\Action; use Feast\Attributes\Param; use Feast\CliController; @@ -68,6 +69,10 @@ public function listenGet( } while ($keepalive && !$exitLoop); } + /** + * @throws \Feast\ServiceContainer\NotFoundException + * @throws \Feast\Exception\ServerFailureException + */ #[Action(usage: '{job}', description: 'Run the specified job. Job will run even if max count exceeded.')] #[Param(type: 'string', name: 'job', description: 'Job ID of job to run (uuid)')] public function runOneGet( @@ -101,6 +106,9 @@ public function runOneGet( } } + /** + * @throws \Feast\Exception\InvalidDateException + */ #[Action(description: 'Run all cron jobs.')] public function runCronGet( ?Date $now, @@ -109,7 +117,6 @@ public function runCronGet( $now ??= Date::createFromNow(); $filePath = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'scheduled_jobs.php'; $privateProcess = $this->shouldRunAsPrivateProcess($config); - /** @noinspection PhpIncludeInspection */ /** @var array $scheduledJobs */ $scheduledJobs = file_exists($filePath) ? require($filePath) : []; foreach ($scheduledJobs as $job) { @@ -119,6 +126,9 @@ public function runCronGet( } } + /** + * @throws \Feast\Exception\InvalidDateException + */ #[Action(usage: '{job}', description: 'Run the specified job.')] #[Param(type: 'string', name: 'job', description: 'Job class to run')] public function runCronItemGet( @@ -143,13 +153,16 @@ public function runCronItemGet( $jobProcess->startRun($now); try { $jobProcess->run(); - } catch (\Exception) { + } catch (Exception) { // Empty catch } $jobProcess->stopRun($now); } - protected function runJob(\Model\Job $job, LoggerInterface $logger, JobMapper $jobMapper): bool + /** + * @throws Exception + */ + protected function runJob(Job $job, LoggerInterface $logger, JobMapper $jobMapper): bool { $canRun = $jobMapper->markJobPendingIfAble($job); if ($canRun === false) { @@ -163,7 +176,7 @@ protected function runJob(\Model\Job $job, LoggerInterface $logger, JobMapper $j if ($jobData instanceof QueueableJob) { try { $success = $jobData->run(); - } catch (\Exception) { + } catch (Exception) { // Empty catch } $job->status = $success ? QueueableJob::JOB_STATUS_COMPLETE : QueueableJob::JOB_STATUS_PENDING; diff --git a/Controllers/MaintenanceController.php b/Controllers/MaintenanceController.php index f7302fe..9cb96dd 100644 --- a/Controllers/MaintenanceController.php +++ b/Controllers/MaintenanceController.php @@ -22,12 +22,16 @@ use Feast\Attributes\Action; use Feast\CliController; +use Feast\ServiceContainer\NotFoundException; use Feast\View; class MaintenanceController extends CliController { protected const MAINTENANCE_VIEW = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'Views' . DIRECTORY_SEPARATOR . 'Error' . DIRECTORY_SEPARATOR . 'maintenance-screen.phtml'; + /** + * @throws NotFoundException + */ #[Action(description: 'Start maintenance mode.')] public function startGet(): void { diff --git a/Controllers/MigrationController.php b/Controllers/MigrationController.php index 556b309..ece88f2 100644 --- a/Controllers/MigrationController.php +++ b/Controllers/MigrationController.php @@ -23,9 +23,12 @@ use Feast\Attributes\Action; use Feast\Attributes\Param; use Feast\Enums\ParamType; +use Feast\Exception\ServerFailureException; use Feast\Interfaces\DatabaseDetailsInterface; +use Feast\ServiceContainer\NotFoundException; use Mapper\MigrationMapper; use Model\Migration; +use PDOException; class MigrationController extends WriteTemplateController { @@ -124,6 +127,9 @@ protected function migrationFileExistsOrEchoError(string $name): bool return true; } + /** + * @throws NotFoundException|ServerFailureException + */ protected function getMigrationModelOrEchoError(string $name): ?Migration { $migrationMapper = new MigrationMapper(); @@ -135,15 +141,15 @@ protected function getMigrationModelOrEchoError(string $name): ?Migration ); return null; } - /** @var Migration $migrationModel */ - $migrationModel = $migrationMapper->findOneByField('migration_id', $name) ?? new Migration(); - return $migrationModel; + /** @var Migration */ + return $migrationMapper->findOneByField('migration_id', $name) ?? new Migration(); } /** * @param string|null $name * @param string $type * @return bool + * @throws NotFoundException|ServerFailureException */ protected function migrationRun(?string $name, string $type = 'up'): bool { @@ -175,6 +181,9 @@ protected function migrationRun(?string $name, string $type = 'up'): bool return true; } + /** + * @throws NotFoundException|ServerFailureException + */ protected function buildMigrationList(): void { $this->buildMigrationListFromDisk(); @@ -199,6 +208,9 @@ protected function buildMigrationListFromDisk(): void } } + /** + * @throws NotFoundException|ServerFailureException + */ protected function buildMigrationListFromDatabase(): void { $migrationMapper = new MigrationMapper(); @@ -243,7 +255,7 @@ protected function runMigrationDownOrUp( $migration->down(); $migrationModel->recordDown(); } - } catch (\PDOException $exception) { + } catch (PDOException $exception) { $this->terminal->error( 'Migration ' . $migrationModel->name . ' failed:' . "\n" . 'Reason: ' . $exception->getMessage() ); diff --git a/Csv/CsvReader.php b/Csv/CsvReader.php index 958c227..4d5ca8f 100644 --- a/Csv/CsvReader.php +++ b/Csv/CsvReader.php @@ -20,6 +20,7 @@ namespace Feast\Csv; +use Generator; use SplFileObject; class CsvReader extends Csv @@ -52,9 +53,9 @@ public function getHeader(): array|false /** * Get yielded values for reader. * - * @return \Generator + * @return Generator */ - public function getIterator(): \Generator + public function getIterator(): Generator { $this->rewind(); while ($row = $this->getNextLine()) { diff --git a/Database/Database.php b/Database/Database.php index 1abe110..c9e56e2 100644 --- a/Database/Database.php +++ b/Database/Database.php @@ -27,6 +27,7 @@ use Feast\Exception\ServerFailureException; use Feast\Interfaces\DatabaseInterface; use PDO; +use stdClass; /** * Manages database connections. @@ -40,13 +41,13 @@ class Database implements DatabaseInterface private string $queryClass; /** - * @param \stdClass $connectionDetails + * @param stdClass $connectionDetails * @param string $pdoClass * @throws DatabaseException * @throws InvalidOptionException * @throws ServerFailureException */ - public function __construct(\stdClass $connectionDetails, string $pdoClass) + public function __construct(stdClass $connectionDetails, string $pdoClass) { $username = (string)$connectionDetails->user; $password = (string)$connectionDetails->pass; @@ -298,7 +299,7 @@ public function tableExists(string $table): bool $query->execute(); return true; - } catch (\Exception) { + } catch (Exception) { return false; } } @@ -315,7 +316,7 @@ public function columnExists(string $table, string $column): bool { $query = $this->describe($table); $statement = $query->execute(); - while ($row = $statement->fetch(\PDO::FETCH_OBJ)) { + while ($row = $statement->fetch(PDO::FETCH_OBJ)) { if (strtolower($column) === strtolower((string)$row->Field)) { return true; } @@ -334,15 +335,15 @@ public function columnExists(string $table, string $column): bool */ public function rawQuery(string $query, array $bindings = [], bool $forceEmulatePrepares = false): bool { - $this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $oldEmulatePrepares = (int)$this->connection->getAttribute(\PDO::ATTR_EMULATE_PREPARES); + $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $oldEmulatePrepares = (int)$this->connection->getAttribute(PDO::ATTR_EMULATE_PREPARES); if ($forceEmulatePrepares) { - $this->connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true); + $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); } $sql = $this->connection->prepare($query); $return = $sql->execute($bindings); if ($forceEmulatePrepares) { - $this->connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, $oldEmulatePrepares); + $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, $oldEmulatePrepares); } return $return; } @@ -376,7 +377,7 @@ public function getDatabaseType(): DatabaseType return $this->databaseType; } - protected function getConfigOptions(\stdClass $connectionDetails): array + protected function getConfigOptions(stdClass $connectionDetails): array { $defaults = [ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, diff --git a/Database/DatabaseDetails.php b/Database/DatabaseDetails.php index 91ffd05..898fc74 100644 --- a/Database/DatabaseDetails.php +++ b/Database/DatabaseDetails.php @@ -20,9 +20,14 @@ namespace Feast\Database; +use DirectoryIterator; +use Exception; use Feast\BaseMapper; +use Feast\Exception\ServerFailureException; use Feast\Interfaces\DatabaseDetailsInterface; use Feast\Interfaces\DatabaseFactoryInterface; +use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; @@ -33,6 +38,9 @@ class DatabaseDetails implements ServiceContainerItemInterface, DatabaseDetailsI /** @var array> */ protected array $databaseDataTypes = []; + /** + * @throws ContainerException|NotFoundException + */ public function __construct(protected DatabaseFactoryInterface $dbFactory) { $this->checkInjected(); @@ -43,10 +51,13 @@ public function setDatabaseFactory(DatabaseFactoryInterface $dbFactory): void $this->dbFactory = $dbFactory; } + /** + * @throws NotFoundException + */ public function cache(): void { $this->databaseDataTypes = []; - $dir = new \DirectoryIterator(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'Mapper'); + $dir = new DirectoryIterator(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'Mapper'); foreach ($dir as $file) { if ($file->isDot() || $file->isDir()) { @@ -75,6 +86,9 @@ public function getDataTypesForTable(string $table, string $connection = 'Defaul return $this->databaseDataTypes[$table] ?? []; } + /** + * @throws ServerFailureException|Exception + */ protected function buildDetailsForTable(string $table, string $connection): void { $dbFactory = $this->dbFactory; diff --git a/Database/DatabaseFactory.php b/Database/DatabaseFactory.php index 1baeeec..f450dc7 100644 --- a/Database/DatabaseFactory.php +++ b/Database/DatabaseFactory.php @@ -30,8 +30,11 @@ use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; +use PDO; use stdClass; +use function di; + /** * Manages database connections. * Direct access to Database class is incorrect usage. @@ -54,15 +57,13 @@ class DatabaseFactory implements ServiceContainerItemInterface, DatabaseFactoryI public function getConnection(string $connection = self::DEFAULT_CONNECTION): DatabaseInterface { if (isset($this->connections->$connection)) { - /** @var DatabaseInterface $connectionObject */ - $connectionObject = $this->connections->$connection; - - return $connectionObject; + /** @var DatabaseInterface */ + return $this->connections->$connection; } /** @var stdClass|null $connectionConfig */ $connectionConfig = $this->config->getSetting('database.' . $connection); /** @var class-string $connectionClass */ - $connectionClass = $this->config->getSetting('pdoClass', \PDO::class); + $connectionClass = $this->config->getSetting('pdoClass', PDO::class); if ($connectionConfig !== null) { $connectionObject = new Database($connectionConfig, $connectionClass); $this->connections->$connection = $connectionObject; @@ -83,7 +84,7 @@ public function getConnection(string $connection = self::DEFAULT_CONNECTION): Da public function __construct(ConfigInterface $config = null, stdClass $connections = null) { $this->checkInjected(); - $this->config = $config ?? \di(ConfigInterface::class); + $this->config = $config ?? di(ConfigInterface::class); $this->connections = $connections ?? new stdClass(); } diff --git a/Database/MySQLQuery.php b/Database/MySQLQuery.php index 66a4eb1..2491c7d 100644 --- a/Database/MySQLQuery.php +++ b/Database/MySQLQuery.php @@ -20,7 +20,9 @@ namespace Feast\Database; +use Exception; use Feast\Date; +use PDO; use stdClass; /** @@ -85,7 +87,7 @@ public function __toString(): string * * @param string $table * @return TableDetails - * @throws \Exception + * @throws Exception */ public function getDescribedTable(string $table): TableDetails { @@ -95,7 +97,7 @@ public function getDescribedTable(string $table): TableDetails $compoundPrimary = false; $statement = $this->describe($table)->execute(); - while ($row = $statement->fetch(\PDO::FETCH_OBJ)) { + while ($row = $statement->fetch(PDO::FETCH_OBJ)) { if (isset($row->Key) && (string)$row->Key === 'PRI') { if (!empty($primaryKey)) { $compoundPrimary = true; @@ -285,7 +287,7 @@ protected function getCastTypeForField(stdClass $field): string { return match (true) { str_starts_with((string)$field->Type, 'json'), - str_ends_with((string)$field->Type, 'json') => \stdClass::class, + str_ends_with((string)$field->Type, 'json') => stdClass::class, str_starts_with((string)$field->Type, 'int'), str_starts_with((string)$field->Type, 'tinyint'), str_starts_with((string)$field->Type, 'bigint'), @@ -302,7 +304,7 @@ protected function getTypeForField(stdClass $field): string $nullPrefix = (string)$field->Null === 'YES' ? 'null|' : ''; return $nullPrefix . match (true) { str_starts_with((string)$field->Type, 'json'), - str_ends_with((string)$field->Type, 'json') => '\\' . \stdClass::class . '|array', + str_ends_with((string)$field->Type, 'json') => '\\' . stdClass::class . '|array', str_starts_with((string)$field->Type, 'int'), str_starts_with((string)$field->Type, 'tinyint'), str_starts_with((string)$field->Type, 'bigint'), diff --git a/Database/Query.php b/Database/Query.php index 68e6eda..9691cba 100755 --- a/Database/Query.php +++ b/Database/Query.php @@ -24,7 +24,9 @@ use Feast\Date; use Feast\Exception\DatabaseException; use Feast\Exception\InvalidArgumentException; +use PDO; use PDOStatement; +use Throwable; abstract class Query { @@ -56,7 +58,7 @@ abstract class Query /** @var array */ protected array $where = []; - public function __construct(protected \PDO $database) + public function __construct(protected PDO $database) { } @@ -207,6 +209,7 @@ public function from(string $table, array $columns = []): static * @param string|array $joinToColumn * @param string|array $joinFromColumn * @return static + * @throws InvalidArgumentException */ public function leftJoin(string $table, string|array $joinToColumn, string|array $joinFromColumn): static { @@ -250,6 +253,7 @@ public function rightJoin(string $table, string|array $joinToColumn, string|arra * @param string|array $joinToColumn * @param string|array $joinFromColumn * @return static + * @throws InvalidArgumentException */ public function innerJoin(string $table, string|array $joinToColumn, string|array $joinFromColumn): static { @@ -264,6 +268,9 @@ public function innerJoin(string $table, string|array $joinToColumn, string|arra return $this; } + /** + * @throws InvalidArgumentException + */ protected function verifyJoinOrThrow(string|array $joinToColumn, string|array $joinFromColumn): void { if (is_string($joinToColumn) && is_string($joinFromColumn)) { @@ -403,7 +410,7 @@ public function execute(): PdoStatement if ($result === false) { throw new DatabaseException((string)$sql->errorInfo()[2]); } - } catch (\Throwable $exception) { + } catch (Throwable $exception) { throw new DatabaseException($exception->getMessage()); } diff --git a/Date.php b/Date.php index 6876778..0b3d76d 100755 --- a/Date.php +++ b/Date.php @@ -20,6 +20,9 @@ namespace Feast; +use DateTime; +use DateTimeZone; +use Exception; use Feast\Exception\InvalidDateException; /** @@ -85,6 +88,7 @@ public static function createFromTimestamp(int $timestamp, ?string $timezone = n /** * Create Date object from now. * + * @param string|null $timezone * @return self * @throws InvalidDateException */ @@ -99,6 +103,7 @@ public static function createFromNow(?string $timezone = null): self * Example: 2020-01-01; 2021-03-26 14:34:45 * * @param string $date + * @param string|null $timezone * @return self * @throws InvalidDateException */ @@ -132,11 +137,14 @@ public static function createFromString(string $date, ?string $timezone = null): */ public static function createFromFormat(string $format, string $dateString, ?string $timezone = null): self { - $timezoneData = isset($timezone) ? new \DateTimeZone($timezone) : null; - $date = \DateTime::createFromFormat($format, $dateString, $timezoneData); + $timezoneData = isset($timezone) ? new DateTimeZone($timezone) : null; + $date = DateTime::createFromFormat($format, $dateString, $timezoneData); return self::createFromTimestamp($date->getTimestamp(), $timezone); } + /** + * @throws InvalidDateException + */ public function __construct( string|int $year, string|int $month, @@ -443,13 +451,13 @@ public function modify(string $amount): Date /** * Get as PHP DateTime object. * - * @return \DateTime - * @throws \Exception + * @return DateTime + * @throws Exception */ - public function getAsDateTime(): \DateTime + public function getAsDateTime(): DateTime { - $timezone = $this->timezone ? new \DateTimeZone($this->timezone) : null; - return new \DateTime($this->getFormattedDate(self::ISO8601), $timezone); + $timezone = $this->timezone ? new DateTimeZone($this->timezone) : null; + return new DateTime($this->getFormattedDate(self::ISO8601), $timezone); } /** @@ -496,6 +504,9 @@ public function lessThan(Date $date): bool return $this->timestamp < $date->timestamp; } + /** + * @throws InvalidDateException + */ protected function throwIfInvalidDate(int $hour, int $minute, int $second, int $month, int $day, int $year): void { if (checkdate($month, $day, $year) === false) { diff --git a/Email/Email.php b/Email/Email.php index 4b83953..d245520 100755 --- a/Email/Email.php +++ b/Email/Email.php @@ -31,7 +31,7 @@ class Email /** @var array $to */ private array $to = []; private string $separator; - private ?string $sendmailPath = null; + private ?string $sendmailPath; private array $smtpDetails = []; private string $subject = ''; /** @var array $cc */ @@ -112,7 +112,7 @@ public function setFrom(string $email, ?string $name = null): static /** * Set email subject. - * + * * @param string $subject * @return static */ @@ -125,7 +125,7 @@ public function setSubject(string $subject): static /** * Set text body for email. - * + * * @param string $body * @return static */ @@ -138,7 +138,7 @@ public function setTextBody(string $body): static /** * Set HTML body for email. - * + * * @param string $body * @return static */ @@ -151,7 +151,7 @@ public function setHtmlBody(string $body): static /** * Add attachment to email. - * + * * @param Attachment $attachment * @return static */ @@ -164,7 +164,7 @@ public function addAttachment(Attachment $attachment): static /** * Add inline attachment to email. - * + * * @param Attachment $attachment * @return static */ @@ -177,7 +177,7 @@ public function addInlineAttachment(Attachment $attachment): static /** * Set ReplyTo address for email (with optional name). - * + * * @param string $email * @param string|null $name * @return static @@ -321,11 +321,11 @@ public function buildEmail(): string /** * Send email - currently uses sendmail only. - * - * @todo Add smtp functionality. - * + * * @throws InvalidArgumentException * @throws ServerFailureException + * @todo Add smtp functionality. + * */ public function sendEmail(): void { diff --git a/Exception/Error404Exception.php b/Exception/Error404Exception.php index 49a7b71..baa3974 100644 --- a/Exception/Error404Exception.php +++ b/Exception/Error404Exception.php @@ -21,11 +21,12 @@ namespace Feast\Exception; use Feast\Enums\ResponseCode; +use Throwable; class Error404Exception extends ServerFailureException { - public function __construct(string $message, int $errorCode = 0, \Throwable $previousException = null) + public function __construct(string $message, int $errorCode = 0, Throwable $previousException = null) { parent::__construct($message, ResponseCode::HTTP_CODE_404, $errorCode, $previousException); } diff --git a/Exception/ServerFailureException.php b/Exception/ServerFailureException.php index b76c86b..a89176f 100644 --- a/Exception/ServerFailureException.php +++ b/Exception/ServerFailureException.php @@ -31,6 +31,8 @@ use function di; +use const RUN_AS; + /** * Server failure exception. Allows instantly overriding http status code. Always caught, * returning the error code if present, or the error message if not, to the client if display errors is true. @@ -38,7 +40,7 @@ class ServerFailureException extends Exception { - protected string $runAs = \RUN_AS; + protected string $runAs = RUN_AS; /** * Construct a new Exception, and include the response code. @@ -75,6 +77,9 @@ public function getResponseCode(): ?ResponseCode /** * Print the error either as a simple error or as a detailed error if errors enabled. + * + * @throws \Feast\ServiceContainer\NotFoundException + * @throws Exception */ public function printError(): void { @@ -100,7 +105,7 @@ public function printError(): void /** * Print parent exception, if any. - * + * * @throws \Feast\ServiceContainer\NotFoundException */ public function printParentException(): void diff --git a/Exception/ThrottleException.php b/Exception/ThrottleException.php index 4e8f255..a284d01 100644 --- a/Exception/ThrottleException.php +++ b/Exception/ThrottleException.php @@ -21,11 +21,12 @@ namespace Feast\Exception; use Feast\Enums\ResponseCode; +use Throwable; class ThrottleException extends ServerFailureException { - public function __construct(string $message, int $errorCode = 0, \Throwable $previousException = null) + public function __construct(string $message, int $errorCode = 0, Throwable $previousException = null) { parent::__construct($message, ResponseCode::HTTP_CODE_429, $errorCode, $previousException); } diff --git a/Form/Field.php b/Form/Field.php index 6a2917b..30d2ce2 100644 --- a/Form/Field.php +++ b/Form/Field.php @@ -112,7 +112,9 @@ protected function showLabel(bool $showLabel, ?Label $labelData, string $positio return $showLabel && $labelData && $labelData->position == $positionToCheck; } - /** @psalm-suppress InvalidToString */ + /** @psalm-suppress InvalidToString + * @throws ServerFailureException + */ public function __toString() { throw new ServerFailureException('__toString not enabled for ' . static::class . '. Use toString()'); @@ -337,7 +339,7 @@ protected function buildMetaData(): string return $output; } - + public function getValuesForValidation(): string|array { return $this->value; diff --git a/Form/Form.php b/Form/Form.php index 15ee7ae..e9d18ee 100644 --- a/Form/Form.php +++ b/Form/Form.php @@ -20,6 +20,7 @@ namespace Feast\Form; +use Exception; use Feast\Exception\InvalidArgumentException; use Feast\Exception\ServerFailureException; use Feast\Form\Filter\Filter; @@ -35,7 +36,6 @@ abstract class Form public const ERROR_NOT_SET = 'Required'; - /** * @param string $name * @param string|null $action @@ -53,7 +53,7 @@ public function __construct( /** * Set form action. - * + * * @param string $action * @return static */ @@ -66,7 +66,7 @@ public function setAction(string $action): static /** * Get field by name. - * + * * @param string $name * @return Field * @throws InvalidArgumentException @@ -84,7 +84,7 @@ public function getField(string $name): Field /** * Add field to form. - * + * * @param Field $field * @return Field */ @@ -137,14 +137,14 @@ public function isValid(bool $validatePartial = false): bool /** * Validate form partial. - * + * * Alias for isValid(true); * * @return bool */ public function isValidPartial(): bool { - return $this->isValid( true); + return $this->isValid(true); } /** @@ -181,7 +181,7 @@ public function filter(array $fields): void * @param bool $showLabel * @param string $value * @return string - * @throws ServerFailureException + * @throws ServerFailureException|Exception */ public function displayField(string $fieldName, bool $showLabel = true, string $value = ''): string { diff --git a/Help.php b/Help.php index 78b8534..0428af7 100644 --- a/Help.php +++ b/Help.php @@ -23,6 +23,10 @@ use Feast\Attributes\Action; use Feast\Attributes\Param; use Feast\Enums\ParamType; +use ReflectionAttribute; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; class Help { @@ -89,7 +93,7 @@ public function printCliMethodHelp(string $command): void protected function analyzeClass(string $controllerClass, string $command): void { try { - $class = new \ReflectionClass($controllerClass); + $class = new ReflectionClass($controllerClass); $methods = $class->getMethods(); $count = 0; @@ -115,9 +119,9 @@ protected function analyzeClass(string $controllerClass, string $command): void } if ($count === 1 && isset($actionAttributes) && isset($parameters)) { $this->terminal->command($command); - /** @var array> $actionAttributes */ + /** @var array> $actionAttributes */ $this->displayActionInfo($actionAttributes, $callable); - /** @var array> $parameters */ + /** @var array> $parameters */ $this->displayParameterInfo($parameters); } else { ksort($descriptions); @@ -128,7 +132,7 @@ protected function analyzeClass(string $controllerClass, string $command): void $this->terminal->message(' ' . str_pad($name, $longestMethod + 3) . $description); } } - } catch (\ReflectionException) { + } catch (ReflectionException) { $this->terminal->error('Class', false); $this->terminal->message(' ' . $command . ' ', false); $this->terminal->error('does not exist!'); @@ -144,13 +148,13 @@ protected function analyzeMethod(string $controllerClass, string $actionName, st { $action = lcfirst(str_replace('-', '', ucwords($actionName, '-')) . 'Get'); try { - $method = new \ReflectionMethod($controllerClass, $action); + $method = new ReflectionMethod($controllerClass, $action); $callable = NameHelper::getControllerClassName($method->getDeclaringClass()) . ':' . $actionName; $actionAttributes = $method->getAttributes(Action::class); $parameters = $method->getAttributes(Param::class); $this->displayActionInfo($actionAttributes, $callable); $this->displayParameterInfo($parameters); - } catch (\ReflectionException) { + } catch (ReflectionException) { $this->terminal->error('Method', false); $this->terminal->message(' ' . $command . ' ', false); $this->terminal->error('does not exist!'); @@ -158,7 +162,7 @@ protected function analyzeMethod(string $controllerClass, string $actionName, st } /** - * @param array<\ReflectionAttribute> $parameters + * @param array> $parameters */ protected function displayParameterInfo(array $parameters): void { @@ -183,7 +187,7 @@ protected function displayParameterInfo(array $parameters): void } /** - * @param array<\ReflectionAttribute> $actionAttributes + * @param array> $actionAttributes * @param string $name */ protected function displayActionInfo(array $actionAttributes, string $name): void diff --git a/HttpController.php b/HttpController.php index 67b927a..a8ee9e1 100755 --- a/HttpController.php +++ b/HttpController.php @@ -26,6 +26,7 @@ use Feast\Interfaces\RouterInterface; use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainer; +use stdClass; /** * Class to manage controllers. @@ -33,7 +34,7 @@ abstract class HttpController implements ControllerInterface { - public \stdClass $jsonEnabledActions; + public stdClass $jsonEnabledActions; public ResponseInterface $response; /** @@ -46,7 +47,7 @@ abstract class HttpController implements ControllerInterface */ public function __construct(public ServiceContainer $di, public View $view, ?ResponseInterface $response = null) { - $this->jsonEnabledActions = new \stdClass(); + $this->jsonEnabledActions = new stdClass(); $this->response = $response ?? $di->get(ResponseInterface::class); } @@ -72,6 +73,7 @@ public function init(): bool * @param string|null $module * @param string $route * @throws NotFoundException + * @throws \Exception */ public function forward( string $action = null, @@ -120,6 +122,7 @@ public function alwaysJson(string $actionName): bool * @param string $route * @param ResponseCode $code (30x) * @throws NotFoundException + * @throws \Exception */ public function redirect( ?string $action = null, diff --git a/HttpRequest/Curl.php b/HttpRequest/Curl.php index 17eb763..6c0f3a8 100644 --- a/HttpRequest/Curl.php +++ b/HttpRequest/Curl.php @@ -31,6 +31,9 @@ class Curl extends HttpRequest implements HttpRequestInterface /** @var CurlHandle $curl */ protected mixed $curl; + /** + * @throws CurlException + */ public function __construct(?string $url = null) { parent::__construct($url); @@ -66,7 +69,8 @@ protected function resetCurl(http://github.com/string%20$url): static /** * Make the HTTP request and store the result */ - public function makeRequest(): HttpRequestInterface { + public function makeRequest(): HttpRequestInterface + { $url = $this->url ?? ''; curl_setopt($this->curl, CURLOPT_URL, $url); curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $this->method); @@ -133,7 +137,10 @@ public function makeRequest(): HttpRequestInterface { $headerSize = (int)curl_getinfo($this->curl, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $headerSize); $this->parseResponseHeaders(explode("\n", $header)); - $this->response = new Response(substr($response, $headerSize), ($this->responseCode ?? ResponseCode::HTTP_CODE_500)); + $this->response = new Response( + substr($response, $headerSize), + ($this->responseCode ?? ResponseCode::HTTP_CODE_500) + ); curl_close($this->curl); return $this; diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index 6c5328b..4585116 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -27,6 +27,7 @@ use Feast\Exception\ServerFailureException; use Feast\Interfaces\HttpRequestInterface; use SimpleXMLElement; +use stdClass; /** * Abstract Class to make HTTP/HTTPS requests @@ -476,9 +477,9 @@ public function getResponseAsString(): string /** * Get the request result as a json object. * - * @return \stdClass|null + * @return stdClass|null */ - public function getResponseAsJson(): ?\stdClass + public function getResponseAsJson(): ?stdClass { if ($this->response === null) { return null; diff --git a/HttpRequest/Response.php b/HttpRequest/Response.php index 9b31fd0..7e31cea 100644 --- a/HttpRequest/Response.php +++ b/HttpRequest/Response.php @@ -23,6 +23,7 @@ use Exception; use Feast\Enums\ResponseCode; use SimpleXMLElement; +use stdClass; class Response { @@ -36,12 +37,11 @@ public function getResponseAsText(): string { return $this->rawResponse; } - public function getResultAsJson(): ?\stdClass + public function getResultAsJson(): ?stdClass { try { - /** @var \stdClass $result */ - $result = json_decode(utf8_encode($this->rawResponse), flags: JSON_THROW_ON_ERROR); - return $result; + /** @var stdClass */ + return json_decode(utf8_encode($this->rawResponse), flags: JSON_THROW_ON_ERROR); } catch (Exception) { return null; } diff --git a/Install/bootstrap.php b/Install/bootstrap.php index c5c2cc3..0fd72e4 100755 --- a/Install/bootstrap.php +++ b/Install/bootstrap.php @@ -17,6 +17,7 @@ */ declare(strict_types=1); +use Feast\Autoloader; use Feast\Interfaces\RouterInterface; $router = di(RouterInterface::class); @@ -25,11 +26,10 @@ # CUSTOM ROUTES GO HERE IF NOT USING ATTRIBUTES # ################################################# - ##################### # END CUSTOM ROUTES # ##################### } -$autoLoader = di(\Feast\Autoloader::class); +$autoLoader = di(Autoloader::class); $autoLoader->addPathMapping('Psr', ['/vendor/Psr', '/Feast/Psr']); diff --git a/Install/container.php b/Install/container.php index 1c0e81f..7d23b25 100644 --- a/Install/container.php +++ b/Install/container.php @@ -62,7 +62,7 @@ ############################################ $configCache = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'config.cache'; if (file_exists($configCache)) { - /** @var \Feast\Interfaces\ConfigInterface|false $config */ + /** @var ConfigInterface|false $config */ $config = unserialize(file_get_contents($configCache)); if (!$config instanceof ConfigInterface) { $config = new Config(); @@ -137,7 +137,7 @@ if (RUN_AS === Main::RUN_AS_WEBAPP) { $session = new Session($config); $container->add(Session::class, $session); - $container->add(Identity::class, new Identity($config,$session)); + $container->add(Identity::class, new Identity($config, $session)); } else { $container->add(CliArguments::class, new CliArguments($argv)); } diff --git a/Install/famine b/Install/famine index 650c0f1..45c7599 100755 --- a/Install/famine +++ b/Install/famine @@ -18,8 +18,11 @@ declare(strict_types=1); +use Feast\Autoloader; +use Feast\Main; + chdir(__DIR__); -if ( file_exists('vendor/autoload.php')) { +if (file_exists('vendor/autoload.php')) { require_once('vendor/autoload.php'); } // Set up framework @@ -34,10 +37,10 @@ if (file_exists(APPLICATION_ROOT . 'Feast/Autoloader.php')) { $autoLoader = new Feast\Autoloader(); $autoLoader->register(); $autoLoader->addPathMapping('Psr', ['/Feast/Psr']); -const RUN_AS = \Feast\Main::RUN_AS_CLI; +const RUN_AS = Main::RUN_AS_CLI; require_once(APPLICATION_ROOT . 'container.php'); /** @var \Feast\ServiceContainer\ServiceContainer $container */ -$container->add(\Feast\Autoloader::class, $autoLoader); +$container->add(Autoloader::class, $autoLoader); // Turn on all error reporting and turn ON display errors error_reporting(-1); diff --git a/Install/public/index.php b/Install/public/index.php index e238ecb..6962357 100644 --- a/Install/public/index.php +++ b/Install/public/index.php @@ -23,7 +23,9 @@ declare(strict_types=1); use Feast\Autoloader; +use Feast\Interfaces\MainInterface; use Feast\Main; +use Feast\ServiceContainer\ServiceContainer; // Application start time $startTime = microtime(true); @@ -39,11 +41,11 @@ include('maintenance-screen.html'); exit; } -if ( file_exists(APPLICATION_ROOT . 'vendor/autoload.php')) { +if (file_exists(APPLICATION_ROOT . 'vendor/autoload.php')) { require_once(APPLICATION_ROOT . 'vendor/autoload.php'); } // Initialize autoloader -if ( file_exists(APPLICATION_ROOT . '/Feast/Autoloader.php')) { +if (file_exists(APPLICATION_ROOT . '/Feast/Autoloader.php')) { require_once(APPLICATION_ROOT . '/Feast/Autoloader.php'); } $autoLoader = new Autoloader(); @@ -53,12 +55,12 @@ const RUN_AS = Main::RUN_AS_WEBAPP; require_once(APPLICATION_ROOT . 'container.php'); -/** @var \Feast\ServiceContainer\ServiceContainer $container */ +/** @var ServiceContainer $container */ $container->add(Autoloader::class, $autoLoader); // Turn on all error reporting and turn OFF display errors by default error_reporting(-1); ini_set('display_errors', 'false'); -$main = di(\Feast\Interfaces\MainInterface::class); +$main = di(MainInterface::class); $main->main(); diff --git a/Interfaces/ErrorLoggerInterface.php b/Interfaces/ErrorLoggerInterface.php index 87fb2b3..29f14d0 100644 --- a/Interfaces/ErrorLoggerInterface.php +++ b/Interfaces/ErrorLoggerInterface.php @@ -20,6 +20,8 @@ namespace Feast\Interfaces; +use Throwable; + interface ErrorLoggerInterface { /** @@ -36,9 +38,9 @@ public function errorHandler(int $errno, string $errstr, string $errfile, int $e /** * Exception handler. * - * @param \Throwable $exception + * @param Throwable $exception * @param bool $caught * @return bool */ - public function exceptionHandler(\Throwable $exception, bool $caught = false): bool; + public function exceptionHandler(Throwable $exception, bool $caught = false): bool; } diff --git a/Interfaces/HttpRequestInterface.php b/Interfaces/HttpRequestInterface.php index 5323c99..414455a 100644 --- a/Interfaces/HttpRequestInterface.php +++ b/Interfaces/HttpRequestInterface.php @@ -64,7 +64,7 @@ public function getResponseAsXml(): ?SimpleXMLElement; /** * Get the request result as a json object. * - * @return \stdClass|null + * @return stdClass|null */ public function getResponseAsJson(): ?stdClass; @@ -199,7 +199,7 @@ public function clearArguments(): HttpRequestInterface; * @return HttpRequestInterface */ public function setArguments(array $arguments): HttpRequestInterface; - + /** * Add multiple arguments to the request * Allows only for simple key => value mappings, not array values. @@ -247,6 +247,7 @@ public function getCurl(): CurlHandle; /** * Get the \Feast\Response object for a finished request. + * * @return Response|null */ public function getResponse(): ?Response; diff --git a/Interfaces/RequestInterface.php b/Interfaces/RequestInterface.php index 1fae22b..652985c 100644 --- a/Interfaces/RequestInterface.php +++ b/Interfaces/RequestInterface.php @@ -22,6 +22,7 @@ use Feast\Date; use Feast\ServiceContainer\ServiceContainerItemInterface; +use stdClass; interface RequestInterface extends ServiceContainerItemInterface { @@ -97,7 +98,7 @@ public function getArgumentArray(string $name, ?array $default = null, string $t /** * Get all arguments. */ - public function getAllArguments(): \stdClass; + public function getAllArguments(): stdClass; /** * Check whether request is a POST request. diff --git a/Jobs/CronJob.php b/Jobs/CronJob.php index a31f2f8..3e47db8 100644 --- a/Jobs/CronJob.php +++ b/Jobs/CronJob.php @@ -21,6 +21,7 @@ namespace Feast\Jobs; use Feast\Date; +use Feast\Exception\InvalidDateException; use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\JobInterface; @@ -791,6 +792,9 @@ protected function checkNotBetween(Date $now): bool return $check < $start || $check > $end; } + /** + * @throws InvalidDateException + */ protected function isRunningOverlapSafe(Date $now): bool { if ($this->allowOverlap) { diff --git a/Jobs/QueueableJob.php b/Jobs/QueueableJob.php index 10fb4b4..ff09241 100644 --- a/Jobs/QueueableJob.php +++ b/Jobs/QueueableJob.php @@ -43,6 +43,7 @@ abstract class QueueableJob implements JobInterface * @param JobMapper $jobMapper * @return Job * @throws InvalidDateException + * @throws \Exception */ public function store(JobMapper $jobMapper = new JobMapper()): Job { @@ -75,33 +76,33 @@ public function setQueueName(string $queueName): static /** * Set maximum number of tries. - * + * * @param int $maxTries * @return $this */ public function setMaxTries(int $maxTries): static { $this->maxTries = $maxTries; - + return $this; } /** * Set the job name for this job. - * + * * @param string $jobName * @return $this */ public function setJobName(string $jobName): static { $this->jobName = $jobName; - + return $this; } /** * Get the name of the job. - * + * * @return string */ public function getJobName(): string @@ -111,7 +112,7 @@ public function getJobName(): string /** * Get the maximum number of retries. - * + * * @return int */ public function getMaxTries(): int @@ -121,13 +122,17 @@ public function getMaxTries(): int /** * Get the name of the queue this job should run on. - * + * * @return string */ - public function getQueueName(): string { + public function getQueueName(): string + { return $this->queueName; } + /** + * @throws \Exception + */ protected function generateUuid(): string { $data = random_bytes(16); diff --git a/Json.php b/Json.php index 3d63b97..d38ce1d 100644 --- a/Json.php +++ b/Json.php @@ -20,14 +20,18 @@ namespace Feast; +use ArgumentCountError; use DateTime; use Feast\Attributes\JsonItem; use Feast\Collection\Collection; use Feast\Collection\CollectionList; use Feast\Collection\Set; use Feast\Exception\ServerFailureException; +use JsonException; +use ReflectionClass; use ReflectionException; use ReflectionProperty; +use stdClass; class Json { @@ -44,7 +48,7 @@ class Json */ public static function marshal(object $object): string { - $return = new \stdClass(); + $return = new stdClass(); $paramInfo = self::getClassParamInfo($object::class); /** * @var string $oldName @@ -60,7 +64,7 @@ public static function marshal(object $object): string if ($reflected->isInitialized($object)) { /** @var scalar|object|array $oldItem */ $oldItem = $object->{$oldName}; - if (is_array($oldItem) || $oldItem instanceof \stdClass) { + if (is_array($oldItem) || $oldItem instanceof stdClass) { $return->{$newName} = self::marshalArray((array)$oldItem); } elseif (is_object( $oldItem @@ -89,8 +93,7 @@ public static function marshal(object $object): string * @param string $data * @param class-string|object $objectOrClass * @return object - * @throws Exception\ServerFailureException - * @throws ReflectionException + * @throws Exception\ServerFailureException|ReflectionException|JsonException * @see \Feast\Attributes\JsonItem */ public static function unmarshal(string $data, string|object $objectOrClass): object @@ -99,7 +102,7 @@ public static function unmarshal(string $data, string|object $objectOrClass): ob try { /** @psalm-suppress MixedMethodCall */ $object = new $objectOrClass(); - } catch (\ArgumentCountError) { + } catch (ArgumentCountError) { throw new ServerFailureException( 'Attempted to unmarshal into a class without a no-argument capable constructor' ); @@ -112,7 +115,7 @@ public static function unmarshal(string $data, string|object $objectOrClass): ob $jsonData = json_decode($data, true, flags: JSON_THROW_ON_ERROR); $paramInfo = self::getClassParamInfo($className); - $classInfo = new \ReflectionClass($className); + $classInfo = new ReflectionClass($className); foreach ($classInfo->getProperties() as $property) { $newPropertyName = $property->getName(); /** @var string $propertyName */ @@ -148,7 +151,7 @@ protected static function marshalArray( * @var scalar|object|array $item */ foreach ($items as $key => $item) { - if (is_array($item) || $item instanceof \stdClass) { + if (is_array($item) || $item instanceof stdClass) { $return[$key] = self::marshalArray((array)$item); } elseif (is_object($item)) { $return[$key] = (array)json_decode(self::marshal($item)); @@ -169,7 +172,7 @@ protected static function getClassParamInfo( string $class ): array { $return = []; - $classInfo = new \ReflectionClass($class); + $classInfo = new ReflectionClass($class); foreach ($classInfo->getProperties() as $property) { $name = $property->getName(); $type = null; @@ -210,7 +213,7 @@ protected static function unmarshalArray( ): void { $newProperty = $property->getName(); $item = []; - + if (class_exists($propertySubtype, true)) { /** * @var string $key @@ -245,8 +248,8 @@ protected static function unmarshalSet( $jsonData = self::unmarshalTempArray($jsonData, $propertySubtype); } $object->{$property->getName()} = new Set( - $propertySubtype, - $jsonData, + $propertySubtype, + $jsonData, preValidated: true ); } @@ -268,8 +271,8 @@ protected static function unmarshalCollection( $jsonData = self::unmarshalTempArray($jsonData, $propertySubtype); } $object->{$property->getName()} = new CollectionList( - $propertySubtype, - $jsonData, + $propertySubtype, + $jsonData, preValidated: true ); } @@ -343,7 +346,7 @@ protected static function unmarshalProperty( $propertySubtype, $propertyValue, ); - } elseif ($propertyType === \stdClass::class && is_array($propertyValue)) { + } elseif ($propertyType === stdClass::class && is_array($propertyValue)) { self::unmarshalStdClass($property, $object, $propertyValue); } elseif (is_a($propertyType, Set::class, true) && is_array($propertyValue)) { /** @psalm-suppress MixedArgumentTypeCoercion */ diff --git a/Logger/ErrorLogger.php b/Logger/ErrorLogger.php index b2b9140..637021c 100644 --- a/Logger/ErrorLogger.php +++ b/Logger/ErrorLogger.php @@ -25,6 +25,7 @@ use Feast\Exception\ServerFailureException; use Feast\Interfaces\ErrorLoggerInterface; use Feast\Interfaces\LoggerInterface; +use Feast\ServiceContainer\ContainerException; use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; @@ -34,6 +35,9 @@ class ErrorLogger implements ServiceContainerItemInterface, ErrorLoggerInterface { use DependencyInjected; + /** + * @throws ContainerException|NotFoundException + */ public function __construct(private LoggerInterface $logger) { $this->checkInjected(); @@ -97,7 +101,7 @@ public function errorHandler(int $errno, string $errstr, string $errfile, int $e /** * Exception handler. * - * @param \Throwable $exception + * @param Throwable $exception * @param bool $caught * @return bool * @throws NotFoundException diff --git a/Logger/Logger.php b/Logger/Logger.php index a641794..2a86656 100644 --- a/Logger/Logger.php +++ b/Logger/Logger.php @@ -23,6 +23,8 @@ use Feast\Enums\LogLevelCode; use Feast\Interfaces\ConfigInterface; use Feast\Main; +use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; use Psr\Log\InvalidArgumentException; @@ -39,6 +41,9 @@ class Logger implements LoggerInterface, ServiceContainerItemInterface, \Feast\I protected const LOG_DIR = APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR; protected LogLevelCode $logLevel; + /** + * @throws ContainerException|NotFoundException + */ public function __construct(private ConfigInterface $config, private string $runAs) { $this->checkInjected(); diff --git a/Main.php b/Main.php index 34d13a4..1314215 100644 --- a/Main.php +++ b/Main.php @@ -20,6 +20,7 @@ namespace Feast; +use Exception; use Feast\Enums\ResponseCode; use Feast\Exception\Error404Exception; use Feast\Exception\ServerFailureException; @@ -34,6 +35,10 @@ use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainer; use Feast\ServiceContainer\ServiceContainerItemInterface; +use ReflectionClass; +use ReflectionException; +use ReflectionNamedType; +use ReflectionParameter; /** * @since 1.0 @@ -69,6 +74,9 @@ public function __construct(protected ServiceContainer $serviceContainer, protec $this->logger = $this->serviceContainer->get(ErrorLoggerInterface::class); } + /** + * @throws NotFoundException + */ protected function buildCliArguments(RouterInterface $router): void { /** @var array{argv:array} $_SERVER */ @@ -79,6 +87,9 @@ protected function buildCliArguments(RouterInterface $router): void $router->buildCliArguments($path); } + /** + * @throws NotFoundException + */ protected function buildWebArguments(RouterInterface $router): void { /** @var ?string $path */ @@ -108,7 +119,7 @@ protected function buildInitialArguments(RouterInterface $router): void * * @throws NotFoundException * @throws Error404Exception - * @throws ServerFailureException|\ReflectionException + * @throws ServerFailureException|ReflectionException */ public function main(): void { @@ -148,11 +159,17 @@ public function main(): void } else { throw $exception; } - } catch (\Exception | ServerFailureException $exception) { + } catch (Exception | ServerFailureException $exception) { $this->handleExceptions($exception, $response, $config); } } + /** + * @throws Error404Exception + * @throws ReflectionException + * @throws NotFoundException + * @throws ServerFailureException + */ protected function runControllerLoop( RouterInterface $router, RequestInterface $request, @@ -197,7 +214,7 @@ protected function runControllerLoop( * @param ControllerInterface $controller * @param string $actionName * @param RequestInterface $request - * @throws \ReflectionException + * @throws ReflectionException */ protected function runAction(ControllerInterface $controller, string $actionName, RequestInterface $request): void { @@ -213,7 +230,7 @@ protected function runAction(ControllerInterface $controller, string $actionName * @param RequestInterface $request * @param bool $buildUnknown * @return array - * @throws \ReflectionException + * @throws ReflectionException */ protected function buildDynamicParameters( ControllerInterface|Plugin|string $controller, @@ -222,7 +239,7 @@ protected function buildDynamicParameters( bool $buildUnknown = true ): array { // Argument gathering - $reflection = new \ReflectionClass($controller); + $reflection = new ReflectionClass($controller); $method = $reflection->getMethod($function); $argumentCount = $method->getNumberOfParameters(); @@ -245,7 +262,7 @@ protected function buildArguments(array $arguments, RequestInterface $request, b { /** @var array $return */ $return = []; - /** @var \ReflectionParameter $argument */ + /** @var ReflectionParameter $argument */ foreach ($arguments as $argument) { $this->buildArgument($request, $argument, $buildUnknown, $return); } @@ -261,13 +278,16 @@ protected function buildArguments(array $arguments, RequestInterface $request, b return $return; } + /** + * @throws ReflectionException|NotFoundException + */ protected function buildArgument( RequestInterface $request, - \ReflectionParameter $argument, + ReflectionParameter $argument, bool $buildUnknown, array &$return ): void { - /** @var ?\ReflectionNamedType $type */ + /** @var ?ReflectionNamedType $type */ $type = $argument->getType(); if ($type !== null) { $argumentType = $type->getName(); @@ -293,7 +313,7 @@ protected function buildArgument( * * @param ConfigInterface $config * @param RequestInterface $request - * @throws \ReflectionException + * @throws ReflectionException */ protected function loadPlugins(ConfigInterface $config, RequestInterface $request): void { @@ -317,7 +337,7 @@ protected function loadPlugins(ConfigInterface $config, RequestInterface $reques * @param string $methodName * @param RouterInterface $router * @param RequestInterface $request - * @throws \ReflectionException + * @throws ReflectionException */ protected function runPlugins(string $methodName, RouterInterface $router, RequestInterface $request): void { @@ -338,17 +358,17 @@ protected function runPlugins(string $methodName, RouterInterface $router, Reque /** * @param class-string $argumentType * @param RequestInterface $request - * @param \ReflectionParameter $argument + * @param ReflectionParameter $argument * @param array $return - * @throws \ReflectionException + * @throws NotFoundException|ReflectionException|ServerFailureException */ protected function buildBaseModelArgument( string $argumentType, RequestInterface $request, - \ReflectionParameter $argument, + ReflectionParameter $argument, array &$return ): void { - $reflected = new \ReflectionClass($argumentType); + $reflected = new ReflectionClass($argumentType); /** @var class-string $mapperName */ $mapperName = $reflected->getConstant('MAPPER_NAME'); /** @var BaseMapper $mapper */ @@ -364,7 +384,7 @@ protected function buildBaseModelArgument( } protected function buildUnknownArgument( - \ReflectionParameter $argument, + ReflectionParameter $argument, string $argumentType, float|object|bool|int|string|null $default, RequestInterface $request, @@ -447,6 +467,9 @@ protected function buildStringArgument( $return[] = $request->getArgumentString($argumentName, $default); } + /** + * @throws Error404Exception + */ protected function handle404Exception( ConfigInterface $config, Error404Exception $exception @@ -461,7 +484,7 @@ protected function handle404Exception( } protected function handleExceptions( - \Exception|ServerFailureException $exception, + Exception|ServerFailureException $exception, ResponseInterface $response, ConfigInterface $config ): void { @@ -478,6 +501,9 @@ protected function handleExceptions( $this->logger->exceptionHandler($exception, true); } + /** + * @throws Error404Exception|ReflectionException|NotFoundException|ServerFailureException + */ protected function runRequest( RouterInterface $router, RequestInterface $request, @@ -493,6 +519,9 @@ protected function runRequest( } } + /** + * @throws Error404Exception + */ protected function checkForControllerAndAction( string $controllerClass, string $controllerName, diff --git a/NameHelper.php b/NameHelper.php index 9695a99..6108031 100644 --- a/NameHelper.php +++ b/NameHelper.php @@ -20,6 +20,8 @@ namespace Feast; +use ReflectionClass; + class NameHelper { @@ -77,10 +79,10 @@ public static function getNameWithDashes(string $name): string } /** - * Get Action name for command line. - * + * Get Action name for command line. + * * Example: runCronItemGet returns run-cron-item. - * + * * @param string $name * @return string */ @@ -94,10 +96,10 @@ public static function getMethodNameAsCallableAction(string $name): string /** * Get name of class in : syntax for controller. * - * @param \ReflectionClass $class + * @param ReflectionClass $class * @return string */ - public static function getControllerClassName(\ReflectionClass $class): string + public static function getControllerClassName(ReflectionClass $class): string { $className = explode('\\', $class->name); $prefix = ''; diff --git a/Profiler/Profiler.php b/Profiler/Profiler.php index f08eeff..6ea9395 100755 --- a/Profiler/Profiler.php +++ b/Profiler/Profiler.php @@ -21,6 +21,8 @@ namespace Feast\Profiler; use Feast\Interfaces\ProfilerInterface; +use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; @@ -33,6 +35,9 @@ class Profiler implements ServiceContainerItemInterface, ProfilerInterface { use DependencyInjected; + /** + * @throws ContainerException|NotFoundException + */ public function __construct(private float $startTime, bool $checkInjected = true) { if ($checkInjected) { @@ -50,7 +55,7 @@ public function getTotalTime(): string if (function_exists('bcsub')) { return bcsub((string)microtime(true), (string)$this->startTime, 4); } - return $this->getTotalTimeNoBcMath((string)microtime(true), (string)$this->startTime, 4); + return $this->getTotalTimeNoBcMath((string)microtime(true), (string)$this->startTime); } /** diff --git a/PsalmLoader.php b/PsalmLoader.php index 357ed60..8352a4e 100644 --- a/PsalmLoader.php +++ b/PsalmLoader.php @@ -18,6 +18,7 @@ declare(strict_types=1); +use Feast\Autoloader; use Feast\Config\Config; use Feast\Database\DatabaseFactory; use Feast\Logger\ErrorLogger; @@ -30,6 +31,7 @@ use Feast\Interfaces\ResponseInterface; use Feast\Interfaces\RouterInterface; use Feast\Logger\Logger; +use Feast\Main; use Feast\Response; use Feast\Profiler\Profiler; use Feast\Request; @@ -42,7 +44,7 @@ // Initialize autoloader require_once(APPLICATION_ROOT . 'Autoloader.php'); -$autoLoader = new \Feast\Autoloader(); +$autoLoader = new Autoloader(); $autoLoader->register(['.php', '.php.txt']); $autoLoader->addPathMapping('Feast', ['.']); $autoLoader->addPathMapping('Psr', ['./Psr']); @@ -55,7 +57,7 @@ require_once(APPLICATION_ROOT . 'DependencyInjector.php'); $container = di(); $config = new Config(overriddenEnvironment: 'development'); -$logger = new Logger($config, \Feast\Main::RUN_AS_CLI); +$logger = new Logger($config, Main::RUN_AS_CLI); $container->add(ConfigInterface::class, $config); $container->add(DatabaseFactoryInterface::class, new DatabaseFactory($config)); @@ -69,5 +71,5 @@ $container->add(ErrorLoggerInterface::class, new ErrorLogger($logger)); $container->add(ResponseInterface::class, new Response()); -const RUN_AS = \Feast\Main::RUN_AS_CLI; +const RUN_AS = Main::RUN_AS_CLI; diff --git a/Request.php b/Request.php index 66bdabc..ff80e51 100755 --- a/Request.php +++ b/Request.php @@ -25,8 +25,11 @@ use Feast\Exception\InvalidDateException; use Feast\Exception\ServerFailureException; use Feast\Interfaces\RequestInterface; +use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; +use stdClass; /** * Manages the request argument including all arguments. @@ -36,13 +39,16 @@ class Request implements ServiceContainerItemInterface, RequestInterface use DependencyInjected; - private \stdClass $arguments; + private stdClass $arguments; private bool $isJson = false; + /** + * @throws ContainerException|NotFoundException + */ public function __construct() { $this->checkInjected(); - $this->arguments = new \stdClass(); + $this->arguments = new stdClass(); } /** @@ -50,7 +56,7 @@ public function __construct() */ public function clearArguments(): void { - $this->arguments = new \stdClass(); + $this->arguments = new stdClass(); } /** @@ -88,6 +94,7 @@ public function getArgumentString(string $name, ?string $default = null): string * Get argument value as Date. * * @param string $name + * @param bool $throwOnFailure * @return Date|null * @throws InvalidDateException */ @@ -194,7 +201,7 @@ public function getArgumentArray(string $name, ?array $default = null, string $t /** * Get all arguments. */ - public function getAllArguments(): \stdClass + public function getAllArguments(): stdClass { return $this->arguments; } @@ -279,6 +286,9 @@ protected function convertArgumentArrayToDate(array $values): array return $return; } + /** + * @throws InvalidDateException + */ protected function convertArgumentToDate(string $argument, bool $throwOnFailure = false): ?Date { try { @@ -295,6 +305,9 @@ protected function convertArgumentToDate(string $argument, bool $throwOnFailure } } + /** + * @throws InvalidArgumentException + */ protected function convertArgumentToFloat(string $argument): float { if (!is_numeric($argument)) { @@ -345,6 +358,9 @@ protected function convertArgumentArrayToInt(array $values): array return $return; } + /** + * @throws InvalidArgumentException + */ protected function convertArgumentToInt(string $argument): int { if (!is_numeric($argument) || str_contains($argument, '.')) { diff --git a/Response.php b/Response.php index 3549dbd..e27fbd9 100644 --- a/Response.php +++ b/Response.php @@ -23,6 +23,8 @@ use Feast\Enums\ResponseCode; use Feast\Interfaces\ResponseInterface; use Feast\Interfaces\RouterInterface; +use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; use JsonException; @@ -41,7 +43,7 @@ class Response implements ServiceContainerItemInterface, ResponseInterface private ?string $redirectPath = null; /** - * Initialize class + * @throws ContainerException|NotFoundException */ public function __construct() { @@ -56,7 +58,6 @@ public function __construct() public function setResponseCode(ResponseCode $responseCode): void { $this->responseCode = $responseCode; - } /** diff --git a/Router/Router.php b/Router/Router.php index 5ea0aeb..e3f762b 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -34,8 +34,10 @@ use Feast\Main; use Feast\NameHelper; use Feast\ServiceContainer; +use Feast\ServiceContainer\ContainerException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; +use ReflectionClass; use ReflectionException; use ReflectionMethod; use stdClass; @@ -57,6 +59,9 @@ class Router implements ServiceContainerItemInterface, RouterInterface private array $routeNameMap = []; private bool $fromCache = false; + /** + * @throws ContainerException|ServiceContainer\NotFoundException + */ public function __construct(private string $runAs = Main::RUN_AS_WEBAPP) { $this->checkInjected(); @@ -119,7 +124,7 @@ public function buildRouteForRequestUrl(string $arguments): void * @param string $arguments * @return void * @throws ServiceContainer\NotFoundException - * @throws ReflectionException + * @throws NotFoundException */ public function buildCliArguments(string $arguments): void { @@ -132,7 +137,7 @@ public function buildCliArguments(string $arguments): void array_shift($queryString); array_shift($queryString); try { - $class = new \ReflectionClass($this->getControllerFullyQualifiedName()); + $class = new ReflectionClass($this->getControllerFullyQualifiedName()); } catch (ReflectionException) { throw new NotFoundException('Controller ' . $this->getControllerName() . ' does not exist'); } @@ -144,6 +149,11 @@ public function buildCliArguments(string $arguments): void $this->setCliArgumentsOnRequest($method, $queryString); } + /** + * @throws Error404Exception + * @throws ReflectionException + * @throws ServiceContainer\NotFoundException + */ protected function buildNamedRoute( string $arguments, RouteData $namedRoute @@ -384,7 +394,7 @@ private function checkActionIsVariadic(): bool if (!method_exists($controller, $actionMethod)) { throw new Error404Exception('Action ' . $actionMethod . ' does not exist!'); } - $controllerClass = new \ReflectionClass($controller); + $controllerClass = new ReflectionClass($controller); $method = $controllerClass->getMethod($actionMethod); return $method->isVariadic(); @@ -655,6 +665,9 @@ public function getDefaultArguments(string $route, string $requestMethod): ?arra return $routeData->arguments; } + /** + * @throws Error404Exception + */ protected function getRouteData(string $route, string $requestMethod): RouteData { /** @var stdClass $namedRoute */ @@ -662,10 +675,8 @@ protected function getRouteData(string $route, string $requestMethod): RouteData if (empty($namedRoute->$route)) { throw new Error404Exception('Route ' . $route . ' does not exist', 500); } - /** @var RouteData $routeData */ - $routeData = $namedRoute->$route; - - return $routeData; + /** @var RouteData */ + return $namedRoute->$route; } /** @@ -820,6 +831,9 @@ public function cache(): void $this->fromCache = false; } + /** + * @throws ReflectionException + */ private function scanRoutes(string $directory, string $filePath = '', string $module = 'Default'): void { $directory = opendir($directory); @@ -835,11 +849,11 @@ private function scanRoutes(string $directory, string $filePath = '', string $mo /** * @param class-string $classString * @param string $module - * @throws ReflectionException + * @throws ReflectionException|RouteException */ private function analyzeControllerForRoute(string $classString, string $module): void { - $class = new \ReflectionClass($classString); + $class = new ReflectionClass($classString); $className = explode('\\', $class->name); $position = count($className) - 1; $className = lcfirst(substr($className[$position], 0, -10)); @@ -850,6 +864,9 @@ private function analyzeControllerForRoute(string $classString, string $module): } } + /** + * @throws RouteException + */ private function analyzeMethodForRoute(ReflectionMethod $method, string $className, string $module): void { $attribute = $method->getAttributes(Path::class)[0] ?? null; diff --git a/Service.php b/Service.php index a723c08..89c870a 100644 --- a/Service.php +++ b/Service.php @@ -25,15 +25,22 @@ use Feast\Interfaces\ConfigInterface; use Feast\Interfaces\HttpRequestInterface; +use function di; + abstract class Service { protected HttpRequestInterface $httpRequest; + /** + * @throws NotFoundException + * @throws ServiceContainer\NotFoundException + * @throws InvalidOptionException + */ public function __construct() { /** @var ConfigInterface $config */ - $config = \di(ConfigInterface::class); + $config = di(ConfigInterface::class); /** @var ?class-string $serviceClass */ $serviceClass = $config->getSetting('service.class'); if ($serviceClass === null) { @@ -47,7 +54,7 @@ public function __construct() /** * Get the underlying HttpRequest object for the Service class. - * + * * @return HttpRequestInterface|null */ public function getHttpRequestObject(): ?HttpRequestInterface diff --git a/ServiceContainer/ServiceContainer.php b/ServiceContainer/ServiceContainer.php index 82575f6..fcb4eab 100644 --- a/ServiceContainer/ServiceContainer.php +++ b/ServiceContainer/ServiceContainer.php @@ -27,6 +27,9 @@ class ServiceContainer implements ContainerInterface, ServiceContainerItemInterf /** @var array $dependencies */ protected array $dependencies = []; + /** + * @throws ContainerException + */ public function __construct() { $this->add(ServiceContainer::class, $this); diff --git a/Session/Identity.php b/Session/Identity.php index 273db32..49723c5 100755 --- a/Session/Identity.php +++ b/Session/Identity.php @@ -22,8 +22,11 @@ use Feast\BaseModel; use Feast\Interfaces\ConfigInterface; +use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; +use stdClass; /** * Class to manage an identity. This class is a convenience shorthand to the Feast_Login namespace @@ -32,9 +35,12 @@ class Identity implements ServiceContainerItemInterface { use DependencyInjected; - protected \stdClass $me; - - public function __construct(protected ConfigInterface $config,protected Session $session) + protected stdClass $me; + + /** + * @throws ContainerException|NotFoundException + */ + public function __construct(protected ConfigInterface $config, protected Session $session) { $this->checkInjected(); $this->me = $session->getNamespace('Feast_Login'); diff --git a/Session/Session.php b/Session/Session.php index 27144c0..a5e54d7 100755 --- a/Session/Session.php +++ b/Session/Session.php @@ -25,6 +25,7 @@ use Feast\ServiceContainer; use Feast\ServiceContainer\ServiceContainerItemInterface; use Feast\Traits\DependencyInjected; +use stdClass; /** * Manage session variables. Also handles session security if "strictIp" setting @@ -35,8 +36,8 @@ class Session implements ServiceContainerItemInterface use DependencyInjected; /** - * Initial creation of Feast_Session. - * + * Initial creation of Feast_Session. + * * If Strict IP setting is enabled, the session is destroyed if the IP doesn't match. * * @param ConfigInterface $config @@ -67,12 +68,12 @@ public function __construct(ConfigInterface $config) * Return session namespace by name. Creates if non-existent. * * @param string $namespace - * @return \stdClass + * @return stdClass */ - public function getNamespace(string $namespace): \stdClass + public function getNamespace(string $namespace): stdClass { - if (!isset($_SESSION[$namespace]) || $_SESSION[$namespace] instanceof \stdClass === false) { - $_SESSION[$namespace] = new \stdClass(); + if (!isset($_SESSION[$namespace]) || $_SESSION[$namespace] instanceof stdClass === false) { + $_SESSION[$namespace] = new stdClass(); } return $_SESSION[$namespace]; @@ -80,7 +81,7 @@ public function getNamespace(string $namespace): \stdClass /** * Destroy a namespace in the session. - * + * * @param string $namespace */ public function destroyNamespace(string $namespace): void diff --git a/Traits/Collection.php b/Traits/Collection.php index 93a90fa..29a928c 100644 --- a/Traits/Collection.php +++ b/Traits/Collection.php @@ -147,7 +147,7 @@ public function objectSort( 'Collection must contain objects of a named class in order to use objectSort' ); } - + /** @var array $array */ $array = $this->array; usort( @@ -452,6 +452,9 @@ public function valid(): bool return key($this->array) !== null; } + /** + * @throws InvalidArgumentException + */ protected function validateTypeOrThrow( mixed $value ): void { diff --git a/Traits/DependencyInjected.php b/Traits/DependencyInjected.php index 89e9441..22c21f4 100644 --- a/Traits/DependencyInjected.php +++ b/Traits/DependencyInjected.php @@ -21,16 +21,17 @@ namespace Feast\Traits; use Feast\ServiceContainer\ContainerException; +use Feast\ServiceContainer\NotFoundException; use Feast\ServiceContainer\ServiceContainer; trait DependencyInjected { /** * Check if the instantiated item is already in the Service Container. Throws if item found. - * + * * @param mixed ...$arguments * @throws ContainerException - * @throws \Feast\ServiceContainer\NotFoundException + * @throws NotFoundException */ public function checkInjected(mixed ...$arguments): void { diff --git a/View.php b/View.php index 33ce7f4..541c57e 100644 --- a/View.php +++ b/View.php @@ -57,6 +57,7 @@ class View extends stdClass implements ServiceContainerItemInterface * @param ConfigInterface $config * @param RouterInterface $router * @throws ServiceContainer\NotFoundException|ServiceContainer\ContainerException + * @throws Exception */ public function __construct(protected ConfigInterface $config, protected RouterInterface $router) { diff --git a/bootstrap.php b/bootstrap.php index 71c395a..82c48f6 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -24,12 +24,15 @@ ########################################################## # +use Feast\Autoloader; +use Feast\Interfaces\RouterInterface; + if (file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php')) { /** @psalm-suppress MissingFile */ require_once(__DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'); } -$routes = di(\Feast\Interfaces\RouterInterface::class); +$routes = di(RouterInterface::class); // Any custom routes below -$autoLoader = di(\Feast\Autoloader::class); +$autoLoader = di(Autoloader::class); // Any custom autoload mappings below diff --git a/composer.json b/composer.json index 559f049..7fc95df 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ ], "require": { "php": "^8.1.0", - "ext-pdo": "*" + "ext-pdo": "*", + "ext-xml": "*" }, "suggest": { "ext-mysql": "*", From f08dfe43ca4196299a07538029a73dd4a2531ca2 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 24 Oct 2021 15:18:42 -0400 Subject: [PATCH 20/56] Minor reformat --- Config/Config.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Config/Config.php b/Config/Config.php index feb87ee..1635775 100755 --- a/Config/Config.php +++ b/Config/Config.php @@ -269,9 +269,7 @@ private function buildInheritedEnvironments(stdClass $config, string &$environme * @var string|int|bool|stdClass $val */ foreach ($config->{$parentEnvironment} as $key => $val) { - $config->$environmentName->$key = $val instanceof stdClass ? $this->cloneObjectOrArrayAsObject( - $val - ) : $val; + $config->$environmentName->$key = $val instanceof stdClass ? $this->cloneObjectOrArrayAsObject($val) : $val; } } } From 3912e31f7f0ffbd2640de274f3f6a07df94c95e6 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 24 Oct 2021 15:26:33 -0400 Subject: [PATCH 21/56] Fix view related bug with DocType from config. --- FeastTests/ViewTest.php | 2 +- View.php | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/FeastTests/ViewTest.php b/FeastTests/ViewTest.php index b10491e..6585bf7 100644 --- a/FeastTests/ViewTest.php +++ b/FeastTests/ViewTest.php @@ -42,7 +42,7 @@ public function setUp(): void $this->returnValueMap( [ ['siteurl', null, 'test'], - ['html.doctype', DocType::HTML_5, DocType::HTML_4_01_STRICT] + ['html.doctype', DocType::HTML_5->value, DocType::HTML_4_01_STRICT->value] ] ) ); diff --git a/View.php b/View.php index 541c57e..3d36f5a 100644 --- a/View.php +++ b/View.php @@ -63,8 +63,11 @@ public function __construct(protected ConfigInterface $config, protected RouterI { $this->checkInjected(); $this->baseUrl = (string)$config->getSetting('siteurl'); - /** @var DocType $docType */ - $docType = $config->getSetting('html.doctype', DocType::HTML_5); + /** + * @var DocType $docType + * @psalm-suppress UndefinedMethod + */ + $docType = DocType::tryFrom((string)$config->getSetting('html.doctype', DocType::HTML_5->value)) ?? DocType::HTML_5; $this->setDoctype($docType); } From a8548970f9705d1fbcbe20e0c76441b97eb2c46e Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Mon, 25 Oct 2021 18:57:51 -0400 Subject: [PATCH 22/56] Rewrite config transforms to allow enums on config to remain enums, rewrite unit tests --- Config/Config.php | 33 +++++++++++++++++++-- Database/Database.php | 7 ++--- FeastTests/Database/DatabaseFactoryTest.php | 2 +- FeastTests/Database/DatabaseTest.php | 16 +++++----- FeastTests/DateTest.php | 3 -- FeastTests/HttpRequest/CurlTest.php | 19 ++++++------ FeastTests/ViewTest.php | 3 -- Form/Field.php | 2 +- Traits/Collection.php | 1 - 9 files changed, 52 insertions(+), 34 deletions(-) diff --git a/Config/Config.php b/Config/Config.php index 1635775..492b510 100755 --- a/Config/Config.php +++ b/Config/Config.php @@ -20,6 +20,7 @@ namespace Feast\Config; +use BackedEnum; use Feast\Exception\ConfigException; use Feast\Exception\ServerFailureException; use Feast\Interfaces\ConfigInterface; @@ -292,14 +293,40 @@ private function addLocalConfig(): void private function cloneObjectOrArrayAsObject(stdClass|array $settings): stdClass { + $return = new stdClass(); + /** + * @psalm-suppress PossibleRawObjectIteration + * @var string $key + * @var scalar|array|stdClass|BackedEnum $val + */ + foreach($settings as $key => $val) { + if ( is_array($val) || $val instanceof stdClass ) { + $return->$key = $this->cloneObjectOrArrayAsObject($val); + } else { + $return->$key = $val; + } + } /** @var stdClass */ - return json_decode(json_encode($settings)); + return $return; } - private function objectToArray(stdClass $object): array + private function objectToArray(stdClass|array $settings): array { + $return = [] ; + /** + * @psalm-suppress PossibleRawObjectIteration + * @var string $key + * @var scalar|array|stdClass|BackedEnum $val + */ + foreach($settings as $key => $val) { + if ( is_array($val) || $val instanceof stdClass ) { + $return[$key] = $this->objectToArray($val); + } else { + $return[$key] = $val; + } + } /** @var array */ - return json_decode(json_encode($object), true); + return $return; } } diff --git a/Database/Database.php b/Database/Database.php index c9e56e2..8b499a2 100644 --- a/Database/Database.php +++ b/Database/Database.php @@ -51,11 +51,8 @@ public function __construct(stdClass $connectionDetails, string $pdoClass) { $username = (string)$connectionDetails->user; $password = (string)$connectionDetails->pass; - /** - * @var DatabaseType - * @psalm-suppress UndefinedMethod - */ - $this->databaseType = DatabaseType::from($connectionDetails->connectionType); + /** @var DatabaseType */ + $this->databaseType = $connectionDetails->connectionType; $queryClass = (string)($connectionDetails->queryClass ?? ''); if ($queryClass === '') { throw new InvalidOptionException( diff --git a/FeastTests/Database/DatabaseFactoryTest.php b/FeastTests/Database/DatabaseFactoryTest.php index 3e06218..7c3c8c0 100644 --- a/FeastTests/Database/DatabaseFactoryTest.php +++ b/FeastTests/Database/DatabaseFactoryTest.php @@ -40,7 +40,7 @@ public function testGetConnection(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL->value; + $details->connectionType = DatabaseType::MYSQL; $details->queryClass = MySQLQuery::class; $config = $this->createStub(Config::class); $config->method('getSetting')->willReturnMap( diff --git a/FeastTests/Database/DatabaseTest.php b/FeastTests/Database/DatabaseTest.php index 1978ef7..4ea81ce 100644 --- a/FeastTests/Database/DatabaseTest.php +++ b/FeastTests/Database/DatabaseTest.php @@ -45,7 +45,7 @@ protected function getValidConnection( $details->pass = 'test'; $details->name = 'Test'; $details->queryClass = $queryClass; - $details->connectionType = $connectionType->value; + $details->connectionType = $connectionType; if ($options) { $details->config = [ \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_OBJ, @@ -81,7 +81,7 @@ public function testInstantiationUnknownType(): void $details->pass = 'test'; $details->name = 'Test'; $details->connectionType = 'This Database Doesn\'t Exist'; - $this->expectException(\ValueError::class); + $this->expectException(\TypeError::class); new Database($details, PDOMock::class); } @@ -93,7 +93,7 @@ public function testInstantiationWithUrl(): void $details->pass = 'test'; $details->name = 'Test'; $details->url = 'mysql:host=localhost;port=3306;'; - $details->connectionType = DatabaseType::MYSQL->value; + $details->connectionType = DatabaseType::MYSQL; $details->queryClass = MySQLQuery::class; $database = new Database($details, PDOMock::class); $this->assertTrue($database instanceof Database); @@ -106,7 +106,7 @@ public function testInstantiationUnknownDbClass(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL->value; + $details->connectionType = DatabaseType::MYSQL; $details->queryClass = MySQLQuery::class; $this->expectException(InvalidOptionException::class); new Database($details, \stdClass::class); @@ -119,7 +119,7 @@ public function testInstantiationInvalidDbClass(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL->value; + $details->connectionType = DatabaseType::MYSQL; $details->queryClass = 'completegibberishdefinitelynotaclass'; $this->expectException(InvalidOptionException::class); new Database($details, \stdClass::class); @@ -132,7 +132,7 @@ public function testInstantiationNonExtendedDbClass(): void $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL->value; + $details->connectionType = DatabaseType::MYSQL; $details->queryClass = \stdClass::class; $this->expectException(InvalidOptionException::class); new Database($details, \stdClass::class); @@ -145,7 +145,7 @@ public function testInstantiationWithRemovedFormerlyDeprecatedMethodMySQL(): voi $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::MYSQL->value; + $details->connectionType = DatabaseType::MYSQL; $this->expectException(InvalidOptionException::class); $database = new Database($details, PDOMock::class); } @@ -157,7 +157,7 @@ public function testInstantiationWithRemovedFormerlyDeprecatedMethodSqLite(): vo $details->user = 'root'; $details->pass = 'test'; $details->name = 'Test'; - $details->connectionType = DatabaseType::SQLITE->value; + $details->connectionType = DatabaseType::SQLITE; $this->expectException(InvalidOptionException::class); $database = new Database($details, PDOMock::class); } diff --git a/FeastTests/DateTest.php b/FeastTests/DateTest.php index feca74d..55c7c66 100755 --- a/FeastTests/DateTest.php +++ b/FeastTests/DateTest.php @@ -22,9 +22,6 @@ use Feast\Exception\InvalidDateException; use PHPUnit\Framework\TestCase; -/** - * @psalm-suppress PropertyNotSetInConstructor - */ class DateTest extends TestCase { diff --git a/FeastTests/HttpRequest/CurlTest.php b/FeastTests/HttpRequest/CurlTest.php index 7e62ffe..e8ccead 100644 --- a/FeastTests/HttpRequest/CurlTest.php +++ b/FeastTests/HttpRequest/CurlTest.php @@ -19,6 +19,7 @@ namespace HttpRequest; +use Feast\Enums\RequestMethod; use Feast\Enums\ResponseCode; use Feast\Exception\BadRequestException; use Feast\Exception\CurlException; @@ -33,7 +34,7 @@ public function testGet(): void { $request = new Curl(); $request->get('http://www.google.com/'); - $this->assertEquals('GET', $request->getMethod()->value); + $this->assertEquals(RequestMethod::GET, $request->getMethod()); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); /** THIS METHOD IS EXPECTED TO FAIL WITH AN EXCEPTION BECAUSE WE ARE MOCKING THE CURL ITEM WITH stdClass */ $this->expectException(\TypeError::class); @@ -44,7 +45,7 @@ public function testPatch(): void { $request = new Curl(); $request->patch('http://www.google.com/'); - $this->assertEquals('PATCH', $request->getMethod()->value); + $this->assertEquals(RequestMethod::PATCH, $request->getMethod()); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -73,7 +74,7 @@ public function testPost(): void { $request = new Curl(); $request->post('http://www.google.com/'); - $this->assertEquals('POST', $request->getMethod()->value); + $this->assertEquals(RequestMethod::POST, $request->getMethod()); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -81,7 +82,7 @@ public function testPostJson(): void { $request = new Curl(); $request->postJson('http://www.google.com/'); - $this->assertEquals('POST', $request->getMethod()->value); + $this->assertEquals(RequestMethod::POST, $request->getMethod()); $this->assertEquals(HttpRequest::CONTENT_TYPE_JSON, $request->getContentType()); } @@ -89,7 +90,7 @@ public function testPut(): void { $request = new Curl(); $request->put('http://www.google.com/'); - $this->assertEquals('PUT', $request->getMethod()->value); + $this->assertEquals(RequestMethod::PUT, $request->getMethod()); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -97,7 +98,7 @@ public function testDelete(): void { $request = new Curl(); $request->delete('http://www.google.com/'); - $this->assertEquals('DELETE', $request->getMethod()->value); + $this->assertEquals(RequestMethod::DELETE, $request->getMethod()); $this->assertTrue($request->getUrl() === 'http://www.google.com/'); } @@ -362,7 +363,7 @@ public function testAddArgumentsPostJson(): void $this->assertIsArray($arguments['type']); $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); - $this->assertEquals('POST', $request->getMethod()->value); + $this->assertEquals(RequestMethod::POST, $request->getMethod()); $request->makeRequest(); } @@ -382,7 +383,7 @@ public function testAddArgumentsPutJson(): void $this->assertIsArray($arguments['type']); $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); - $this->assertEquals('PUT', $request->getMethod()->value); + $this->assertEquals(RequestMethod::PUT, $request->getMethod()); $request->makeRequest(); } @@ -402,7 +403,7 @@ public function testAddArgumentsPatchJson(): void $this->assertIsArray($arguments['type']); $this->assertEquals('text', $arguments['type'][0]); $this->assertEquals('pass', $arguments['type'][1]); - $this->assertEquals('PATCH', $request->getMethod()->value); + $this->assertEquals(RequestMethod::PATCH, $request->getMethod()); $request->makeRequest(); } diff --git a/FeastTests/ViewTest.php b/FeastTests/ViewTest.php index 6585bf7..1d2ba0c 100644 --- a/FeastTests/ViewTest.php +++ b/FeastTests/ViewTest.php @@ -23,9 +23,6 @@ use Feast\View; use PHPUnit\Framework\TestCase; -/** - * @psalm-suppress PropertyNotSetInConstructor - */ class ViewTest extends TestCase { protected View $view; diff --git a/Form/Field.php b/Form/Field.php index 30d2ce2..cb5b89a 100644 --- a/Form/Field.php +++ b/Form/Field.php @@ -112,7 +112,7 @@ protected function showLabel(bool $showLabel, ?Label $labelData, string $positio return $showLabel && $labelData && $labelData->position == $positionToCheck; } - /** @psalm-suppress InvalidToString + /** * @throws ServerFailureException */ public function __toString() diff --git a/Traits/Collection.php b/Traits/Collection.php index 29a928c..68e5a3f 100644 --- a/Traits/Collection.php +++ b/Traits/Collection.php @@ -210,7 +210,6 @@ public function reduce(callable $callback, mixed $initial = null): mixed public function walk(callable $callback, mixed $arg = null): bool { - /** @psalm-suppress PossiblyInvalidPropertyAssignmentValue - Wrong */ return array_walk($this->array, $callback, $arg); } From 97bc20df703c350ca7e95dd35413583a46cf9500 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Mon, 25 Oct 2021 19:15:45 -0400 Subject: [PATCH 23/56] Whitespace fixes --- Install/Mapper/JobMapper.php.txt | 7 +++++-- Install/Model/Job.php.txt | 2 +- Install/Model/Migration.php.txt | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Install/Mapper/JobMapper.php.txt b/Install/Mapper/JobMapper.php.txt index 7ebb02b..d0f6131 100644 --- a/Install/Mapper/JobMapper.php.txt +++ b/Install/Mapper/JobMapper.php.txt @@ -6,7 +6,8 @@ namespace Mapper; use \Feast\BaseMapper; use \Model\Job; - +use \Feast\Exception\ServerFailureException; +use \Feast\ServiceContainer\NotFoundException; class JobMapper extends BaseMapper { protected const OBJECT_NAME = Job::class; @@ -17,6 +18,8 @@ class JobMapper extends BaseMapper * @param int|string $value * @param bool $validate * @return ?Job + * @throws ServerFailureException + * @throws NotFoundException */ public function findByPrimaryKey(int|string $value, bool $validate = false): ?Job { @@ -37,7 +40,7 @@ class JobMapper extends BaseMapper } return null; } - + public function markJobPendingIfAble(Job $job): bool { $query = $this->connection->update(self::TABLE_NAME,['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) ->where('job_id = ? and status IN (?,?)',[$job->job_id,\Feast\Jobs\QueueableJob::JOB_STATUS_PENDING,\Feast\Jobs\QueueableJob::JOB_STATUS_FAILED]); diff --git a/Install/Model/Job.php.txt b/Install/Model/Job.php.txt index 53f9924..accb2a3 100644 --- a/Install/Model/Job.php.txt +++ b/Install/Model/Job.php.txt @@ -20,4 +20,4 @@ class Job extends BaseModel public int $tries; public int $max_tries; public string $queue_name; -} \ No newline at end of file +} diff --git a/Install/Model/Migration.php.txt b/Install/Model/Migration.php.txt index 0b6c109..bc497f2 100644 --- a/Install/Model/Migration.php.txt +++ b/Install/Model/Migration.php.txt @@ -48,4 +48,4 @@ class Migration extends BaseModel $this->status = 'down'; $this->save(); } -} \ No newline at end of file +} From 6d83fb9ac5946d6a478ae06fd0235feaa7636fbd Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Mon, 25 Oct 2021 21:54:59 -0400 Subject: [PATCH 24/56] Update psalm and psalm suppresses --- Main.php | 5 +---- ServiceContainer/ServiceContainer.php | 1 - composer.lock | 17 +++++++++-------- psalm.xml | 2 ++ 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Main.php b/Main.php index 1314215..3f187db 100644 --- a/Main.php +++ b/Main.php @@ -187,10 +187,7 @@ protected function runControllerLoop( $this->checkForControllerAndAction($controllerClass, $controllerName, $actionName); /* @var $controller ControllerInterface */ $arguments = $this->buildDynamicParameters($controllerClass, '__construct', $request, false); - /** - * @var HttpController $controller - * @psalm-suppress UnsafeInstantiation - */ + /** @var HttpController $controller */ $controller = new $controllerClass(...$arguments); $allowRunning = $controller->init(); diff --git a/ServiceContainer/ServiceContainer.php b/ServiceContainer/ServiceContainer.php index fcb4eab..fc5a1bb 100644 --- a/ServiceContainer/ServiceContainer.php +++ b/ServiceContainer/ServiceContainer.php @@ -43,7 +43,6 @@ public function __construct() * @psalm-param returned::class $id * @param mixed ...$arguments * @return returned - * @psalm-suppress MismatchingDocblockReturnType - Needed for the dynamic return * @psalm-suppress MoreSpecificImplementedParamType - Needed for the dynamic return * @psalm-suppress InvalidReturnStatement * @psalm-suppress InvalidReturnType diff --git a/composer.lock b/composer.lock index 3ca9d9d..281aacc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ee688c83ba436ed6395805eb009fcab2", + "content-hash": "7c0ac43e57846ac94ac06e1ed6567026", "packages": [], "packages-dev": [ { "name": "psalm/phar", - "version": "4.10.0", + "version": "4.11.1", "source": { "type": "git", "url": "https://github.com/psalm/phar.git", - "reference": "79c5b2210e1a1cabeee671db1160c96869572d08" + "reference": "ec8acfd8cb0332e68fc57bfb2a57942a3e028d4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/79c5b2210e1a1cabeee671db1160c96869572d08", - "reference": "79c5b2210e1a1cabeee671db1160c96869572d08", + "url": "https://api.github.com/repos/psalm/phar/zipball/ec8acfd8cb0332e68fc57bfb2a57942a3e028d4e", + "reference": "ec8acfd8cb0332e68fc57bfb2a57942a3e028d4e", "shasum": "" }, "require": { @@ -38,9 +38,9 @@ "description": "Composer-based Psalm Phar", "support": { "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/4.10.0" + "source": "https://github.com/psalm/phar/tree/4.11.1" }, - "time": "2021-09-05T00:07:08+00:00" + "time": "2021-10-24T13:47:38+00:00" } ], "aliases": [], @@ -50,7 +50,8 @@ "prefer-lowest": false, "platform": { "php": "^8.1.0", - "ext-pdo": "*" + "ext-pdo": "*", + "ext-xml": "*" }, "platform-dev": [], "plugin-api-version": "2.1.0" diff --git a/psalm.xml b/psalm.xml index 7ad4f99..06aeb1d 100644 --- a/psalm.xml +++ b/psalm.xml @@ -7,6 +7,8 @@ allowPhpStormGenerics="true" strictBinaryOperands="true" allowStringToStandInForClass="true" + findUnusedPsalmSuppress="true" + sealAllMethods="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config%20vendor/vimeo/psalm/config.xsd" From 451a85ba83d0d846374b9b6a2d83590d4f7db21c Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 26 Oct 2021 19:59:11 -0400 Subject: [PATCH 25/56] Switch JobController catch to throwable --- Controllers/JobController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Controllers/JobController.php b/Controllers/JobController.php index 245048d..63c0501 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -176,7 +176,7 @@ protected function runJob(Job $job, LoggerInterface $logger, JobMapper $jobMappe if ($jobData instanceof QueueableJob) { try { $success = $jobData->run(); - } catch (Exception) { + } catch (\Throwable) { // Empty catch } $job->status = $success ? QueueableJob::JOB_STATUS_COMPLETE : QueueableJob::JOB_STATUS_PENDING; From 2bb3a49a0ce77c3b63f2dcf832d9aaf0bf593a01 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 26 Oct 2021 20:10:26 -0400 Subject: [PATCH 26/56] Refetch job to record status change properly --- Controllers/JobController.php | 2 ++ FeastTests/Controllers/JobControllerTest.php | 1 + 2 files changed, 3 insertions(+) diff --git a/Controllers/JobController.php b/Controllers/JobController.php index 63c0501..eff8d3f 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -170,6 +170,8 @@ protected function runJob(Job $job, LoggerInterface $logger, JobMapper $jobMappe $this->terminal->error('Could not lock job ' . $job->job_id . '.'); return false; } + /** @var Job $job */ + $job = $jobMapper->findByPrimaryKey($job->job_id); /** @var ?QueueableJob $jobData */ $jobData = unserialize($job->job_context); $success = false; diff --git a/FeastTests/Controllers/JobControllerTest.php b/FeastTests/Controllers/JobControllerTest.php index 85bed1d..898b2f8 100644 --- a/FeastTests/Controllers/JobControllerTest.php +++ b/FeastTests/Controllers/JobControllerTest.php @@ -112,6 +112,7 @@ public function testRunWithJobs(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findOnePendingByQueues')->willReturn($job); + $jobMapper->method('findByPrimaryKey')->willReturn($job); $jobMapper->method('markJobPendingIfAble')->willReturn(true); $controller->listenGet($this->createStub(LoggerInterface::class), $jobMapper, 'default', false); From 4634e9d38d7dcd9af40c63d60e1dc3ae89e740e7 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 26 Oct 2021 20:18:04 -0400 Subject: [PATCH 27/56] Add Job ran_at --- Controllers/JobController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Controllers/JobController.php b/Controllers/JobController.php index eff8d3f..25af8ba 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -182,6 +182,7 @@ protected function runJob(Job $job, LoggerInterface $logger, JobMapper $jobMappe // Empty catch } $job->status = $success ? QueueableJob::JOB_STATUS_COMPLETE : QueueableJob::JOB_STATUS_PENDING; + $job->ran_at = Date::createFromNow(); $job->tries++; if ($job->tries >= $job->max_tries && $success === false) { $job->status = QueueableJob::JOB_STATUS_FAILED; From fff4976ac85414aeb5ac87fb44ca4842d72afb79 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 18 Nov 2021 22:03:34 -0500 Subject: [PATCH 28/56] Switch db methods to variadic [BC Break] Switch the having and where methods of the MySQLQuery class to variadic. This disallows passing in arrays as previously allowed. --- Database/MySQLQuery.php | 4 -- Database/Query.php | 12 ++--- FeastTests/Database/MySQLQueryTest.php | 64 ++++++++++++++----------- FeastTests/Database/SQLiteQueryTest.php | 49 +++++++++---------- 4 files changed, 67 insertions(+), 62 deletions(-) diff --git a/Database/MySQLQuery.php b/Database/MySQLQuery.php index 2491c7d..d962593 100644 --- a/Database/MySQLQuery.php +++ b/Database/MySQLQuery.php @@ -217,8 +217,6 @@ protected function whereToString(string $sql): string $sql .= '(' . $where['statement'] . ') and '; if (is_array($where['bindings'])) { $this->bindings = array_merge($this->bindings, array_values($where['bindings'])); - } elseif ($where['bindings'] !== null) { - $this->bindings[] = $where['bindings']; } } $sql = substr($sql, 0, -5); @@ -249,8 +247,6 @@ protected function havingToString(string $sql): string $havingBinding = $having['bindings']; if (is_array($havingBinding)) { $this->bindings = array_merge($this->bindings, array_values($havingBinding)); - } else { - $this->bindings[] = $havingBinding; } } $sql = substr($sql, 0, -5); diff --git a/Database/Query.php b/Database/Query.php index 9691cba..82b7da1 100755 --- a/Database/Query.php +++ b/Database/Query.php @@ -65,13 +65,13 @@ public function __construct(protected PDO $database) /** * Add where clause. * - * Bindings can be a scalar or Feast\Date or an array for multiple bindings. + * Bindings can be a scalar or Feast\Date and are variadic for multiple bindings. * * @param string $statement - * @param Date|string|int|bool|float|array|null $bindings + * @param Date|string|int|bool|float|null $bindings * @return static */ - public function where(string $statement, Date|string|int|bool|float|array|null $bindings = null): static + public function where(string $statement, Date|string|int|bool|float|null ...$bindings): static { $this->where[] = ['statement' => $statement, 'bindings' => $bindings]; @@ -81,13 +81,13 @@ public function where(string $statement, Date|string|int|bool|float|array|null $ /** * Add having clause. * - * Bindings can be a scalar or an array for multiple bindings. + * Bindings can be a scalar or Feast\Date and are variadic for multiple bindings. * * @param string $statement - * @param Date|string|int|bool|float|array|null $bindings + * @param Date|string|int|bool|float|null $bindings * @return static */ - public function having(string $statement, Date|string|int|bool|float|array|null $bindings = null): static + public function having(string $statement, Date|string|int|bool|float|null ...$bindings): static { $this->having[] = ['statement' => $statement, 'bindings' => $bindings]; diff --git a/FeastTests/Database/MySQLQueryTest.php b/FeastTests/Database/MySQLQueryTest.php index b0318d5..8ed61dd 100644 --- a/FeastTests/Database/MySQLQueryTest.php +++ b/FeastTests/Database/MySQLQueryTest.php @@ -44,56 +44,57 @@ public function testFrom(): void { $query = $this->getValidQuery(); $query->from('test', ['test']); - $this->assertEquals('SELECT test FROM test', $query->__toString()); + $this->assertEquals('SELECT test FROM test', (string)$query); } public function testSelect(): void { $query = $this->getValidQuery(); $query->select('test'); - $this->assertEquals('SELECT test.* FROM test', $query->__toString()); + $this->assertEquals('SELECT test.* FROM test', (string)$query); } public function testFromNoColumn(): void { $query = $this->getValidQuery(); $query->from('test'); - $this->assertEquals('SELECT test.* FROM test', $query->__toString()); + $this->assertEquals('SELECT test.* FROM test', (string)$query); } public function testLimit(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->limit(1, 1); - $this->assertEquals('SELECT test FROM test LIMIT 1,1', $query->__toString()); + $this->assertEquals('SELECT test FROM test LIMIT 1,1', (string)$query); } public function testGroupBy(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->groupBy('test'); - $this->assertEquals('SELECT test FROM test GROUP BY test', $query->__toString()); + $this->assertEquals('SELECT test FROM test GROUP BY test', (string)$query); } public function testReplace(): void { $query = $this->getValidQuery(); $query->replace('test', ['test' => 'test']); - $this->assertEquals('REPLACE INTO test (test) VALUES (?)', $query->__toString()); + $this->assertEquals('REPLACE INTO test (test) VALUES (?)', (string)$query); } - public function testWhere(): void + public function testWhereMulti(): void { $query = $this->getValidQuery(); - $query->from('test', ['test'])->where('test = ?', ['test']); - $this->assertEquals('SELECT test FROM test WHERE (test = ?)', $query->__toString()); + $query->from('test', ['test'])->where('test = ? and feast = ?', 'test', 'framework'); + $this->assertEquals('SELECT test FROM test WHERE (test = ? and feast = ?)', (string)$query); + $this->assertEquals('SELECT test FROM test WHERE (test = \'test\' and feast = \'framework\')', $query->getRawQueryWithParams()); } - public function testWhereNonArray(): void + public function testWhereSingle(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->where('test = ?', 'test'); - $this->assertEquals('SELECT test FROM test WHERE (test = ?)', $query->__toString()); + $this->assertEquals('SELECT test FROM test WHERE (test = ?)', (string)$query); } public function testExecute(): void @@ -116,21 +117,21 @@ public function testDescribe(): void { $query = $this->getValidQuery(); $query->describe('test'); - $this->assertEquals('DESCRIBE test', $query->__toString()); + $this->assertEquals('DESCRIBE test', (string)$query); } public function testDelete(): void { $query = $this->getValidQuery(); $query->delete('test'); - $this->assertEquals('DELETE FROM test', $query->__toString()); + $this->assertEquals('DELETE FROM test', (string)$query); } public function testInnerJoin(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->innerJoin('test2', 'test.ing', 'test2.ing'); - $this->assertEquals('SELECT test FROM test INNER JOIN test2 ON test.ing = test2.ing', $query->__toString()); + $this->assertEquals('SELECT test FROM test INNER JOIN test2 ON test.ing = test2.ing', (string)$query); } public function testInnerJoinArray(): void @@ -139,7 +140,7 @@ public function testInnerJoinArray(): void $query->from('test', ['test'])->innerJoin('test2', ['test.ing', 'test.feast'], ['test2.ing', 'test2.feast']); $this->assertEquals( 'SELECT test FROM test INNER JOIN test2 ON test.ing = test2.ing AND test.feast = test2.feast', - $query->__toString() + (string)$query ); } @@ -161,28 +162,28 @@ public function testLeftJoinUsing(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->leftJoinUsing('test2', 'ing'); - $this->assertEquals('SELECT test FROM test LEFT JOIN test2 USING (ing)', $query->__toString()); + $this->assertEquals('SELECT test FROM test LEFT JOIN test2 USING (ing)', (string)$query); } public function testLeftJoinUsingArray(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->leftJoinUsing('test2', ['ing', 'feast']); - $this->assertEquals('SELECT test FROM test LEFT JOIN test2 USING (ing,feast)', $query->__toString()); + $this->assertEquals('SELECT test FROM test LEFT JOIN test2 USING (ing,feast)', (string)$query); } public function testInsert(): void { $query = $this->getValidQuery(); $query->insert('test', ['testing' => 'test2']); - $this->assertEquals('INSERT INTO test (testing) VALUES (?)', $query->__toString()); + $this->assertEquals('INSERT INTO test (testing) VALUES (?)', (string)$query); } public function testInnerJoinUsing(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->innerJoinUsing('test2', 'ing'); - $this->assertEquals('SELECT test FROM test INNER JOIN test2 USING (ing)', $query->__toString()); + $this->assertEquals('SELECT test FROM test INNER JOIN test2 USING (ing)', (string)$query); } public function testGetRawQueryWithParams(): void @@ -192,10 +193,17 @@ public function testGetRawQueryWithParams(): void $this->assertEquals('SELECT test FROM test WHERE (test = \'test\')', $query->getRawQueryWithParams()); } + public function testGetRawQueryWithMultiParams(): void + { + $query = $this->getValidQuery(); + $query->from('test', ['test'])->where('test = ? and feast = ?', 'test', 'framework'); + $this->assertEquals('SELECT test FROM test WHERE (test = \'test\' and feast = \'framework\')', $query->getRawQueryWithParams()); + } + public function testGetRawQueryNoParams(): void { $query = $this->getValidQuery(); - $query->from('test', ['test'])->where('1=1', ['test']); + $query->from('test', ['test'])->where('1=1', 'test'); $this->assertEquals('SELECT test FROM test WHERE (1=1)', $query->getRawQueryWithParams()); } @@ -210,49 +218,49 @@ public function testLeftJoin(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->leftJoin('test2', 'test.ing', 'test2.ing'); - $this->assertEquals('SELECT test FROM test LEFT JOIN test2 ON test.ing = test2.ing', $query->__toString()); + $this->assertEquals('SELECT test FROM test LEFT JOIN test2 ON test.ing = test2.ing', (string)$query); } public function testRightJoinUsing(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->rightJoinUsing('test2', 'ing'); - $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 USING (ing)', $query->__toString()); + $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 USING (ing)', (string)$query); } public function testRightJoin(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->rightJoin('test2', 'test.ing', 'test2.ing'); - $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 ON test.ing = test2.ing', $query->__toString()); + $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 ON test.ing = test2.ing', (string)$query); } public function testOrderBy(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->orderBy('test'); - $this->assertEquals('SELECT test FROM test ORDER BY test', $query->__toString()); + $this->assertEquals('SELECT test FROM test ORDER BY test', (string)$query); } public function testUpdate(): void { $query = $this->getValidQuery(); $query->update('test', ['field' => 'test']); - $this->assertEquals('UPDATE test SET field = ?', $query->__toString()); + $this->assertEquals('UPDATE test SET field = ?', (string)$query); } public function testHaving(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->having('test > ?', '1'); - $this->assertEquals('SELECT test FROM test HAVING (test > ?)', $query->__toString()); + $this->assertEquals('SELECT test FROM test HAVING (test > ?)', (string)$query); } public function testHavingMulti(): void { $query = $this->getValidQuery(); - $query->from('test', ['test'])->having('test > ?', ['1']); - $this->assertEquals('SELECT test FROM test HAVING (test > ?)', $query->__toString()); + $query->from('test', ['test'])->having('test > ?', '1'); + $this->assertEquals('SELECT test FROM test HAVING (test > ?)', (string)$query); } public function testSQLiteQuery(): void diff --git a/FeastTests/Database/SQLiteQueryTest.php b/FeastTests/Database/SQLiteQueryTest.php index 34ddb70..192b204 100644 --- a/FeastTests/Database/SQLiteQueryTest.php +++ b/FeastTests/Database/SQLiteQueryTest.php @@ -41,56 +41,56 @@ public function testFrom(): void { $query = $this->getValidQuery(); $query->from('test', ['test']); - $this->assertEquals('SELECT test FROM test', $query->__toString()); + $this->assertEquals('SELECT test FROM test', (string)$query); } public function testSelect(): void { $query = $this->getValidQuery(); $query->select('test'); - $this->assertEquals('SELECT test.* FROM test', $query->__toString()); + $this->assertEquals('SELECT test.* FROM test', (string)$query); } public function testFromNoColumn(): void { $query = $this->getValidQuery(); $query->from('test'); - $this->assertEquals('SELECT test.* FROM test', $query->__toString()); + $this->assertEquals('SELECT test.* FROM test', (string)$query); } public function testLimit(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->limit(1, 1); - $this->assertEquals('SELECT test FROM test LIMIT 1,1', $query->__toString()); + $this->assertEquals('SELECT test FROM test LIMIT 1,1', (string)$query); } public function testGroupBy(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->groupBy('test'); - $this->assertEquals('SELECT test FROM test GROUP BY test', $query->__toString()); + $this->assertEquals('SELECT test FROM test GROUP BY test', (string)$query); } public function testReplace(): void { $query = $this->getValidQuery(); $query->replace('test', ['test' => 'test']); - $this->assertEquals('REPLACE INTO test (test) VALUES (?)', $query->__toString()); + $this->assertEquals('REPLACE INTO test (test) VALUES (?)', (string)$query); } public function testWhere(): void { $query = $this->getValidQuery(); - $query->from('test', ['test'])->where('test = ?', ['test']); - $this->assertEquals('SELECT test FROM test WHERE (test = ?)', $query->__toString()); + $query->from('test', ['test'])->where('test = ?', 'test'); + $this->assertEquals('SELECT test FROM test WHERE (test = ?)', (string)$query); } public function testWhereNonArray(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->where('test = ?', 'test'); - $this->assertEquals('SELECT test FROM test WHERE (test = ?)', $query->__toString()); + $this->assertEquals('SELECT test FROM test WHERE (test = ?)', (string)$query); } public function testExecute(): void @@ -113,42 +113,42 @@ public function testDescribe(): void { $query = $this->getValidQuery(); $query->describe('test'); - $this->assertEquals('DESCRIBE test', $query->__toString()); + $this->assertEquals('DESCRIBE test', (string)$query); } public function testDelete(): void { $query = $this->getValidQuery(); $query->delete('test'); - $this->assertEquals('DELETE FROM test', $query->__toString()); + $this->assertEquals('DELETE FROM test', (string)$query); } public function testInnerJoin(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->innerJoin('test2', 'test.ing', 'test2.ing'); - $this->assertEquals('SELECT test FROM test INNER JOIN test2 ON test.ing = test2.ing', $query->__toString()); + $this->assertEquals('SELECT test FROM test INNER JOIN test2 ON test.ing = test2.ing', (string)$query); } public function testLeftJoinUsing(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->leftJoinUsing('test2', 'ing'); - $this->assertEquals('SELECT test FROM test LEFT JOIN test2 USING (ing)', $query->__toString()); + $this->assertEquals('SELECT test FROM test LEFT JOIN test2 USING (ing)', (string)$query); } public function testInsert(): void { $query = $this->getValidQuery(); $query->insert('test', ['testing' => 'test2']); - $this->assertEquals('INSERT INTO test (testing) VALUES (?)', $query->__toString()); + $this->assertEquals('INSERT INTO test (testing) VALUES (?)', (string)$query); } public function testInnerJoinUsing(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->innerJoinUsing('test2', 'ing'); - $this->assertEquals('SELECT test FROM test INNER JOIN test2 USING (ing)', $query->__toString()); + $this->assertEquals('SELECT test FROM test INNER JOIN test2 USING (ing)', (string)$query); } public function testGetRawQueryWithParams(): void @@ -161,7 +161,7 @@ public function testGetRawQueryWithParams(): void public function testGetRawQueryNoParams(): void { $query = $this->getValidQuery(); - $query->from('test', ['test'])->where('1=1', ['test']); + $query->from('test', ['test'])->where('1=1', 'test'); $this->assertEquals('SELECT test FROM test WHERE (1=1)', $query->getRawQueryWithParams()); } @@ -169,49 +169,50 @@ public function testLeftJoin(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->leftJoin('test2', 'test.ing', 'test2.ing'); - $this->assertEquals('SELECT test FROM test LEFT JOIN test2 ON test.ing = test2.ing', $query->__toString()); + $this->assertEquals('SELECT test FROM test LEFT JOIN test2 ON test.ing = test2.ing', (string)$query); } public function testRightJoinUsing(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->rightJoinUsing('test2', 'ing'); - $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 USING (ing)', $query->__toString()); + $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 USING (ing)', (string)$query); } public function testRightJoin(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->rightJoin('test2', 'test.ing', 'test2.ing'); - $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 ON test.ing = test2.ing', $query->__toString()); + $this->assertEquals('SELECT test FROM test RIGHT JOIN test2 ON test.ing = test2.ing', (string)$query); } public function testOrderBy(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->orderBy('test'); - $this->assertEquals('SELECT test FROM test ORDER BY test', $query->__toString()); + $this->assertEquals('SELECT test FROM test ORDER BY test', (string)$query); } public function testUpdate(): void { $query = $this->getValidQuery(); $query->update('test', ['field' => 'test']); - $this->assertEquals('UPDATE test SET field = ?', $query->__toString()); + $this->assertEquals('UPDATE test SET field = ?', (string)$query); } public function testHaving(): void { $query = $this->getValidQuery(); $query->from('test', ['test'])->having('test > ?', '1'); - $this->assertEquals('SELECT test FROM test HAVING (test > ?)', $query->__toString()); + $this->assertEquals('SELECT test FROM test HAVING (test > ?)', (string)$query); } public function testHavingMulti(): void { $query = $this->getValidQuery(); - $query->from('test', ['test'])->having('test > ?', ['1']); - $this->assertEquals('SELECT test FROM test HAVING (test > ?)', $query->__toString()); + $query->from('test', ['test'])->having('test > ? and test < ?', '1','3'); + $this->assertEquals('SELECT test FROM test HAVING (test > ? and test < ?)', (string)$query); + $this->assertEquals('SELECT test FROM test HAVING (test > \'1\' and test < \'3\')', $query->getRawQueryWithParams()); } } From ac1b43840f8964ee45f6d28c46d86fc673d961b5 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 18 Nov 2021 22:05:51 -0500 Subject: [PATCH 29/56] Update Job method name [BC Break] --- Controllers/JobController.php | 12 ++++-- FeastTests/Controllers/JobControllerTest.php | 12 +++--- FeastTests/Mapper/JobMapper.php | 39 ++++++++++++------- Install/Mapper/JobMapper.php.txt | 41 +++++++++++++++----- 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/Controllers/JobController.php b/Controllers/JobController.php index 150affa..2b760e2 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -60,7 +60,7 @@ public function listenGet( do { $job = $jobMapper->findOnePendingByQueues($queueList); if ($job instanceof Job && !file_exists(APPLICATION_ROOT . DIRECTORY_SEPARATOR . 'maintenance.txt')) { - $this->runJob($job, $errorLogger,$logger, $jobMapper); + $this->runJob($job, $errorLogger, $logger, $jobMapper); } else { $this->terminal->command('No jobs found. ', false); if ($keepalive) { @@ -167,9 +167,13 @@ public function runCronItemGet( /** * @throws Exception */ - protected function runJob(Job $job, ErrorLoggerInterface $errorLogger, LoggerInterface $logger, JobMapper $jobMapper): bool - { - $canRun = $jobMapper->markJobPendingIfAble($job); + protected function runJob( + Job $job, + ErrorLoggerInterface $errorLogger, + LoggerInterface $logger, + JobMapper $jobMapper + ): bool { + $canRun = $jobMapper->markJobRunningIfAble($job); if ($canRun === false) { $logger->error('Could not lock job ' . $job->job_id . '.'); $this->terminal->error('Could not lock job ' . $job->job_id . '.'); diff --git a/FeastTests/Controllers/JobControllerTest.php b/FeastTests/Controllers/JobControllerTest.php index 58d2e66..40274df 100644 --- a/FeastTests/Controllers/JobControllerTest.php +++ b/FeastTests/Controllers/JobControllerTest.php @@ -130,7 +130,7 @@ public function testRunWithJobs(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findOnePendingByQueues')->willReturn($job); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->listenGet( $this->createStub(LoggerInterface::class), @@ -253,7 +253,7 @@ public function testRunOneCannotLock(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(false); + $jobMapper->method('markJobRunningIfAble')->willReturn(false); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -287,7 +287,7 @@ public function testRunOneSuccess(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -321,7 +321,7 @@ public function testRunOneWillThrow(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -355,7 +355,7 @@ public function testRunOneTriesExceeded(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -388,7 +388,7 @@ public function testRunOneInvalidContext(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, diff --git a/FeastTests/Mapper/JobMapper.php b/FeastTests/Mapper/JobMapper.php index 80450c5..ff3cc75 100644 --- a/FeastTests/Mapper/JobMapper.php +++ b/FeastTests/Mapper/JobMapper.php @@ -4,10 +4,8 @@ namespace Mapper; -use Feast\BaseMapper; -use Feast\Exception\ServerFailureException; -use Feast\ServiceContainer\NotFoundException; -use Model\Job; +use \Feast\BaseMapper; +use \Model\Job; class JobMapper extends BaseMapper { @@ -19,8 +17,6 @@ class JobMapper extends BaseMapper * @param int|string $value * @param bool $validate * @return ?Job - * @throws ServerFailureException - * @throws NotFoundException */ public function findByPrimaryKey(int|string $value, bool $validate = false): ?Job { @@ -32,11 +28,19 @@ public function findByPrimaryKey(int|string $value, bool $validate = false): ?Jo return null; } + /** + * Find a single pending job if available. + * + * @param array $queues + * @return \Model\Job|null + * @throws \Feast\Exception\ServerFailureException + * @throws \Feast\ServiceContainer\NotFoundException + */ public function findOnePendingByQueues(array $queues): ?Job { $query = $this->getQueryBase()->where('status = ?', 'pending')->where( - 'tries < max_tries and queue_name IN (' . str_repeat('?,', count($queues) - 1) . '?)', - $queues + 'tries < max_tries and queue_name IN (' . str_repeat('?,', count($queues) - 1) . '?)', + ...$queues ); $return = $this->fetchOne($query); if ($return === null || $return instanceof Job) { @@ -45,16 +49,23 @@ public function findOnePendingByQueues(array $queues): ?Job return null; } - public function markJobPendingIfAble(Job $job): bool + /** + * Mark Job as running. + * + * @param \Model\Job $job + * @return bool + * @throws \Exception + */ + public function markJobRunningIfAble(Job $job): bool { $query = $this->connection->update(self::TABLE_NAME, ['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) ->where( 'job_id = ? and status IN (?,?)', - [ - $job->job_id, - \Feast\Jobs\QueueableJob::JOB_STATUS_PENDING, - \Feast\Jobs\QueueableJob::JOB_STATUS_FAILED - ] + + $job->job_id, + \Feast\Jobs\QueueableJob::JOB_STATUS_PENDING, + \Feast\Jobs\QueueableJob::JOB_STATUS_FAILED + ); $result = $query->execute(); return $result->rowCount() !== 0; diff --git a/Install/Mapper/JobMapper.php.txt b/Install/Mapper/JobMapper.php.txt index d0f6131..ff3cc75 100644 --- a/Install/Mapper/JobMapper.php.txt +++ b/Install/Mapper/JobMapper.php.txt @@ -6,8 +6,7 @@ namespace Mapper; use \Feast\BaseMapper; use \Model\Job; -use \Feast\Exception\ServerFailureException; -use \Feast\ServiceContainer\NotFoundException; + class JobMapper extends BaseMapper { protected const OBJECT_NAME = Job::class; @@ -18,8 +17,6 @@ class JobMapper extends BaseMapper * @param int|string $value * @param bool $validate * @return ?Job - * @throws ServerFailureException - * @throws NotFoundException */ public function findByPrimaryKey(int|string $value, bool $validate = false): ?Job { @@ -31,19 +28,45 @@ class JobMapper extends BaseMapper return null; } + /** + * Find a single pending job if available. + * + * @param array $queues + * @return \Model\Job|null + * @throws \Feast\Exception\ServerFailureException + * @throws \Feast\ServiceContainer\NotFoundException + */ public function findOnePendingByQueues(array $queues): ?Job { - $query = $this->getQueryBase()->where('status = ?','pending')->where('tries < max_tries and queue_name IN (' . str_repeat('?,',count($queues)-1) . '?)',$queues); + $query = $this->getQueryBase()->where('status = ?', 'pending')->where( + 'tries < max_tries and queue_name IN (' . str_repeat('?,', count($queues) - 1) . '?)', + ...$queues + ); $return = $this->fetchOne($query); - if ( $return === null || $return instanceof Job ) { + if ($return === null || $return instanceof Job) { return $return; } return null; } - public function markJobPendingIfAble(Job $job): bool { - $query = $this->connection->update(self::TABLE_NAME,['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) - ->where('job_id = ? and status IN (?,?)',[$job->job_id,\Feast\Jobs\QueueableJob::JOB_STATUS_PENDING,\Feast\Jobs\QueueableJob::JOB_STATUS_FAILED]); + /** + * Mark Job as running. + * + * @param \Model\Job $job + * @return bool + * @throws \Exception + */ + public function markJobRunningIfAble(Job $job): bool + { + $query = $this->connection->update(self::TABLE_NAME, ['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) + ->where( + 'job_id = ? and status IN (?,?)', + + $job->job_id, + \Feast\Jobs\QueueableJob::JOB_STATUS_PENDING, + \Feast\Jobs\QueueableJob::JOB_STATUS_FAILED + + ); $result = $query->execute(); return $result->rowCount() !== 0; } From 56259f1560e5c72f9ca2c5ab273f4b7553aad999 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 23 Nov 2021 17:33:59 -0500 Subject: [PATCH 30/56] Psalm update, remove suppressions, update errors --- Controllers/JobController.php | 12 ++++++++++++ Logger/Logger.php | 5 +---- Response.php | 3 +-- Router/Router.php | 3 +-- composer.lock | 12 ++++++------ 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Controllers/JobController.php b/Controllers/JobController.php index 2b760e2..7fe4453 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -37,6 +37,18 @@ class JobController extends CliController { + /** + * @param LoggerInterface $logger + * @param JobMapper $jobMapper + * @param ErrorLoggerInterface $errorLogger + * @param string|null $queues + * @param bool $keepalive + * @param positive-int $wait + * @param bool $exitLoop + * @return void + * @throws \Feast\Exception\ServerFailureException + * @throws \Feast\ServiceContainer\NotFoundException + */ #[Action(usage: '--keepalive={true|false} {queues}', description: 'Listen for and run all jobs on one or more queues.')] #[Param(type: 'string', name: 'queues', description: 'Name of queues to monitor, pipe delimited')] #[Param(type: 'bool', name: 'keepalive', description: 'True to run as a process loop (default: true)', paramType: ParamType::FLAG)] diff --git a/Logger/Logger.php b/Logger/Logger.php index 2a86656..f963a14 100644 --- a/Logger/Logger.php +++ b/Logger/Logger.php @@ -165,13 +165,11 @@ public function log($level, $message, array $context = []): void $message = strtoupper($level) . ': ' . $message; $level = $this->getLevelFromString($level); } - /** @psalm-suppress UndefinedPropertyFetch - False error */ if ($level instanceof LogLevelCode === false || $level->value < $this->logLevel->value) { return; } $message = $this->interpolateContext($message, $context); - /** @psalm-suppress UndefinedPropertyFetch - False error */ - $this->rawLog((int)$level->value, (date('[Y-m-d H:i:s] ')) . trim($message)); + $this->rawLog($level->value, (date('[Y-m-d H:i:s] ')) . trim($message)); if (isset($context['exception']) && $context['exception'] instanceof Throwable) { $exception = $context['exception']; @@ -209,7 +207,6 @@ protected function interpolateContext(Stringable|string $message, array $context */ public function rawLog(int $level, string $message): void { - /** @psalm-suppress UndefinedPropertyFetch */ if ($level < $this->logLevel->value) { return; } diff --git a/Response.php b/Response.php index e27fbd9..dcf3913 100644 --- a/Response.php +++ b/Response.php @@ -65,8 +65,7 @@ public function setResponseCode(ResponseCode $responseCode): void */ public function sendResponseCode(): void { - /** @psalm-suppress UndefinedPropertyFetch */ - http_response_code((int)$this->responseCode->value); + http_response_code($this->responseCode->value); } /** diff --git a/Router/Router.php b/Router/Router.php index ca890cc..f756325 100644 --- a/Router/Router.php +++ b/Router/Router.php @@ -877,14 +877,13 @@ private function analyzeMethodForRoute(ReflectionMethod $method, string $classNa /** @var Path $pathAttribute */ $pathAttribute = $attribute->newInstance(); foreach ($pathAttribute->getMethods() as $methodType) { - /** @psalm-suppress UndefinedPropertyFetch */ $this->addRoute( $pathAttribute->path, $className, $methodName, $pathAttribute->name, $pathAttribute->defaults, - (string)$methodType->value, + $methodType->value, $module ); } diff --git a/composer.lock b/composer.lock index e250c2d..b465906 100644 --- a/composer.lock +++ b/composer.lock @@ -9,16 +9,16 @@ "packages-dev": [ { "name": "psalm/phar", - "version": "4.12.0", + "version": "4.13.0", "source": { "type": "git", "url": "https://github.com/psalm/phar.git", - "reference": "0ccb66911b6ca4cd4ecb88a619476791cf624bab" + "reference": "2883fa6aaff05564365cb16435196616bd853e3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/0ccb66911b6ca4cd4ecb88a619476791cf624bab", - "reference": "0ccb66911b6ca4cd4ecb88a619476791cf624bab", + "url": "https://api.github.com/repos/psalm/phar/zipball/2883fa6aaff05564365cb16435196616bd853e3a", + "reference": "2883fa6aaff05564365cb16435196616bd853e3a", "shasum": "" }, "require": { @@ -38,9 +38,9 @@ "description": "Composer-based Psalm Phar", "support": { "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/4.12.0" + "source": "https://github.com/psalm/phar/tree/4.13.0" }, - "time": "2021-11-06T18:59:32+00:00" + "time": "2021-11-21T02:42:44+00:00" } ], "aliases": [], From 39687376003b9956f5bcefb255e3674a1c7c825a Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 23 Nov 2021 18:25:44 -0500 Subject: [PATCH 31/56] Correct tests --- FeastTests/Controllers/JobControllerTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/FeastTests/Controllers/JobControllerTest.php b/FeastTests/Controllers/JobControllerTest.php index 40274df..58d2e66 100644 --- a/FeastTests/Controllers/JobControllerTest.php +++ b/FeastTests/Controllers/JobControllerTest.php @@ -130,7 +130,7 @@ public function testRunWithJobs(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findOnePendingByQueues')->willReturn($job); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobRunningIfAble')->willReturn(true); + $jobMapper->method('markJobPendingIfAble')->willReturn(true); $controller->listenGet( $this->createStub(LoggerInterface::class), @@ -253,7 +253,7 @@ public function testRunOneCannotLock(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobRunningIfAble')->willReturn(false); + $jobMapper->method('markJobPendingIfAble')->willReturn(false); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -287,7 +287,7 @@ public function testRunOneSuccess(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobRunningIfAble')->willReturn(true); + $jobMapper->method('markJobPendingIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -321,7 +321,7 @@ public function testRunOneWillThrow(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobRunningIfAble')->willReturn(true); + $jobMapper->method('markJobPendingIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -355,7 +355,7 @@ public function testRunOneTriesExceeded(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobRunningIfAble')->willReturn(true); + $jobMapper->method('markJobPendingIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -388,7 +388,7 @@ public function testRunOneInvalidContext(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobRunningIfAble')->willReturn(true); + $jobMapper->method('markJobPendingIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, From c046f9f1b0d936e46de7f0821750aa442c7ea431 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 23 Nov 2021 18:41:26 -0500 Subject: [PATCH 32/56] Remove markJobPendingIfAble method --- Controllers/JobController.php | 3 +-- FeastTests/Controllers/JobControllerTest.php | 12 +++++------ FeastTests/Mapper/JobMapper.php | 9 +-------- Install/Mapper/JobMapper.php.txt | 21 +++++++------------- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/Controllers/JobController.php b/Controllers/JobController.php index e558e4d..7fe4453 100644 --- a/Controllers/JobController.php +++ b/Controllers/JobController.php @@ -185,8 +185,7 @@ protected function runJob( LoggerInterface $logger, JobMapper $jobMapper ): bool { - /** @psalm-suppress DeprecatedMethod @todo: switch to markJobRunningIfAble */ - $canRun = $jobMapper->markJobPendingIfAble($job); + $canRun = $jobMapper->markJobRunningIfAble($job); if ($canRun === false) { $logger->error('Could not lock job ' . $job->job_id . '.'); $this->terminal->error('Could not lock job ' . $job->job_id . '.'); diff --git a/FeastTests/Controllers/JobControllerTest.php b/FeastTests/Controllers/JobControllerTest.php index 58d2e66..40274df 100644 --- a/FeastTests/Controllers/JobControllerTest.php +++ b/FeastTests/Controllers/JobControllerTest.php @@ -130,7 +130,7 @@ public function testRunWithJobs(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findOnePendingByQueues')->willReturn($job); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->listenGet( $this->createStub(LoggerInterface::class), @@ -253,7 +253,7 @@ public function testRunOneCannotLock(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(false); + $jobMapper->method('markJobRunningIfAble')->willReturn(false); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -287,7 +287,7 @@ public function testRunOneSuccess(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -321,7 +321,7 @@ public function testRunOneWillThrow(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -355,7 +355,7 @@ public function testRunOneTriesExceeded(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, @@ -388,7 +388,7 @@ public function testRunOneInvalidContext(): void $jobMapper = $this->createStub(JobMapper::class); $jobMapper->method('findByPrimaryKey')->willReturn($job); - $jobMapper->method('markJobPendingIfAble')->willReturn(true); + $jobMapper->method('markJobRunningIfAble')->willReturn(true); $controller->runOneGet( $this->createStub(LoggerInterface::class), $jobMapper, diff --git a/FeastTests/Mapper/JobMapper.php b/FeastTests/Mapper/JobMapper.php index c7eb6ea..33a2e41 100644 --- a/FeastTests/Mapper/JobMapper.php +++ b/FeastTests/Mapper/JobMapper.php @@ -52,19 +52,12 @@ public function findOnePendingByQueues(array $queues): ?Job } /** - * Mark job as running. Method name is incorrect and will be removed in 2.0 + * Mark job as running. * * @param \Model\Job $job * @return bool * @throws \Exception - * @deprecated */ - public function markJobPendingIfAble(Job $job): bool - { - trigger_error('This method is deprecated. Use JobMapper::markJobRunningIfAble', E_USER_DEPRECATED); - return $this->markJobRunningIfAble($job); - } - public function markJobRunningIfAble(Job $job): bool { $query = $this->connection->update(self::TABLE_NAME, ['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) diff --git a/Install/Mapper/JobMapper.php.txt b/Install/Mapper/JobMapper.php.txt index 83482e3..3a62691 100644 --- a/Install/Mapper/JobMapper.php.txt +++ b/Install/Mapper/JobMapper.php.txt @@ -49,20 +49,13 @@ class JobMapper extends BaseMapper return null; } - /** - * Mark job as running. Method name is incorrect and will be removed in 2.0 - * - * @param \Model\Job $job - * @return bool - * @throws \Exception - * @deprecated - */ - public function markJobPendingIfAble(Job $job): bool - { - trigger_error('This method is deprecated. Use JobMapper::markJobRunningIfAble',E_USER_DEPRECATED); - return $this->markJobRunningIfAble($job); - } - + /** + * Mark job as running. + * + * @param \Model\Job $job + * @return bool + * @throws \Exception + */ public function markJobRunningIfAble(Job $job): bool { $query = $this->connection->update(self::TABLE_NAME,['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) ->where('job_id = ? and status IN (?,?)',[$job->job_id,\Feast\Jobs\QueueableJob::JOB_STATUS_PENDING,\Feast\Jobs\QueueableJob::JOB_STATUS_FAILED]); From b1657bea6f9c9b6d1fc9d0ec004f9ed626762c62 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 24 Nov 2021 17:53:43 -0500 Subject: [PATCH 33/56] Use psalm dev-master for 2.0 --- composer.json | 2 +- composer.lock | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index ffa7864..66feacc 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "ext-curl": "*" }, "require-dev": { - "psalm/phar": "^4.10" + "psalm/phar": "dev-master" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index b465906..7b9a48f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2076bd2bed809846d11b8c89fe4af351", + "content-hash": "e10a43de0a0dc3169f60b51604c0efec", "packages": [], "packages-dev": [ { "name": "psalm/phar", - "version": "4.13.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/psalm/phar.git", - "reference": "2883fa6aaff05564365cb16435196616bd853e3a" + "reference": "882ca87ee9b10237b0e952d6cb713bcead486d3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/2883fa6aaff05564365cb16435196616bd853e3a", - "reference": "2883fa6aaff05564365cb16435196616bd853e3a", + "url": "https://api.github.com/repos/psalm/phar/zipball/882ca87ee9b10237b0e952d6cb713bcead486d3b", + "reference": "882ca87ee9b10237b0e952d6cb713bcead486d3b", "shasum": "" }, "require": { @@ -27,6 +27,7 @@ "conflict": { "vimeo/psalm": "*" }, + "default-branch": true, "bin": [ "psalm.phar" ], @@ -38,14 +39,16 @@ "description": "Composer-based Psalm Phar", "support": { "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/4.13.0" + "source": "https://github.com/psalm/phar/tree/master" }, - "time": "2021-11-21T02:42:44+00:00" + "time": "2021-11-24T22:46:14+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "psalm/phar": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 78cb8136c1cdabec43c4161822273cd71cdc1453 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 24 Nov 2021 18:04:13 -0500 Subject: [PATCH 34/56] Remove redundant null call, add php extensions --- Csv/CsvReader.php | 3 ++- composer.json | 6 +++++- composer.lock | 8 ++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Csv/CsvReader.php b/Csv/CsvReader.php index 4d5ca8f..fdd6c5e 100644 --- a/Csv/CsvReader.php +++ b/Csv/CsvReader.php @@ -91,8 +91,9 @@ public function getNextLine(): array|false $this->buildHeader(); } $return = []; + /** @var array|false $row */ $row = $this->file->fgetcsv(); - if ($row === null || $row === false) { + if ($row === false) { return false; } diff --git a/composer.json b/composer.json index 66feacc..c6ee836 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,11 @@ "php": "^8.1.0", "ext-pdo": "*", "ext-xml": "*", - "ext-readline": "*" + "ext-readline": "*", + "ext-ctype": "*", + "ext-simplexml": "*", + "ext-libxml": "*", + "ext-fileinfo": "*" }, "suggest": { "ext-mysql": "*", diff --git a/composer.lock b/composer.lock index 7b9a48f..a503903 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e10a43de0a0dc3169f60b51604c0efec", + "content-hash": "c485b209d8b022f85b5a4317b180c7cc", "packages": [], "packages-dev": [ { @@ -55,7 +55,11 @@ "php": "^8.1.0", "ext-pdo": "*", "ext-xml": "*", - "ext-readline": "*" + "ext-readline": "*", + "ext-ctype": "*", + "ext-simplexml": "*", + "ext-libxml": "*", + "ext-fileinfo": "*" }, "platform-dev": [], "plugin-api-version": "2.1.0" From 3fc30e78215e19904480037431fc252ff5e9ba9e Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 24 Nov 2021 18:11:54 -0500 Subject: [PATCH 35/56] switch workflow runner to use psalm from composer --- .github/workflows/Psalm.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Psalm.yaml b/.github/workflows/Psalm.yaml index a4226b5..9ed76d3 100644 --- a/.github/workflows/Psalm.yaml +++ b/.github/workflows/Psalm.yaml @@ -11,7 +11,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: '8.1' - tools: psalm + # tools: psalm - name: Checkout project uses: actions/checkout@v2 @@ -30,7 +30,7 @@ jobs: run: composer install --prefer-dist - name: Run Psalm - run: psalm --show-info=true --no-diff --report=results.sarif + run: ./vendor/bin/psalm --show-info=true --no-diff --report=results.sarif - name: Upload Security Analysis results to GitHub continue-on-error: true From a1bcd6f587a38e4d4f8f74b61ac2397618e8e89b Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 24 Nov 2021 18:13:02 -0500 Subject: [PATCH 36/56] switch workflow runner to use psalm from composer --- .github/workflows/Psalm.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/Psalm.yaml b/.github/workflows/Psalm.yaml index 9ed76d3..e7de9f8 100644 --- a/.github/workflows/Psalm.yaml +++ b/.github/workflows/Psalm.yaml @@ -11,7 +11,6 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: '8.1' - # tools: psalm - name: Checkout project uses: actions/checkout@v2 @@ -30,7 +29,7 @@ jobs: run: composer install --prefer-dist - name: Run Psalm - run: ./vendor/bin/psalm --show-info=true --no-diff --report=results.sarif + run: ./vendor/bin/psalm.phar --show-info=true --no-diff --report=results.sarif - name: Upload Security Analysis results to GitHub continue-on-error: true From f9ce42661326e53e320ed087409c4454319afed5 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 24 Nov 2021 18:19:24 -0500 Subject: [PATCH 37/56] v2.x specific badges --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6f6dbf6..0b56bdc 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,11 @@ # FEAST Framework -![PHPUnit](https://github.com/FeastFramework/framework/workflows/PHPUnit/badge.svg?branch=master) -![Psalm Static analysis](https://github.com/FeastFramework/framework/workflows/Psalm%20Static%20analysis/badge.svg?branch=master) +![PHPUnit](https://github.com/FeastFramework/framework/workflows/PHPUnit/badge.svg?branch=v2.x) +![Psalm Static analysis](https://github.com/FeastFramework/framework/workflows/Psalm%20Static%20analysis/badge.svg?branch=v2.x) [![codecov](https://codecov.io/gh/FeastFramework/framework/branch/master/graph/badge.svg?token=FBP2AKLJB3)](https://codecov.io/gh/FeastFramework/framework) -![PHP Version](https://img.shields.io/packagist/php-v/feast/framework) -[![Packagist](https://img.shields.io/packagist/v/feast/framework)](https://packagist.org/packages/feast/framework) +[![Packagist](https://img.shields.io/packagist/php-v/feast/framework/v2.x-dev)](https://packagist.org/packages/feast/framework) ![License](https://img.shields.io/packagist/l/feast/framework.svg) [![Docs](https://img.shields.io/badge/docs-quickstart-green.svg)](https://docs.feast-framework.com) ## Fast, Easy, Agile, Slim, Tested and Trans-Fat Free. From ca1a988bb618f82042491a52f4f833f8558a7db0 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 25 Nov 2021 13:51:42 -0500 Subject: [PATCH 38/56] Update models doc --- docs/models.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/models.md b/docs/models.md index 946fdec..25fb1d2 100644 --- a/docs/models.md +++ b/docs/models.md @@ -129,11 +129,11 @@ FEAST has many methods to delete records from the database. The main methods to #### Events -FEAST has methods for different database events that you can override in the child class. These events -can fire when a model is saved or when a model is deleted. The methods are `onSave` and `onDelete`. +FEAST has methods for different database events that you can override in the child class. These events can fire when a +model is saved or when a model is deleted. The methods are `onSave` and `onDelete`. -Note that `onDelete` will only fire if `delete` is called, rather than the other deletion methods, as delete -is the only method that has access to a model. +Note that `onDelete` will only fire if `delete` is called, rather than the other deletion methods, as delete is the only +method that has access to a model. [Back to Top](#working-with-databases) @@ -216,15 +216,16 @@ parameters. 3. `autoIncrement` - True if you wish for this to be an autoincrement column. An easier way to create an auto incrementing primary key is with the `autoIncrement` method. This method will create an -int column with the passed in name and optional length. +int column with the passed in name and optional length. #### Adding primary key -FEAST can add a primary key to specified column with the `primary` method. The `primary` method takes only one parameter: -`columnName`. Note that the column specified within this parameter must exist. Also, `primary` method can be called only +FEAST can add a primary key to specified column with the `primary` method. The `primary` method takes only one +parameter: +`columnName`. Note that the column specified within this parameter must exist. Also, `primary` method can be called only once per table. Otherwise, an exception will be thrown. -The `autoIncrement` method already calls the `primary` method, so the `primary` method should not be called when +The `autoIncrement` method already calls the `primary` method, so the `primary` method should not be called when creating an auto incrementing column with the `autoIncrement` method. #### Altering tables @@ -237,8 +238,8 @@ To quickly run all migrations that have not ran up, simply run `php famine feast:migration:run-all` in your terminal. For more detailed or advanced usage, see [feast:migration](cli.md#feastmigration) in the CLI docs. -If you have cached your database info (see [feast:cache:dbinfo-generate](cli.md#feastcachedbinfo-generate)), then the cache -will automatically re-generate after migrations are ran. +If you have cached your database info (see [feast:cache:dbinfo-generate](cli.md#feastcachedbinfo-generate)), then the +cache will automatically re-generate after migrations are ran. [Back to Top](#working-with-databases) @@ -274,14 +275,15 @@ All bindings in the below methods are passed in as a prepared statement executio ##### Where The `where` method creates a where clause on the query. It takes in a statement (or the where clause) -and bindings as either a `\Feast\Date` argument, a scalar, or an array of bindings for multiple. Each call to -the `where` method will create a parenthesis wrapped group, allowing you to focus only on what you need for that piece -of the statement. +and bindings as either a `\Feast\Date` argument, or a scalar. Multiple bindings may be passed in as needed. In versions +of FEAST Framework prior to 2.0, multiple bindings are passed in as an array. Each call to the `where` method will create +a parenthesis wrapped group, allowing you to focus only on what you need for that piece of the statement. Example ```php -$query->where('test = ? or test_name = ?', ['feast','feast])->where('active' => 1); +$query->where('test = ? or test_name = ?', ['feast','feast'])->where('active' => 1); // In version 1.x +$query->where('test = ? or test_name = ?', 'feast','feast')->where('active' => 1); // In version 2.0.0 and above // This will result in the following where clause on the query. // where (test = ? or test_name = ?) and (active = ?) ``` From 8c24419930498e790d99f14d7e2743f05370c596 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 25 Nov 2021 13:59:54 -0500 Subject: [PATCH 39/56] Fix install file --- Install/Mapper/JobMapper.php.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Install/Mapper/JobMapper.php.txt b/Install/Mapper/JobMapper.php.txt index 3a62691..746d50d 100644 --- a/Install/Mapper/JobMapper.php.txt +++ b/Install/Mapper/JobMapper.php.txt @@ -58,7 +58,7 @@ class JobMapper extends BaseMapper */ public function markJobRunningIfAble(Job $job): bool { $query = $this->connection->update(self::TABLE_NAME,['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) - ->where('job_id = ? and status IN (?,?)',[$job->job_id,\Feast\Jobs\QueueableJob::JOB_STATUS_PENDING,\Feast\Jobs\QueueableJob::JOB_STATUS_FAILED]); + ->where('job_id = ? and status IN (?,?)',$job->job_id,\Feast\Jobs\QueueableJob::JOB_STATUS_PENDING,\Feast\Jobs\QueueableJob::JOB_STATUS_FAILED); $result = $query->execute(); return $result->rowCount() !== 0; } From 51fe508449261ad41e1ddfa3044735d7970ac6b0 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 25 Nov 2021 14:02:22 -0500 Subject: [PATCH 40/56] Reformat install file --- Install/Mapper/JobMapper.php.txt | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Install/Mapper/JobMapper.php.txt b/Install/Mapper/JobMapper.php.txt index 746d50d..3facf8a 100644 --- a/Install/Mapper/JobMapper.php.txt +++ b/Install/Mapper/JobMapper.php.txt @@ -39,7 +39,7 @@ class JobMapper extends BaseMapper public function findOnePendingByQueues(array $queues): ?Job { $query = $this->getQueryBase()->where('status = ?', 'pending')->where( - 'tries < max_tries and queue_name IN (' . str_repeat('?,', count($queues) - 1) . '?)', + 'tries < max_tries and queue_name IN (' . str_repeat('?,', count($queues) - 1) . '?)', ...$queues ); $return = $this->fetchOne($query); @@ -49,16 +49,22 @@ class JobMapper extends BaseMapper return null; } - /** - * Mark job as running. - * - * @param \Model\Job $job - * @return bool - * @throws \Exception - */ - public function markJobRunningIfAble(Job $job): bool { - $query = $this->connection->update(self::TABLE_NAME,['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) - ->where('job_id = ? and status IN (?,?)',$job->job_id,\Feast\Jobs\QueueableJob::JOB_STATUS_PENDING,\Feast\Jobs\QueueableJob::JOB_STATUS_FAILED); + /** + * Mark job as running. + * + * @param \Model\Job $job + * @return bool + * @throws \Exception + */ + public function markJobRunningIfAble(Job $job): bool + { + $query = $this->connection->update(self::TABLE_NAME, ['status' => \Feast\Jobs\QueueableJob::JOB_STATUS_RUNNING]) + ->where( + 'job_id = ? and status IN (?,?)', + $job->job_id, + \Feast\Jobs\QueueableJob::JOB_STATUS_PENDING, + \Feast\Jobs\QueueableJob::JOB_STATUS_FAILED + ); $result = $query->execute(); return $result->rowCount() !== 0; } From 729c13279b2db5d1c595e658ae8c4143f1cc6445 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 25 Nov 2021 14:08:33 -0500 Subject: [PATCH 41/56] Update badges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 81529c1..3f7bac6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ ![Psalm Static analysis](https://github.com/FeastFramework/framework/workflows/Psalm%20Static%20analysis/badge.svg?branch=v2.x) [![codecov](https://codecov.io/gh/FeastFramework/framework/branch/v2.x/graph/badge.svg?token=FBP2AKLJB3)](https://codecov.io/gh/FeastFramework/framework) -[![Packagist](https://img.shields.io/packagist/php-v/feast/framework/v2.x-dev)](https://packagist.org/packages/feast/framework) +![PHP Version](https://img.shields.io/packagist/php-v/feast/framework/v2.x-dev) +[![Packagist](https://img.shields.io/packagist/v/feast/framework)](https://packagist.org/packages/feast/framework) ![License](https://img.shields.io/packagist/l/feast/framework.svg) [![Docs](https://img.shields.io/badge/docs-quickstart-green.svg)](https://docs.feast-framework.com) ## Fast, Easy, Agile, Slim, Tested and Trans-Fat Free. From bf393586759183c6c3cfc771372499cfe298d0fb Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 26 Nov 2021 18:02:34 -0500 Subject: [PATCH 42/56] Bugfix - http request classes invalid conversion to string --- HttpRequest/Curl.php | 2 +- HttpRequest/Simple.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HttpRequest/Curl.php b/HttpRequest/Curl.php index 6c0f3a8..a168bcb 100644 --- a/HttpRequest/Curl.php +++ b/HttpRequest/Curl.php @@ -73,7 +73,7 @@ public function makeRequest(): HttpRequestInterface { $url = $this->url ?? ''; curl_setopt($this->curl, CURLOPT_URL, $url); - curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $this->method); + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $this->method->value); curl_setopt($this->curl, CURLOPT_USERAGENT, $this->userAgent); $header = []; $header[] = 'Accept-language: ' . $this->language; diff --git a/HttpRequest/Simple.php b/HttpRequest/Simple.php index 9408784..c8b282e 100644 --- a/HttpRequest/Simple.php +++ b/HttpRequest/Simple.php @@ -42,7 +42,7 @@ public function makeRequest(): Simple } $url = $this->url; $context = []; - $context['method'] = $this->method; + $context['method'] = $this->method->value; $context['user_agent'] = $this->userAgent; $context['request_fulluri'] = true; $header = 'Accept-language: ' . $this->language . "\r\n"; From 757ed4e998a79adc54549f797363f71441fa7b4d Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 26 Nov 2021 18:24:23 -0500 Subject: [PATCH 43/56] Bugfix for getResultAsJson (can be array or stdClass) --- HttpRequest/HttpRequest.php | 4 ++-- HttpRequest/Response.php | 4 ++-- Interfaces/HttpRequestInterface.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index 4585116..03aab96 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -477,9 +477,9 @@ public function getResponseAsString(): string /** * Get the request result as a json object. * - * @return stdClass|null + * @return array|stdClass|null */ - public function getResponseAsJson(): ?stdClass + public function getResponseAsJson(): null|array|stdClass { if ($this->response === null) { return null; diff --git a/HttpRequest/Response.php b/HttpRequest/Response.php index 7e31cea..56d0a9a 100644 --- a/HttpRequest/Response.php +++ b/HttpRequest/Response.php @@ -37,10 +37,10 @@ public function getResponseAsText(): string { return $this->rawResponse; } - public function getResultAsJson(): ?stdClass + public function getResultAsJson(): stdClass|array|null { try { - /** @var stdClass */ + /** @var stdClass|array */ return json_decode(utf8_encode($this->rawResponse), flags: JSON_THROW_ON_ERROR); } catch (Exception) { return null; diff --git a/Interfaces/HttpRequestInterface.php b/Interfaces/HttpRequestInterface.php index 414455a..6350aec 100644 --- a/Interfaces/HttpRequestInterface.php +++ b/Interfaces/HttpRequestInterface.php @@ -64,9 +64,9 @@ public function getResponseAsXml(): ?SimpleXMLElement; /** * Get the request result as a json object. * - * @return stdClass|null + * @return null|array|stdClass */ - public function getResponseAsJson(): ?stdClass; + public function getResponseAsJson(): null|array|stdClass; /** * Get the request result as a string. From 64e9ad2804c64b06cbb79c8e807b1f90edfd6c37 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 1 Dec 2021 21:55:10 -0500 Subject: [PATCH 44/56] Update psalm, remove unused psalm-suppress --- HttpRequest/HttpRequest.php | 4 ---- View.php | 4 ---- composer.lock | 8 ++++---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index 03aab96..81147f3 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -424,10 +424,6 @@ protected function parseResponseHeaders(array $responseHeaders): void } $status = explode(' ', $responseHeaders[0]); $responseCode = !empty($status[1]) ? (int)$status[1] : ResponseCode::HTTP_CODE_500->value; - /** - * @var ResponseCode - * @psalm-suppress UndefinedMethod - */ $this->responseCode = ResponseCode::tryFrom($responseCode) ?? ResponseCode::HTTP_CODE_500; foreach ($responseHeaders as $header) { diff --git a/View.php b/View.php index 3d36f5a..1bf2fdb 100644 --- a/View.php +++ b/View.php @@ -63,10 +63,6 @@ public function __construct(protected ConfigInterface $config, protected RouterI { $this->checkInjected(); $this->baseUrl = (string)$config->getSetting('siteurl'); - /** - * @var DocType $docType - * @psalm-suppress UndefinedMethod - */ $docType = DocType::tryFrom((string)$config->getSetting('html.doctype', DocType::HTML_5->value)) ?? DocType::HTML_5; $this->setDoctype($docType); } diff --git a/composer.lock b/composer.lock index a503903..eeab33f 100644 --- a/composer.lock +++ b/composer.lock @@ -13,12 +13,12 @@ "source": { "type": "git", "url": "https://github.com/psalm/phar.git", - "reference": "882ca87ee9b10237b0e952d6cb713bcead486d3b" + "reference": "f9d8fed0c36503ef37884d40b1f490803e2bcf05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/882ca87ee9b10237b0e952d6cb713bcead486d3b", - "reference": "882ca87ee9b10237b0e952d6cb713bcead486d3b", + "url": "https://api.github.com/repos/psalm/phar/zipball/f9d8fed0c36503ef37884d40b1f490803e2bcf05", + "reference": "f9d8fed0c36503ef37884d40b1f490803e2bcf05", "shasum": "" }, "require": { @@ -41,7 +41,7 @@ "issues": "https://github.com/psalm/phar/issues", "source": "https://github.com/psalm/phar/tree/master" }, - "time": "2021-11-24T22:46:14+00:00" + "time": "2021-12-01T22:46:28+00:00" } ], "aliases": [], From 20a36020debf27458afdb3f9e948b4559353f357 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 1 Dec 2021 22:03:04 -0500 Subject: [PATCH 45/56] Delete CNAME --- docs/CNAME | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/CNAME diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index ae42c55..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.feast-framework.com \ No newline at end of file From b9371876ef1358daa2736eb3a4ad62fbd3cca688 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 30 Jun 2022 12:48:15 -0400 Subject: [PATCH 46/56] Formatting fix --- Config/Config.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Config/Config.php b/Config/Config.php index 5222b0c..91adefd 100755 --- a/Config/Config.php +++ b/Config/Config.php @@ -338,8 +338,8 @@ private function cloneObjectOrArrayAsObject(stdClass|array $settings): stdClass * @var string $key * @var scalar|array|stdClass|BackedEnum $val */ - foreach($settings as $key => $val) { - if ( is_array($val) || $val instanceof stdClass ) { + foreach ($settings as $key => $val) { + if (is_array($val) || $val instanceof stdClass) { $return->$key = $this->cloneObjectOrArrayAsObject($val); } else { $return->$key = $val; @@ -357,8 +357,8 @@ private function objectToArray(stdClass|array $settings): array * @var string $key * @var scalar|array|stdClass|BackedEnum $val */ - foreach($settings as $key => $val) { - if ( is_array($val) || $val instanceof stdClass ) { + foreach ($settings as $key => $val) { + if (is_array($val) || $val instanceof stdClass) { $return[$key] = $this->objectToArray($val); } else { $return[$key] = $val; From aaeb4caab8ae73d03b2b08be4110697932440358 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Sun, 2 Oct 2022 19:49:58 -0400 Subject: [PATCH 47/56] Remove bad docblock --- Session/Session.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Session/Session.php b/Session/Session.php index 36175c7..7703a15 100755 --- a/Session/Session.php +++ b/Session/Session.php @@ -48,7 +48,6 @@ class Session implements ServiceContainerItemInterface * @param ConfigInterface $config * @throws ContainerException * @throws NotFoundException - * @throws ResponseException */ public function __construct(ConfigInterface $config) { From 3ae2f2729441045ff413d3d9c92a2395a7a9f9eb Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Tue, 29 Nov 2022 19:37:23 -0500 Subject: [PATCH 48/56] PHP 8.2 Updates --- .github/workflows/PhpUnit.yaml | 2 +- .github/workflows/Psalm.yaml | 2 +- Database/Database.php | 16 +++++++--------- Date.php | 2 +- FeastTests/DateTest.php | 2 +- FeastTests/IdentityTest.php | 5 ----- HttpRequest/HttpRequest.php | 7 +++++-- HttpRequest/Response.php | 18 +++++++++++------- README.md | 8 ++++---- composer.json | 2 +- composer.lock | 16 ++++++++-------- docs/index.md | 6 +++--- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/PhpUnit.yaml b/.github/workflows/PhpUnit.yaml index e364621..02e2dc4 100644 --- a/.github/workflows/PhpUnit.yaml +++ b/.github/workflows/PhpUnit.yaml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' extensions: bcmath coverage: xdebug tools: phpunit diff --git a/.github/workflows/Psalm.yaml b/.github/workflows/Psalm.yaml index e7de9f8..b45f209 100644 --- a/.github/workflows/Psalm.yaml +++ b/.github/workflows/Psalm.yaml @@ -10,7 +10,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' - name: Checkout project uses: actions/checkout@v2 diff --git a/Database/Database.php b/Database/Database.php index 23ac2ff..56c0f15 100644 --- a/Database/Database.php +++ b/Database/Database.php @@ -44,23 +44,21 @@ class Database implements DatabaseInterface private PDO $connection; private DatabaseType $databaseType; private string $queryClass; - private LoggerInterface $logger; private string $escapeCharacter; /** * @param stdClass $connectionDetails * @param string $pdoClass - * @param LoggerInterface|null $logger - * @throws DatabaseException + * @param LoggerInterface $logger * @throws InvalidOptionException * @throws ServerFailureException - * @throws \Feast\ServiceContainer\NotFoundException */ - public function __construct(stdClass $connectionDetails, string $pdoClass, ?LoggerInterface $logger) - { - $logger ??= di(LoggerInterface::INTERFACE_NAME); - $this->logger = $logger; - + public function __construct( + #[\SensitiveParameter] + stdClass $connectionDetails, + string $pdoClass, + private readonly LoggerInterface $logger + ) { $username = (string)$connectionDetails->user; $password = (string)$connectionDetails->pass; /** @var DatabaseType */ diff --git a/Date.php b/Date.php index 0b3d76d..23efd29 100755 --- a/Date.php +++ b/Date.php @@ -439,7 +439,7 @@ public function __toString(): string */ public function modify(string $amount): Date { - $modification = strtotime('+' . $amount, $this->timestamp); + $modification = strtotime($amount, $this->timestamp); if ($modification === false) { throw new InvalidDateException('Invalid date modification'); } diff --git a/FeastTests/DateTest.php b/FeastTests/DateTest.php index 55c7c66..7ec8db1 100755 --- a/FeastTests/DateTest.php +++ b/FeastTests/DateTest.php @@ -438,7 +438,7 @@ public function testBadDateModification(): void { $date = Date::createFromString('2018-01-31 00:00:00'); $this->expectException(InvalidDateException::class); - $date->modify('+1 blorgh'); + $date->modify('1 blorgh'); } public function testGetAsDateTime(): void diff --git a/FeastTests/IdentityTest.php b/FeastTests/IdentityTest.php index 11c242d..0f22c5e 100644 --- a/FeastTests/IdentityTest.php +++ b/FeastTests/IdentityTest.php @@ -63,15 +63,12 @@ public function testSaveUser(): void { di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER); $session = $this->createStub(Session::class); - $namespace = new stdClass(); - $session->Feast_Login = $namespace; $config = $this->createStub(ConfigInterface::class); $config->method('getSetting')->willReturn('test'); $testUser = new MockUser(); $testUser->user = 'testUser'; - $namespace->identity = $testUser; $identity = new Identity($config,$session); $user = $identity->getUser(); $this->assertEquals(null, $user); @@ -85,8 +82,6 @@ public function testDestroyUser(): void { di(null, \Feast\Enums\ServiceContainer::CLEAR_CONTAINER); $session = $this->createStub(Session::class); - $namespace = new stdClass(); - $session->Feast_Login = $namespace; $config = $this->createStub(ConfigInterface::class); $config->method('getSetting')->willReturn('test'); diff --git a/HttpRequest/HttpRequest.php b/HttpRequest/HttpRequest.php index 81147f3..a6400c6 100644 --- a/HttpRequest/HttpRequest.php +++ b/HttpRequest/HttpRequest.php @@ -329,8 +329,11 @@ public function getUserAgent(): string * @param string $password * @return HttpRequest */ - public function authenticate(string $username, string $password): HttpRequestInterface - { + public function authenticate( + string $username, + #[\SensitiveParameter] + string $password + ): HttpRequestInterface { $this->username = $username; $this->password = $password; diff --git a/HttpRequest/Response.php b/HttpRequest/Response.php index 56d0a9a..8a9438b 100644 --- a/HttpRequest/Response.php +++ b/HttpRequest/Response.php @@ -27,13 +27,13 @@ class Response { - + public function __construct(protected string $rawResponse, protected ResponseCode $responseCode) { - } - - public function getResponseAsText(): string { + + public function getResponseAsText(): string + { return $this->rawResponse; } @@ -41,13 +41,17 @@ public function getResultAsJson(): stdClass|array|null { try { /** @var stdClass|array */ - return json_decode(utf8_encode($this->rawResponse), flags: JSON_THROW_ON_ERROR); + return json_decode( + mb_convert_encoding($this->rawResponse, 'UTF-8', ['ISO-8859-1', 'UTF-8']), + flags: JSON_THROW_ON_ERROR + ); } catch (Exception) { return null; } } - - public function getResultAsXml(): ?SimpleXMLElement { + + public function getResultAsXml(): ?SimpleXMLElement + { try { return new SimpleXMLElement($this->rawResponse, LIBXML_NOERROR | LIBXML_NOWARNING); } catch (Exception) { diff --git a/README.md b/README.md index 3f7bac6..9385280 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ # FEAST Framework -![PHPUnit](https://github.com/FeastFramework/framework/workflows/PHPUnit/badge.svg?branch=v2.x) -![Psalm Static analysis](https://github.com/FeastFramework/framework/workflows/Psalm%20Static%20analysis/badge.svg?branch=v2.x) -[![codecov](https://codecov.io/gh/FeastFramework/framework/branch/v2.x/graph/badge.svg?token=FBP2AKLJB3)](https://codecov.io/gh/FeastFramework/framework) +![PHPUnit](https://github.com/FeastFramework/framework/workflows/PHPUnit/badge.svg?branch=v3.x) +![Psalm Static analysis](https://github.com/FeastFramework/framework/workflows/Psalm%20Static%20analysis/badge.svg?branch=v3.x) +[![codecov](https://codecov.io/gh/FeastFramework/framework/branch/v3.x/graph/badge.svg?token=FBP2AKLJB3)](https://codecov.io/gh/FeastFramework/framework) -![PHP Version](https://img.shields.io/packagist/php-v/feast/framework/v2.x-dev) +![PHP Version](https://img.shields.io/packagist/php-v/feast/framework/v3.x-dev) [![Packagist](https://img.shields.io/packagist/v/feast/framework)](https://packagist.org/packages/feast/framework) ![License](https://img.shields.io/packagist/l/feast/framework.svg) [![Docs](https://img.shields.io/badge/docs-quickstart-green.svg)](https://docs.feast-framework.com) diff --git a/composer.json b/composer.json index 26504b3..ee1686a 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": "^8.1.0", + "php": "^8.2.0", "ext-pdo": "*", "ext-xml": "*", "ext-readline": "*", diff --git a/composer.lock b/composer.lock index 91f5021..1c8d4ab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7851f377b0b126cd0715550dcad67826", + "content-hash": "3f37ec1ab718163295e8b43c2e02c774", "packages": [], "packages-dev": [ { "name": "psalm/phar", - "version": "4.28.0", + "version": "4.30.0", "source": { "type": "git", "url": "https://github.com/psalm/phar.git", - "reference": "2fb09be7df082d4d29c6356ea34866501d187f3e" + "reference": "33723713902e1345904a5c9064ef7848bee0d490" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/phar/zipball/2fb09be7df082d4d29c6356ea34866501d187f3e", - "reference": "2fb09be7df082d4d29c6356ea34866501d187f3e", + "url": "https://api.github.com/repos/psalm/phar/zipball/33723713902e1345904a5c9064ef7848bee0d490", + "reference": "33723713902e1345904a5c9064ef7848bee0d490", "shasum": "" }, "require": { @@ -38,9 +38,9 @@ "description": "Composer-based Psalm Phar", "support": { "issues": "https://github.com/psalm/phar/issues", - "source": "https://github.com/psalm/phar/tree/4.28.0" + "source": "https://github.com/psalm/phar/tree/4.30.0" }, - "time": "2022-10-07T16:22:13+00:00" + "time": "2022-11-06T20:41:58+00:00" } ], "aliases": [], @@ -49,7 +49,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1.0", + "php": "^8.2.0", "ext-pdo": "*", "ext-xml": "*", "ext-readline": "*", diff --git a/docs/index.md b/docs/index.md index dac6200..442c110 100644 --- a/docs/index.md +++ b/docs/index.md @@ -67,10 +67,10 @@ up with the following principles always in mind. ## Requirements -The current release of FEAST requires >=PHP 8.1 and (if you wish to use the Curl service classes) PHP 8.1-curl. In -addition, PHP 8.1-bcmath is recommended. +The current release of FEAST requires >=PHP 8.2 and (if you wish to use the Curl service classes) PHP 8.2-curl. In +addition, PHP 8.2-bcmath is recommended. -The v1.x line of FEAST requires >=PHP 8.0. The same recommendations above apply +The v1.x line of FEAST requires >=PHP 8.0 and v2.x line requires >=PHP 8.1. The same recommendations above apply FEAST currently works with the MySQL and PostgreSQL database management systems as well as with SQLite for simple queries. From 7954973719636523b0701e8d6e6bad7cfc672719 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 7 Dec 2022 20:00:12 -0500 Subject: [PATCH 49/56] Make Attribute classes readonly --- Attributes/AccessControl.php | 2 +- Attributes/Action.php | 2 +- Attributes/JsonItem.php | 2 +- Attributes/JsonParam.php | 2 +- Attributes/Param.php | 2 +- Attributes/Path.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Attributes/AccessControl.php b/Attributes/AccessControl.php index 7dbf094..8460c44 100644 --- a/Attributes/AccessControl.php +++ b/Attributes/AccessControl.php @@ -23,7 +23,7 @@ use Attribute; #[Attribute(Attribute::TARGET_METHOD)] -class AccessControl +readonly class AccessControl { /** * AccessControl constructor. diff --git a/Attributes/Action.php b/Attributes/Action.php index 13f1579..7964d45 100644 --- a/Attributes/Action.php +++ b/Attributes/Action.php @@ -23,7 +23,7 @@ use Attribute; #[Attribute(Attribute::TARGET_METHOD)] -class Action +readonly class Action { public function __construct(public string $usage = '', public string $description = '') { diff --git a/Attributes/JsonItem.php b/Attributes/JsonItem.php index 0e5b5bb..6300c99 100644 --- a/Attributes/JsonItem.php +++ b/Attributes/JsonItem.php @@ -24,7 +24,7 @@ use Feast\Date; #[Attribute(Attribute::TARGET_PROPERTY)] -class JsonItem +readonly class JsonItem { /** * JsonItem constructor. diff --git a/Attributes/JsonParam.php b/Attributes/JsonParam.php index 3675f99..bb12c8b 100644 --- a/Attributes/JsonParam.php +++ b/Attributes/JsonParam.php @@ -23,7 +23,7 @@ use Attribute; #[Attribute(Attribute::TARGET_PARAMETER)] -class JsonParam +readonly class JsonParam { public function __construct(public string $key = '') { diff --git a/Attributes/Param.php b/Attributes/Param.php index c9d098f..141fb1f 100644 --- a/Attributes/Param.php +++ b/Attributes/Param.php @@ -25,7 +25,7 @@ use Feast\Terminal; #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] -class Param +readonly class Param { public function __construct( public string $type = '', diff --git a/Attributes/Path.php b/Attributes/Path.php index e9a809a..5e8f624 100644 --- a/Attributes/Path.php +++ b/Attributes/Path.php @@ -24,7 +24,7 @@ use Feast\Enums\RequestMethod; #[Attribute(Attribute::TARGET_METHOD)] -class Path +readonly class Path { final public const METHOD_GET = 1; final public const METHOD_POST = 2; From d345a0329697df4441132b412c48bd3549993543 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Mon, 30 Jan 2023 17:11:22 -0500 Subject: [PATCH 50/56] Add getResponseCode to response object --- FeastTests/HttpRequest/CurlTest.php | 2 +- HttpRequest/Response.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FeastTests/HttpRequest/CurlTest.php b/FeastTests/HttpRequest/CurlTest.php index 30f2b67..404124d 100644 --- a/FeastTests/HttpRequest/CurlTest.php +++ b/FeastTests/HttpRequest/CurlTest.php @@ -205,7 +205,7 @@ public function testGetResponseCode(): void $request->get('https://www.google.com/json'); $request->makeRequest(); $response = $request->getResponse(); - $this->assertEquals(200, $response->getResponseCode()); + $this->assertEquals(ResponseCode::HTTP_CODE_200, $response->getResponseCode()); $this->assertNull( $request->getResponseAsXml() ); diff --git a/HttpRequest/Response.php b/HttpRequest/Response.php index 0a966c4..7edb450 100644 --- a/HttpRequest/Response.php +++ b/HttpRequest/Response.php @@ -58,7 +58,7 @@ public function getResultAsXml(): ?SimpleXMLElement } } - public function getResponseCode(): int + public function getResponseCode(): ResponseCode { return $this->responseCode; } From 7c70476b17f7c0a58784dcf88ed0efc820274a47 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 15 Feb 2023 18:33:17 -0800 Subject: [PATCH 51/56] Additional windows line ending test fixes --- FeastTests/Exception/ServerFailureExceptionTest.php | 6 +++--- FeastTests/HttpRequest/CurlTest.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/FeastTests/Exception/ServerFailureExceptionTest.php b/FeastTests/Exception/ServerFailureExceptionTest.php index deff9dd..fecd765 100644 --- a/FeastTests/Exception/ServerFailureExceptionTest.php +++ b/FeastTests/Exception/ServerFailureExceptionTest.php @@ -66,9 +66,9 @@ public function testPrintParentExceptionCliWithParent(): void $exception->printParentException(); $output = $this->getActualOutputForAssertion(); $this->assertStringContainsString( - 'Test -Thrown on line', - $output + str_replace("\r\n","\n",'Test +Thrown on line'), + str_replace("\r\n","\n",$output) ); } diff --git a/FeastTests/HttpRequest/CurlTest.php b/FeastTests/HttpRequest/CurlTest.php index f17b50e..ca01037 100644 --- a/FeastTests/HttpRequest/CurlTest.php +++ b/FeastTests/HttpRequest/CurlTest.php @@ -151,7 +151,7 @@ public function testMakeRequestAndGetResponseCode(): void $request->makeRequest(); $this->assertEquals(ResponseCode::HTTP_CODE_200, $request->getResponseCode()); $this->assertEquals( - ' + str_replace("\r\n","\n",' @@ -161,8 +161,8 @@ public function testMakeRequestAndGetResponseCode(): void Test -', - $request->getResponseAsString() +'), + str_replace("\r\n","\n",$request->getResponseAsString()) ); } From b19570c6181fa08cfa0dc87691da613ee7c8854b Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 15 Feb 2023 18:35:00 -0800 Subject: [PATCH 52/56] Un-version lock PHP Unit --- .github/workflows/PhpUnit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/PhpUnit.yaml b/.github/workflows/PhpUnit.yaml index a791283..e364621 100644 --- a/.github/workflows/PhpUnit.yaml +++ b/.github/workflows/PhpUnit.yaml @@ -13,7 +13,7 @@ jobs: php-version: '8.1' extensions: bcmath coverage: xdebug - tools: phpunit:9 + tools: phpunit - name: Run PHPUnit run: phpunit -c FeastTests/githubphpunit.xml --coverage-clover coverage.xml - uses: codecov/codecov-action@v1 From 81db8bd499e844e4b880b66eb499a951730629c7 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Wed, 15 Feb 2023 18:37:59 -0800 Subject: [PATCH 53/56] Update PHPUnit configs --- FeastTests/githubphpunit.xml | 47 ++++++++++++------------- phpunit.xml | 66 ++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 62 deletions(-) diff --git a/FeastTests/githubphpunit.xml b/FeastTests/githubphpunit.xml index 0bd5f39..f65e021 100644 --- a/FeastTests/githubphpunit.xml +++ b/FeastTests/githubphpunit.xml @@ -1,26 +1,21 @@ - - - - . - - - - - ../ - - - ../FeastTests - ../Install - ../PsalmLoader.php - - - - - - \ No newline at end of file + + + + + . + + + + + ../ + + + ../FeastTests + ../Install + ../PsalmLoader.php + + + + + + diff --git a/phpunit.xml b/phpunit.xml index eaa617d..adda20d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,36 +1,30 @@ - - - - FeastTests - - - - - . - Form.php - Email.php - Main.php - Attributes - Collection - Traits/Collection.php - Controllers - - - FeastTests - Install - PsalmLoader.php - ../Autoloader.php - - - - - - - - \ No newline at end of file + + + + + FeastTests + + + + + . + Form.php + Email.php + Main.php + Attributes + Collection + Traits/Collection.php + Controllers + + + FeastTests + Install + PsalmLoader.php + ../Autoloader.php + + + + + + + From 8221171edf9b18ac41c23574a4bed02e80473001 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Thu, 16 Feb 2023 23:14:08 -0500 Subject: [PATCH 54/56] Correct bad pathing for phpunit.xml --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index adda20d..6198ce2 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,5 @@ - + FeastTests From 63c08d1092f086bd84b6ca45f5d75aa5bb254dda Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 17 Feb 2023 10:11:49 -0500 Subject: [PATCH 55/56] Update PHP versions --- .github/workflows/PhpUnit-MacOs.yaml | 4 ++-- .github/workflows/PhpUnit-Win.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/PhpUnit-MacOs.yaml b/.github/workflows/PhpUnit-MacOs.yaml index aed82a1..70d25f0 100644 --- a/.github/workflows/PhpUnit-MacOs.yaml +++ b/.github/workflows/PhpUnit-MacOs.yaml @@ -10,10 +10,10 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' extensions: bcmath coverage: xdebug - tools: phpunit:9 + tools: phpunit - name: Run PHPUnit run: phpunit -c FeastTests/githubphpunit.xml --coverage-clover coverage.xml \ No newline at end of file diff --git a/.github/workflows/PhpUnit-Win.yaml b/.github/workflows/PhpUnit-Win.yaml index 1fd4101..873b2fc 100644 --- a/.github/workflows/PhpUnit-Win.yaml +++ b/.github/workflows/PhpUnit-Win.yaml @@ -10,10 +10,10 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' extensions: bcmath coverage: xdebug - tools: phpunit:9 + tools: phpunit - name: Run PHPUnit run: phpunit -c FeastTests/githubphpunit.xml --coverage-clover coverage.xml \ No newline at end of file From 49a636edf2bfcab8d585c6d804111b19cb6ecc02 Mon Sep 17 00:00:00 2001 From: Jeremy Presutti Date: Fri, 17 Feb 2023 10:14:48 -0500 Subject: [PATCH 56/56] Update PHP versions --- .github/workflows/PhpUnit-MacOs.yaml | 2 +- .github/workflows/PhpUnit-Win.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/PhpUnit-MacOs.yaml b/.github/workflows/PhpUnit-MacOs.yaml index 70d25f0..d15e217 100644 --- a/.github/workflows/PhpUnit-MacOs.yaml +++ b/.github/workflows/PhpUnit-MacOs.yaml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' extensions: bcmath coverage: xdebug tools: phpunit diff --git a/.github/workflows/PhpUnit-Win.yaml b/.github/workflows/PhpUnit-Win.yaml index 873b2fc..9adf0db 100644 --- a/.github/workflows/PhpUnit-Win.yaml +++ b/.github/workflows/PhpUnit-Win.yaml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' extensions: bcmath coverage: xdebug tools: phpunit