SuperAPI AI Directory

basercms-plugin-5x-update

Coding & development

View repository

Updated 10-15 17:24

About this skill

baserCMS Plugin Version Migration Guide (5.2 → 5.3)

A collection of baserCMS-specific issues and fix recipes encountered when migrating baserCMS plugins from the 5.2 series to the 5.3 series.By versionorganize and grow it.

Recommended: Before starting, refer once to basercms5-claude-workflow-setup(environment setup) to prepare the working environment (design = superpowers brainstorming / permission organization = permissions-audit / Auto mode on top of that / Markdown preview of spec and plan). This is a suggestion, and if it is already in place, skip it.

Covered: 5.2 → 5.3. The 5.3 series is based on PHP 8.5 / CakePHP 5.2.x (in development at the time of writing). Role division:

  • PHP itself-caused (implicit nullable, dynamic properties, non-canonical casts, setAccessible, null offsets, etc.) → php-migration Skill
  • CakePHP itself / related packages-caused (AbstractMigration→BaseMigration, event return values, PluginCollection, TreeBehavior, ResultSet, order()/group(), etc.) → cakephp-migration Skill
  • Running, aggregating, and isolating unit testsProcedure → basercms-unittest Skill
  • This document focuses only on baserCMS-specific + plugin test infrastructure that does not fall under the above.

General Principles

  • Distinguish between "fatal errors" and "deprecation warnings"。logs/debug.log の debug: や logs/error.log の error: で …は非推奨です Items marked with are warnings (operation continues). Fatal / Exception are the real errors.
  • Classify massive test failures by root cause. Even with hundreds of failures, there are only a dozen or so causes. Aggregate exception messages and eliminate systemic causes first (mixed vendor, plugins not loaded, invalid fixtures).
  • For dependency updates, use composer require , andcomposer update do not run the entire dependency set (cakephp-migration same as the general principle).

Changes from 5.2 → 5.3

0. Update dependency versions (composer.json)

Plugin's require-dev(or require), raise baser-core to the 5.3 series.

"require-dev": {
    "baserproject/baser-core": "5.3.x"
}
  • After raising it, with composer update baserproject/baser-core --with-dependencies(if the plugin has its own vendor), pull in the 5.3 series + CakePHP 5.2.x.
  • If maintaining PHP 8.1 compatibility, keep the PHP requirement as "php": ">=8.1" (for PHP 8.5 deprecation handling, follow the ❌ mark = leave-as-is policy in php-migration ).

Test Infrastructure (standalone plugin test harness)

Running a "standalone execution" plugin that has its own vendor/ と tests/TestApp/ on 5.3 is where things get stuck the most. For core plugins (tested via the app in the parent directory), the app-side tests/bootstrap.php performs equivalent processing, so this section refers to the app side.

T-1. Due to CakePHP double loading (mixed vendor), validateUnique(): Argument #2 ($options) not passed

composer test が root's phpunit(../../vendor/bin/phpunit) is started, while the plugin's tests/bootstrap.php が ROOT/vendor/autoload.phpWhen loading (= plugin's own vendor),two different CakePHP versions are loaded simultaneously. Composer's autoloader is register(prepend=true) , and since the last one wins, classes loaded at startup (e.g., Cake\ORM\Table 5.0 series) and classes loaded for the first time at runtime (e.g., Cake\Validation\Validator 5.2 series) coexist, causingvalidateUnique() signature mismatch ArgumentCountError errors in etc.

  • Detection: in the stack trace, …/vendor/cakephp/…(root) and …/plugins/<plugin>/vendor/cakephp/…(own) bothappear.
  • Fix (standalone): the test isRun with the plugin's own vendor/bin/phpunit . The ROOT/vendor read by bootstrap and the autoload of the phpunit bin use the same vendor, so CakePHP is unified to a single version.
    // plugins/<plugin>/composer.json
    "scripts": { "test": ["Composer\\Config::disableProcessTimeout", "vendor/bin/phpunit --colors=always"] }
    
    Execution (via Docker, see basercms-unittest): docker compose exec <container> sh -c "cd /var/www/html/plugins/<plugin> && vendor/bin/phpunit"

T-2. plugins.php の null → [](CakePHP 5.2's PluginCollection::create requires array)

tests/TestApp/config/plugins.php The value of null , thenBaseApplication::bootstrap() → PluginCollection::addFromConfig() → create($name, null) で TypeError: …create(): Argument #2 ($config) must be of type array, null given (allowed up to 5.1).

// Before → After
return ['BaserCore' => null, ...];  →  return ['BaserCore' => []];
  • Related: cakephp-migration Also described in 1-4.

T-3. Plugin loading uses BcApp.testAppPluginsToLoad (plugins.php is BaserCore only)

baser-core 5.2+ の BcTestCase::loadTestAppPlugins() は Configure::read('BcApp.testAppPluginsToLoad') Readuntil there are no unbootstrapped plugins, per-snapshot(with a guard) bootstrap each plugin.

  • In plugins.php, BaserCore onlyand list content-related plugins (BcBlog / BcCustomContent / BcSearchIndex, etc.) andthe target plugin itselfは testAppPluginsToLoad inconfig/plugins.php is BaserCore + infrastructure only,tests/bootstrap.php で Plugin::loaded() generate testAppPluginsToLoad from) uses the same method.
    // plugins/<plugin>/tests/bootstrap.php(Migrator の後など)
    Configure::write('BcApp.testAppPluginsToLoad', [
        'BcMcp',            // 対象プラグイン自身
        'BcSearchIndex', 'BcBlog', 'BcCustomContent', // 依存コンテンツプラグイン
    ]);
    

T-4. In integration tests, Plugin named 'X' is already loaded(double-load exception)

Always protect together with T-3. The plugin is both plugins.php and testAppPluginsToLoadIf written in , the per-request app that the integration test generates for each request plugins.php again addFromConfig and re-adds already loaded plugins, resulting in CakeException: Plugin named 'X' is already loaded(CakePHP 5.2、cakephp-migration 2-7). 500 → further masked by T-6 described later and appears as an "unexplained 500".

  • Fix: BaserCore is plugins.php, others are in testAppPluginsToLoadSeparateand do not duplicate.

T-5. Classes of bundled sub-plugins are not found(e.g., Class "BcCcFile\Utility\BcCcFileUtil" not found)

BcCcFile は sub-plugins bundled in bc-custom-content(vendor/baserproject/bc-custom-content/plugins/BcCcFile/)で、BcCustomContentPlugin::bootstrap() → loadPlugin() が App.paths.plugins add BcUtil::includePluginClass() でdynamically register PSR-4. Since it does not appear in composer's autoload, classes cannot be resolved unless the parent plugin is bootstrapped.

  • Fix: Parent plugin (BcCustomContent)を T-3 の testAppPluginsToLoad put in and bootstrap. There is no need to manually register the sub-plugin's namespace in composer.json.

T-6. The true cause of the test is MissingTemplateException: Error/error500.php masked by

The standalone test harness has templates/Error/error500.php because there is no , the 500 that occurred in the integration test turns into "error page rendering failure" andthe real exception is not visible(Regardless of whether debug is enabled, baserCMS attempts to render an error template.)

  • Investigation method (temporary measure): plugins/<plugin>/templates/Error/error500.php temporarily, and capture the $error(Throwable) passed to the template and write it to a file → run the test → confirm the root cause → delete the template.
    <?php // TEMP: 調査用。確認後に削除
    $e = $error ?? null;
    if ($e) { file_put_contents('/tmp/real_error.txt', get_class($e).': '.$e->getMessage()."\n".$e->getTraceAsString()); }
    echo 'ERR500';
    
  • Note that what the error renderer looks for is not Plugin's templates/Error/(tests/TestApp/templates/ ).

baserCMS-specific deprecations

B-1. BcFile 's constructor/delete() path specification

logs/error.log the following appears (required as of 6.0.0= currently a warning).

BcFile では、コンストラクタでパスの指定をしないのは非推奨です。パスの指定行うようにしてください。この要件はバージョン 6.0.0 で必須となります。
BcFile::delete() では、第一引数にパスを指定するのは非推奨です。パスの指定はコンストラクタで行ってください。この要件はバージョン 6.0.0 で必須となります。
  • Fix: pass the path in the constructor, and makedelete() take no arguments.
    // Before → After
    $file = new BcFile();           $file = new BcFile($path);
    $file->delete($path);           $file->delete();
    
  • Only apply to places in your own plugin's code that use BcFile (occurrences inside vendor are waiting on a fix from the baser-core side, and can be ignored for plugin migration).

TreeBehavior + Content fixture

C-1. Cannot set a node's parent as itself.(Content save test)

CakePHP 5.2's TreeBehavior throws an exception when saving "self as parent" (cakephp-migration the same type of strictness as 2-11). Typical cases that tend to surface in baserCMS test scenarios:

  • BlogContentScenario etc. give defaults via $parentId = $args[2] ?? 1 like ?? 1 , so even if the test passes null , it becomes invalid data with parent_id=1 becomes.id=1 content, then parent_id == id (self-parent) .
  • Harmless in read tests, but it only throws an exception in tests that save Content (editXxx updating name/title/site_id etc., causing Content-related records to be saved).
  • Fix (test side): after loading the scenario, correct the Content to a valid root and rebuild the tree.
    $contentsTable = TableRegistry::getTableLocator()->get('BaserCore.Contents');
    $contentsTable->updateAll(['parent_id' => null], ['id' => 1]); // behavior を介さず is-a-root に
    $contentsTable->recover();                                     // lft/rght 再構築
    
  • Also check whether the plugin's main code is sending parent_id to the root (cakephp-migration 2-11). If it is not sending it, no application fix is needed, and the fixture correction above is sufficient.

Date/time-dependent tests

D-1. Hardcoding year/month in upload paths, etc.

The file upload destination has its directory split by the runtime year/month (date('Y/m')), so if you assert with fixed values like'2025/09/...' , it willalways fail when the month changes.

// Before → After
$this->assertEquals('2025/09/00000001_image_field.png', $result['image_field'] ?? '');
$this->assertEquals(date('Y/m') . '/00000001_image_field.png', $result['image_field'] ?? '');
  • For general notes on time comparison, also see cakephp-migration 3-4 (do not use the actual time() ).

How to proceed with investigation (checklist)

  1. For test execution and tallying all failures, suspect basercms-unittest skill. First vendor mixed in (T-1) , and check whether there are two cakephp paths in the trace.
  2. PluginCollection::create(): … null given → T-2、Plugin named 'X' is already loaded → T-3/T-4。
  3. Class "…\…" not found(sub-plugin) → T-5.
  4. For unexplained 500s in integration tests → visualize the root cause with T-6, then classify.
  5. For framework/PHP causes, go to the relevant section of cakephp-migration / php-migration . baserCMS-specific deprecations are B-1, TreeBehavior is C-1, date dependency is D-1.
  6. Each time you fix something, check php -l → --filter individually → at the end, rerun all tests to confirm the reduction in count and check for new regressions.

Note: Immediately after adding or updating a new skill file in .claude/skills/ , it is not reflected in the current Claude Code session (it is discovered when the session starts).Restart Claude Codethen /basercms-plugin-5x-update so that it can be called.