diff --git a/.editorconfig b/.editorconfig index 209e731..87b5178 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ ; This file is for unifying the coding style for different editors and IDEs. -; More information at http://editorconfig.org +; More information at https://editorconfig.org root = true @@ -19,5 +19,8 @@ indent_size = 2 [*.twig] insert_final_newline = false +[*.neon] +indent_style = tab + [Makefile] indent_style = tab diff --git a/.gitattributes b/.gitattributes index b89e116..2e7fdfe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,6 @@ # Define the line ending behavior of the different file extensions # Set default behavior, in case users don't have core.autocrlf set. -* text=auto -* text eol=lf +* text text=auto eol=lf # Explicitly declare text files we want to always be normalized and converted # to native line endings on checkout. @@ -33,6 +32,7 @@ *.jpeg binary *.gif binary *.webp binary +*.avif binary *.ico binary *.mo binary *.pdf binary @@ -41,6 +41,7 @@ *.phar binary *.woff binary *.woff2 binary +*.ttc binary *.ttf binary *.otf binary *.eot binary diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 1267b00..06aa901 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -6,22 +6,30 @@ on: - '**/*.php' - '.github/workflows/php.yml' - '**/composer.json' + - 'phpcs.xml' + - 'phpstan.neon.dist' + - 'phpunit.xml.dist' + - 'psalm.xml' push: paths: - '**/*.php' - '.github/workflows/php.yml' - '**/composer.json' + - 'phpcs.xml' + - 'phpstan.neon.dist' + - 'phpunit.xml.dist' + - 'psalm.xml' jobs: cs: uses: bedita/github-workflows/.github/workflows/php-cs.yml@v2 with: - php_versions: '["8.3"]' + php_versions: '["8.4"]' stan: uses: bedita/github-workflows/.github/workflows/php-stan.yml@v2 with: - php_versions: '["8.3"]' + php_versions: '["8.4"]' unit: name: 'Run unit tests' @@ -31,7 +39,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [8.3] + php-version: [8.3, 8.4] env: PHP_VERSION: '${{ matrix.php-version }}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e9b592..7f86570 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,4 +9,4 @@ jobs: uses: bedita/github-workflows/.github/workflows/release.yml@v2 with: main_branch: 'master' - dist_branches: '["master"]' + dist_branches: '["master", "2.x"]' diff --git a/.gitignore b/.gitignore index 68780d2..502bf97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# User specific & automatically generated files # -################################################# +# CakePHP specific files # +########################## /config/app_local.php /config/.env /logs/* @@ -29,7 +29,7 @@ Thumbs.db # Tool specific files # ####################### # PHPUnit -.phpunit.result.cache +.phpunit.cache tests.sqlite # vim *~ diff --git a/composer.json b/composer.json index 4587a0d..c7f5540 100644 --- a/composer.json +++ b/composer.json @@ -6,23 +6,28 @@ "license": "MIT", "require": { "php": ">=8.3", - "bedita/api": "^5.43", - "bedita/aws": "^3.0.5", - "bedita/core": "^5.43", - "cakephp/cakephp": "^4.5.0", - "cakephp/plugin-installer": "^1.3.1" + "bedita/api": "^6.0", + "bedita/aws": "^5.0", + "bedita/core": "^6.0", + "cakephp/cakephp": "~5.2.9", + "cakephp/plugin-installer": "^2.0" }, - "require-dev": { - "bedita/dev-tools": "^2", - "cakephp/bake": "^2.8", - "cakephp/cakephp-codesniffer": "~4.7.0", - "cakephp/debug_kit": "^4.9.3", - "cakephp/repl": "^0.1", - "dereuromark/cakephp-ide-helper": "^1.18", - "josegonzalez/dotenv": "^3.2", + "bedita/dev-tools": "^3.2", + "cakephp/bake": "^3.5.0", + "cakephp/cakephp-codesniffer": "^5.0", + "cakephp/debug_kit": "^5.3.0", + "cakephp/repl": "^2.0.1", + "dereuromark/cakephp-ide-helper": "^2.5.3", + "josegonzalez/dotenv": "^4.0", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.6" + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpunit/phpunit": "^11.5 || ^12.1" + }, + "minimum-stability": "dev", + "suggest": { + "markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification." }, "autoload": { "psr-4": { @@ -45,8 +50,8 @@ "@stan" ], "stan": "vendor/bin/phpstan analyse", - "cs-check": "vendor/bin/phpcs -n -p --extensions=php src/ tests/ config/*.php", - "cs-fix": "vendor/bin/phpcbf -p --extensions=php src/ tests/ config/*.php", + "cs-check": "vendor/bin/phpcs -n -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/ config/*.php", + "cs-fix": "vendor/bin/phpcbf -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/ config/*.php", "test": "vendor/bin/phpunit --colors=always", "migrate": [ "@cache-clear", @@ -67,8 +72,9 @@ "config": { "sort-packages": true, "allow-plugins": { + "cakephp/plugin-installer": true, "dealerdirect/phpcodesniffer-composer-installer": true, - "cakephp/plugin-installer": true + "phpstan/extension-installer": true } } } diff --git a/config/.env.example b/config/.env.example index e90937b..01bf3a4 100644 --- a/config/.env.example +++ b/config/.env.example @@ -23,7 +23,7 @@ export SECURITY_SALT="__SALT__" # Uncomment these to define cache configuration via environment variables. #export CACHE_DURATION="+2 minutes" #export CACHE_DEFAULT_URL="file:///path/to/tmp/cache/?prefix=${APP_NAME}_default_&duration=${CACHE_DURATION}" -#export CACHE_CAKECORE_URL="file:///path/to/tmp/cache/persistent?prefix=${APP_NAME}_cake_core_&serialize=true&duration=${CACHE_DURATION}" +#export CACHE_CAKECORE_URL="file:///path/to/tmp/cache/persistent?prefix=${APP_NAME}_cake_translations_&serialize=true&duration=${CACHE_DURATION}" #export CACHE_CAKEMODEL_URL="file:///path/to/tmp/cache/models?prefix=${APP_NAME}_cake_model_&serialize=true&duration=${CACHE_DURATION}" # Uncomment these to define email transport configuration via environment variables. diff --git a/config/app.php b/config/app.php index 8319925..b04a7c5 100644 --- a/config/app.php +++ b/config/app.php @@ -6,6 +6,7 @@ use Cake\Database\Connection; use Cake\Database\Driver\Mysql; use Cake\Log\Engine\FileLog; +use Cake\Mailer\Transport\MailTransport; return [ /** @@ -118,9 +119,9 @@ * Duration will be set to '+2 minutes' in bootstrap.php when debug = true * If you set 'className' => 'Null' core cache will be disabled. */ - '_cake_core_' => [ + '_cake_translations_' => [ 'className' => FileEngine::class, - 'prefix' => 'myapp_cake_core_', + 'prefix' => 'myapp_cake_translations_', 'path' => CACHE . 'persistent' . DS, 'serialize' => true, 'duration' => '+1 years', @@ -169,7 +170,7 @@ ], ], - /** + /* * Configure the Error and Exception handlers used by your application. * * By default errors are displayed using Debugger, when debug is true and logged @@ -183,11 +184,11 @@ * Options: * * - `errorLevel` - int - The level of errors you are interested in capturing. - * - `trace` - boolean - Whether or not backtraces should be included in + * - `trace` - boolean - Whether backtraces should be included in * logged errors/exceptions. - * - `log` - boolean - Whether or not you want exceptions logged. + * - `log` - boolean - Whether you want exceptions logged. * - `exceptionRenderer` - string - The class responsible for rendering uncaught exceptions. - * The chosen class will be used for for both CLI and web environments. If you want different + * The chosen class will be used for both CLI and web environments. If you want different * classes used in CLI and web environments you'll need to write that conditional logic as well. * The conventional location for custom renderers is in `src/Error`. Your exception renderer needs to * implement the `render()` method and return either a string or Http\Response. @@ -202,7 +203,7 @@ * - `extraFatalErrorMemory` - int - The number of megabytes to increase the memory limit by * when a fatal error is encountered. This allows * breathing room to complete logging or error handling. - * - `ignoredDeprecationPaths` - array - A list of glob compatible file paths that deprecations + * - `ignoredDeprecationPaths` - array - A list of glob-compatible file paths that deprecations * should be ignored in. Use this to ignore deprecations for plugins or parts of * your application that still emit deprecations. */ @@ -212,10 +213,10 @@ 'skipLog' => ['Cake\Network\Exception\NotFoundException', 'BEdita\API\Exception\ExpiredTokenException'], 'log' => true, 'trace' => true, - 'ignoredDeprecationPaths' => [], + 'ignoredDeprecationPaths' => ['vendor/cakephp/cakephp/src/Log/Engine/FileLog.php'], ], - /** + /* * Debugger configuration * * Define development error values for Cake\Error\Debugger @@ -231,7 +232,7 @@ 'editor' => 'vscode', ], - /** + /* * Email configuration. * * By defining transports separately from delivery profiles you can easily @@ -252,17 +253,32 @@ */ 'EmailTransport' => [ 'default' => [ + 'className' => MailTransport::class, + /* + * The keys host, port, timeout, username, password, client and tls + * are used in SMTP transports + */ + //'host' => 'localhost', + //'port' => 25, + //'timeout' => 30, + /* + * It is recommended to set these options through your environment or app_local.php + */ + //'username' => null, + //'password' => null, + //'client' => null, + //'tls' => false, 'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null), ], ], - /** + /* * Email delivery profiles * * Delivery profiles allow you to predefine various properties about email * messages from your application and give the settings a name. This saves * duplication across your application and makes maintenance and development - * easier. Each profile accepts a number of keys. See `Cake\Mailer\Email` + * easier. Each profile accepts a number of keys. See `Cake\Mailer\Mailer` * for more information. */ 'Email' => [ @@ -277,14 +293,14 @@ ], ], - /** + /* * Connection information used by the ORM to connect * to your application's datastores. * * ### Notes * - Drivers include Mysql Postgres Sqlite Sqlserver - * See vendor\cakephp\cakephp\src\Database\Driver for complete list - * - Do not use periods in database name - it may lead to error. + * See vendor\cakephp\cakephp\src\Database\Driver for the complete list + * - Do not use periods in database name - it may lead to errors. * See https://github.com/cakephp/cakephp/issues/6471 for details. * - 'encoding' is recommended to be set to full UTF-8 4-Byte support. * E.g set it to 'utf8mb4' in MariaDB and MySQL and 'utf8' for any @@ -298,8 +314,8 @@ * The values in app_local.php will override any values set here * and should be used for local and per-environment configurations. * - * Environment variable based configurations can be loaded here or - * in app_local.php depending on the applications needs. + * Environment variable-based configurations can be loaded here or + * in app_local.php depending on the application's needs. */ 'default' => [ 'className' => Connection::class, @@ -345,6 +361,16 @@ * The test connection is used during the test suite. */ 'test' => [ + // 'className' => Connection::class, + // 'driver' => Mysql::class, + // 'persistent' => false, + // 'timezone' => 'UTC', + // 'encoding' => 'utf8mb4', + // 'flags' => [], + // 'cacheMetadata' => true, + // 'quoteIdentifiers' => false, + // 'log' => false, + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], 'url' => env('DATABASE_TEST_URL', 'sqlite:///tmp/bedita5_test.sqlite'), ], ], @@ -379,7 +405,7 @@ ], ], - /** + /* * Session configuration. * * Contains an array of settings to use for session configuration. The @@ -392,18 +418,23 @@ * Avoid using `.` in cookie names, as PHP will drop sessions from cookies with `.` in the name. * - `cookiePath` - The url path for which session cookie is set. Maps to the * `session.cookie_path` php.ini config. Defaults to base path of app. - * - `timeout` - The time in minutes the session should be valid for. - * Pass 0 to disable checking timeout. - * Please note that php.ini's session.gc_maxlifetime must be equal to or greater - * than the largest Session['timeout'] in all served websites for it to have the - * desired effect. + * - `timeout` - The time in minutes a session can be 'idle'. If no request is received in + * this duration, the session will be expired and rotated. Pass 0 to disable idle timeout checks. * - `defaults` - The default configuration set to use as a basis for your session. * There are four built-in options: php, cake, cache, database. * - `handler` - Can be used to enable a custom session handler. Expects an * array with at least the `engine` key, being the name of the Session engine * class to use for managing the session. CakePHP bundles the `CacheSession` * and `DatabaseSession` engines. - * - `ini` - An associative array of additional ini values to set. + * - `ini` - An associative array of additional 'session.*` ini values to set. + * + * Within the `ini` key, you will likely want to define: + * + * - `session.cookie_lifetime` - The number of seconds that cookies are valid for. This + * should be longer than `Session.timeout`. + * - `session.gc_maxlifetime` - The number of seconds after which a session is considered 'garbage' + * that can be deleted by PHP's session cleanup behavior. This value should be greater than both + * `Sesssion.timeout` and `session.cookie_lifetime`. * * The built-in `defaults` options are: * @@ -412,7 +443,7 @@ * - 'database' - Uses CakePHP's database sessions. * - 'cache' - Use the Cache class to save sessions. * - * To define a custom session handler, save it at src/Network/Session/.php. + * To define a custom session handler, save it at src/Http/Session/.php. * Make sure the class implements PHP's `SessionHandlerInterface` and set * Session.handler to * @@ -422,6 +453,48 @@ 'defaults' => 'php', ], + /** + * DebugKit configuration. + * + * Contains an array of configurations to apply to the DebugKit plugin, if loaded. + * Documentation: https://book.cakephp.org/debugkit/5/en/index.html#configuration + * + * ## Options + * + * - `panels` - Enable or disable panels. The key is the panel name, and the value is true to enable, + * or false to disable. + * - `includeSchemaReflection` - Set to true to enable logging of schema reflection queries. Disabled by default. + * - `safeTld` - Set an array of whitelisted TLDs for local development. + * - `forceEnable` - Force DebugKit to display. Careful with this, it is usually safer to simply whitelist + * your local TLDs. + * - `ignorePathsPattern` - Regex pattern (including delimiter) to ignore paths. + * DebugKit won’t save data for request URLs that match this regex. + * - `ignoreAuthorization` - Set to true to ignore Cake Authorization plugin for DebugKit requests. + * Disabled by default. + * - `maxDepth` - Defines how many levels of nested data should be shown in general for debug output. + * Default is 5. WARNING: Increasing the max depth level can lead to an out of memory error. + * - `variablesPanelMaxDepth` - Defines how many levels of nested data should be shown in the variables tab. + * Default is 5. WARNING: Increasing the max depth level can lead to an out of memory error. + */ + 'DebugKit' => [ + 'forceEnable' => filter_var(env('DEBUG_KIT_FORCE_ENABLE', false), FILTER_VALIDATE_BOOLEAN), + 'safeTld' => env('DEBUG_KIT_SAFE_TLD', null), + 'ignoreAuthorization' => env('DEBUG_KIT_IGNORE_AUTHORIZATION', false), + ], + + /** + * TestSuite configuration. + * + * ## Options + * + * - `errorLevel` - Defaults to `E_ALL`. Can be set to `false` to disable overwrite error level. + * - `fixtureStrategy` - Defaults to TruncateStrategy. Can be set to any class implementing FixtureStrategyInterface. + */ + 'TestSuite' => [ + 'errorLevel' => null, + 'fixtureStrategy' => null, + ], + /** * Filesystem configuration. * @@ -432,11 +505,13 @@ 'default' => [ 'className' => 'BEdita/Core.Local', 'path' => WWW_ROOT . '_files', + 'baseUrl' => env('FILESYSTEM_BASE_DEFAULT_URL', null), 'url' => env('FILESYSTEM_DEFAULT_URL', null), ], 'thumbnails' => [ 'className' => 'BEdita/Core.Local', 'path' => WWW_ROOT . '_files' . DS . 'thumbs', + 'baseUrl' => env('FILESYSTEM_BASE_THUMBNAILS_URL', null), 'url' => env('FILESYSTEM_THUMBNAILS_URL', null), ], ], diff --git a/config/app_local.example.php b/config/app_local.example.php index c800880..bd59b41 100644 --- a/config/app_local.example.php +++ b/config/app_local.example.php @@ -1,4 +1,7 @@ getMessage() . "\n"); } @@ -89,19 +96,18 @@ } /* - * When debug = true the metadata cache should only last - * for a short time. + * When debug = true the metadata cache should only last for a short time. */ if (Configure::read('debug')) { Configure::write('Cache._bedita_core_.duration', '+2 minutes'); Configure::write('Cache._bedita_object_types_.duration', '+2 minutes'); - Configure::write('Cache._cake_model_.duration', '+2 minutes'); - Configure::write('Cache._cake_core_.duration', '+2 minutes'); + Configure::write('Cache._cake_model_.duration', '+1 minute'); + Configure::write('Cache._cake_translations_.duration', '+1 minute'); } /* * Set the default server timezone. Using UTC makes time calculations / conversions easier. - * Check http://php.net/manual/en/timezones.php for list of valid timezone strings. + * Check https://php.net/manual/en/timezones.php for list of valid timezone strings. */ date_default_timezone_set(Configure::read('App.defaultTimezone')); @@ -123,15 +129,26 @@ (new ExceptionTrap(Configure::read('Error')))->register(); /* - * Include the CLI bootstrap overrides. + * CLI/Command specific configuration. */ if (PHP_SAPI === 'cli') { - require CONFIG . 'bootstrap_cli.php'; + // Set the fullBaseUrl to allow URLs to be generated in commands. + // This is useful when sending email from commands. + // Configure::write('App.fullBaseUrl', php_uname('n')); + + // Set logs to different files so they don't have permission conflicts. + if (Configure::check('Log.debug')) { + Configure::write('Log.debug.file', 'cli-debug'); + } + if (Configure::check('Log.error')) { + Configure::write('Log.error.file', 'cli-error'); + } } /* * Set the full base URL. * This URL is used as the base of all absolute links. + * Can be very useful for CLI/Commandline applications. */ $fullBaseUrl = Configure::read('App.fullBaseUrl'); if (!$fullBaseUrl) { @@ -141,7 +158,7 @@ * you can enable `$trustProxy` to rely on the `X-Forwarded-Proto` * header to determine whether to generate URLs using `https`. * - * See also https://book.cakephp.org/4/en/controllers/request-response.html#trusting-proxy-headers + * See also https://book.cakephp.org/5/en/controllers/request-response.html#trusting-proxy-headers */ $trustProxy = false; @@ -151,7 +168,7 @@ } $httpHost = env('HTTP_HOST'); - if (isset($httpHost)) { + if ($httpHost) { $fullBaseUrl = 'http' . $s . '://' . $httpHost; } unset($httpHost, $s); @@ -161,9 +178,59 @@ } unset($fullBaseUrl); -Cache::setConfig(Configure::consume('Cache') ?: []); -ConnectionManager::setConfig(Configure::consume('Datasources') ?: []); -TransportFactory::setConfig(Configure::consume('EmailTransport') ?: []); -Mailer::setConfig(Configure::consume('Email') ?: []); -Log::setConfig(Configure::consume('Log') ?: []); -Security::setSalt((string)Configure::consume('Security.salt')); +/* + * Apply the loaded configuration settings to their respective systems. + * This will also remove the loaded config data from memory. + */ +Cache::setConfig(Configure::consume('Cache')); +ConnectionManager::setConfig(Configure::consume('Datasources')); +TransportFactory::setConfig(Configure::consume('EmailTransport')); +Mailer::setConfig(Configure::consume('Email')); +Log::setConfig(Configure::consume('Log')); +Security::setSalt(Configure::consume('Security.salt')); + +/* + * Setup detectors for mobile and tablet. + * If you don't use these checks you can safely remove this code + * and the mobiledetect package from composer.json. + */ +// ServerRequest::addDetector('mobile', function ($request) { +// $detector = new \Detection\MobileDetect(); + +// return $detector->isMobile(); +// }); +// ServerRequest::addDetector('tablet', function ($request) { +// $detector = new \Detection\MobileDetect(); + +// return $detector->isTablet(); +// }); + +/* + * You can enable default locale format parsing by adding calls + * to `useLocaleParser()`. This enables the automatic conversion of + * locale specific date formats when processing request data. For details see + * @link https://book.cakephp.org/5/en/core-libraries/internationalization-and-localization.html#parsing-localized-datetime-data + */ +// \Cake\Database\TypeFactory::build('time')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('date')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('datetime')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('timestamp')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('datetimefractional')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('timestampfractional')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('datetimetimezone')->useLocaleParser(); +// \Cake\Database\TypeFactory::build('timestamptimezone')->useLocaleParser(); + +/* + * Custom Inflector rules, can be set to correctly pluralize or singularize + * table, model, controller names or whatever other string is passed to the + * inflection functions. + */ +// \Cake\Utility\Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']); +// \Cake\Utility\Inflector::rules('irregular', ['red' => 'redlings']); +// \Cake\Utility\Inflector::rules('uninflected', ['dontinflectme']); + +// set a custom date and time format +// see https://book.cakephp.org/5/en/core-libraries/time.html#setting-the-default-locale-and-format-string +// and https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax +// \Cake\I18n\Date::setToStringFormat('dd.MM.yyyy'); +// \Cake\I18n\Time::setToStringFormat('dd.MM.yyyy HH:mm'); diff --git a/config/bootstrap_cli.php b/config/bootstrap_cli.php deleted file mode 100644 index fc0dc30..0000000 --- a/config/bootstrap_cli.php +++ /dev/null @@ -1,35 +0,0 @@ - + + */src/Controller/* + diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 40cb809..cb0d17d 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,4 +1,5 @@ parameters: + level: 9 bootstrapFiles: - tests/bootstrap.php paths: @@ -6,4 +7,3 @@ parameters: - tests excludePaths: - src/Console/Installer.php - level: 1 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index bc417c5..affe678 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,30 +1,39 @@ - - - - ./src/ - - - src/Console/Installer.php - - - - - - - - - - tests/TestCase/ - - - - - - - - - - - + + + + + + + + + + ./tests/TestCase/ + + + + + + + + + + + + + src/ + plugins/*/src/ + + + src/Console/Installer.php + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..4e8b692 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/src/Application.php b/src/Application.php index c07afa9..a90e3ef 100644 --- a/src/Application.php +++ b/src/Application.php @@ -39,6 +39,11 @@ public function bootstrap(): void // Load other plugins via `Plugin` configuration key $this->addConfigPlugins(); + + // if (PHP_SAPI !== 'cli') { + // // The bake plugin requires fallback table classes to work properly + // FactoryLocator::add('Table', (new TableLocator())->allowFallbackClass(false)); + // } } /** @@ -49,6 +54,8 @@ protected function bootstrapCli(): void parent::bootstrapCli(); if (Configure::read('debug')) { $this->addOptionalPlugin('Cake/Repl'); + $this->addOptionalPlugin('DebugKit'); } + $this->addOptionalPlugin('IdeHelper'); } } diff --git a/src/Console/Installer.php b/src/Console/Installer.php index 838882b..229d86a 100644 --- a/src/Console/Installer.php +++ b/src/Console/Installer.php @@ -22,6 +22,7 @@ use Cake\Codeception\Console\Installer as CodeceptionInstaller; use Cake\Utility\Security; +use Composer\IO\IOInterface; use Composer\Script\Event; use Exception; @@ -52,7 +53,7 @@ class Installer * @throws \Exception Exception raised by validator. * @return void */ - public static function postInstall(Event $event) + public static function postInstall(Event $event): void { $io = $event->getIO(); @@ -76,7 +77,7 @@ public static function postInstall(Event $event) * @param \Composer\IO\IOInterface $io IO interface to write to console. * @return void */ - public static function createAppLocalConfig($dir, $io) + public static function createAppLocalConfig(string $dir, IOInterface $io): void { $appLocalConfig = $dir . '/config/app_local.php'; $appLocalConfigTemplate = $dir . '/config/app_local.example.php'; @@ -93,7 +94,7 @@ public static function createAppLocalConfig($dir, $io) * @param \Composer\IO\IOInterface $io IO interface to write to console. * @return void */ - public static function createWritableDirectories($dir, $io) + public static function createWritableDirectories(string $dir, IOInterface $io): void { foreach (static::WRITABLE_DIRS as $path) { $path = $dir . '/' . $path; @@ -113,7 +114,7 @@ public static function createWritableDirectories($dir, $io) * @param \Composer\IO\IOInterface $io IO interface to write to console. * @return void */ - public static function setFolderPermissions($dir, $io) + public static function setFolderPermissions(string $dir, IOInterface $io): void { // ask if the permissions should be changed if ($io->isInteractive()) { @@ -127,7 +128,7 @@ public static function setFolderPermissions($dir, $io) 'Set Folder Permissions ? (Default to Y) [Y,n]? ', $validator, 10, - 'Y' + 'Y', ); if (in_array($setFolderPermissions, ['n', 'N'])) { @@ -136,7 +137,7 @@ public static function setFolderPermissions($dir, $io) } // Change the permissions on a path and output the results. - $changePerms = function ($path) use ($io) { + $changePerms = function ($path) use ($io): void { $currentPerms = fileperms($path) & 0777; $worldWritable = $currentPerms | 0007; if ($worldWritable == $currentPerms) { @@ -151,7 +152,7 @@ public static function setFolderPermissions($dir, $io) } }; - $walker = function ($dir) use (&$walker, $changePerms) { + $walker = function ($dir) use (&$walker, $changePerms): void { $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; @@ -177,7 +178,7 @@ public static function setFolderPermissions($dir, $io) * @param \Composer\IO\IOInterface $io IO interface to write to console. * @return void */ - public static function setSecuritySalt($dir, $io) + public static function setSecuritySalt(string $dir, IOInterface $io): void { $newKey = hash('sha256', Security::randomBytes(64)); static::setSecuritySaltInFile($dir, $io, $newKey, 'app_local.php'); @@ -192,7 +193,7 @@ public static function setSecuritySalt($dir, $io) * @param string $file A path to a file relative to the application's root * @return void */ - public static function setSecuritySaltInFile($dir, $io, $newKey, $file) + public static function setSecuritySaltInFile(string $dir, IOInterface $io, string $newKey, string $file): void { $config = $dir . '/config/' . $file; $content = file_get_contents($config); @@ -223,7 +224,7 @@ public static function setSecuritySaltInFile($dir, $io, $newKey, $file) * @param string $file A path to a file relative to the application's root * @return void */ - public static function setAppNameInFile($dir, $io, $appName, $file) + public static function setAppNameInFile(string $dir, IOInterface $io, string $appName, string $file): void { $config = $dir . '/config/' . $file; $content = file_get_contents($config); diff --git a/tests/TestCase/ApplicationTest.php b/tests/TestCase/ApplicationTest.php index fa9b22f..3c00efd 100644 --- a/tests/TestCase/ApplicationTest.php +++ b/tests/TestCase/ApplicationTest.php @@ -14,48 +14,85 @@ * @since 3.3.0 * @license https://opensource.org/licenses/mit-license.php MIT License */ + namespace MyApp\Test\TestCase; +use Authentication\Middleware\AuthenticationMiddleware; +use Authorization\Middleware\AuthorizationMiddleware; +use BEdita\API\Middleware\ApplicationMiddleware; +use BEdita\API\Middleware\BodyParserMiddleware; +use BEdita\API\Middleware\LoggedUserMiddleware; use Cake\Core\Configure; +use Cake\Error\Middleware\ErrorHandlerMiddleware; +use Cake\Http\MiddlewareQueue; +use Cake\Routing\Middleware\RoutingMiddleware; +use Cake\TestSuite\IntegrationTestTrait; use Cake\TestSuite\TestCase; use MyApp\Application; /** - * {@see MyApp\Application} Test Case - * - * @coversDefaultClass \MyApp\Application + * ApplicationTest class */ class ApplicationTest extends TestCase { + use IntegrationTestTrait; + /** - * Test `bootstrap` method + * Test bootstrap in production. * * @return void - * @covers ::bootstrap() */ public function testBootstrap() { - Configure::write('Plugins', []); - $app = new Application(CONFIG); + Configure::write('debug', false); + $app = new Application(dirname(__DIR__, 2) . '/config'); $app->bootstrap(); - static::assertTrue($app->getPlugins()->has('BEdita/Core')); - static::assertTrue($app->getPlugins()->has('BEdita/API')); - static::assertTrue($app->getPlugins()->has('Migrations')); + $plugins = $app->getPlugins(); + + $this->assertTrue($plugins->has('Bake'), 'plugins has Bake?'); + $this->assertFalse($plugins->has('DebugKit'), 'plugins has DebugKit?'); + $this->assertTrue($plugins->has('Migrations'), 'plugins has Migrations?'); } /** - * Test `bootstrapCli` method + * Test bootstrap add DebugKit plugin in debug mode. * * @return void - * @covers ::bootstrapCli() */ - public function testBootstrapCli() + public function testBootstrapInDebug() { - $currDebug = Configure::read('debug'); Configure::write('debug', true); - $app = new Application(CONFIG); + $app = new Application(dirname(__DIR__, 2) . '/config'); $app->bootstrap(); - static::assertTrue($app->getPlugins()->has('Cake/Repl')); - Configure::write('debug', $currDebug); + $plugins = $app->getPlugins(); + + $this->assertTrue($plugins->has('DebugKit'), 'plugins has DebugKit?'); + } + + /** + * testMiddleware + * + * @return void + */ + public function testMiddleware() + { + $app = new Application(dirname(__DIR__, 2) . '/config'); + $middleware = new MiddlewareQueue(); + + $middleware = $app->middleware($middleware); + + $this->assertInstanceOf(ErrorHandlerMiddleware::class, $middleware->current()); + $middleware->seek(1); + $this->assertInstanceOf(RoutingMiddleware::class, $middleware->current()); + $middleware->seek(2); + $this->assertInstanceOf(BodyParserMiddleware::class, $middleware->current()); + $middleware->seek(3); + $this->assertInstanceOf(AuthenticationMiddleware::class, $middleware->current()); + $middleware->seek(4); + $this->assertInstanceOf(ApplicationMiddleware::class, $middleware->current()); + $middleware->seek(5); + $this->assertInstanceOf(LoggedUserMiddleware::class, $middleware->current()); + $middleware->seek(6); + $this->assertInstanceOf(AuthorizationMiddleware::class, $middleware->current()); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 6826d58..b2fbaa4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -15,8 +15,10 @@ * @license https://opensource.org/licenses/mit-license.php MIT License */ +use Cake\Chronos\Chronos; use Cake\Core\Configure; use Cake\Datasource\ConnectionManager; +use Cake\TestSuite\ConnectionHelper; /** * Test runner bootstrap. @@ -46,11 +48,18 @@ ConnectionManager::alias('test_debug_kit', 'debug_kit'); +// Fixate now to avoid one-second-leap-issues +Chronos::setTestNow(Chronos::now()); + // Fixate sessionid early on, as php7.2+ // does not allow the sessionid to be set after stdout // has been written to. session_id('cli'); +// Connection aliasing needs to happen before migrations are run. +// Otherwise, table objects inside migrations would use the default datasource +ConnectionHelper::addTestAliases(); + // Use migrations to build test database schema. // // Will rebuild the database if the migration state differs @@ -61,4 +70,5 @@ // load schema from a SQL dump file with // use Cake\TestSuite\Fixture\SchemaLoader; // (new SchemaLoader())->loadSqlFiles('./tests/schema.sql', 'test'); + // (new Migrator())->run();