Error when running `fl2 upgrade 2.0` on my extension

Open
#51 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
58/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
php, typescript
Domain
cli, tooling

Research direction

Inspect boilerplate/stubs/backend/advanced-api-resource.php and src/steps/stubs/backend/advanced-api-resource.ts, then reproduce the reported fl2 upgrade 2.0 flow with an extension lacking a model class. Verify that generated resources remain valid PHP and that the upgrade proceeds past the Intervention Image Library step without the reported parser error.

Written by the indexing model from the issue text.

Description

Managed to fix it via https://github.com/huoxin233/flarum-ext-money-with-history/commit/b2654a89dc1bafc15d93e814fad3708f3d379cdb, not sure if the issue in cli is valid and still worth fixing


I ran fl2 upgrade 2.0 on my Money with History extension https://github.com/huoxin233/flarum-ext-money-with-history/commit/e49634eab5d563fe67c251ced3fd969e11f3b7d8

Got an error half way through:

  STEP 12/16    Prepare for JSON:API changes.

     =>     DONE     Flarum 2.0 completely refactors the JSON:API implementation. The way resource CRUD operations,
                     serialization and extending other resources is done has completely changed.

                     The tool cannot completely automate this change, but it has added some boilerplate code and TODO comments to help you get started.
                     You should make whatever changes you can now, then properly test and adapt your code once the upgrade process is over.

                     Read more: https://docs.flarum.org/2.x/extend/update-2_0#jsonapi

                     Checkout our additional guide which provides concrete examples for the upgrading process of the JSON:API layer:
                     https://docs.flarum.org/2.x/extend/update-2_0-api

     =>    COMMIT    chore(2.0): JSON:API changes

  STEP 13/16    Intervention Image Library v3

     =>   WORKING.  
Error occurred, and could not complete:
The PHP subsystem returned an invalid value: Syntax error, unexpected '{' on line 15 - #0 /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php(185): PhpParser\ParserAbstract->doParse()
#1 /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/php-subsystem/src/Upgrade/Replacement.php(29): PhpParser\ParserAbstract->parse()
#2 /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/php-subsystem/index.php(37): Flarum\CliPhpSubsystem\Upgrade\Replacement->handle()
#3 {main}
Error: The PHP subsystem returned an invalid value: Syntax error, unexpected '{' on line 15 - #0 /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php(185): PhpParser\ParserAbstract->doParse()
#1 /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/php-subsystem/src/Upgrade/Replacement.php(29): PhpParser\ParserAbstract->parse()
#2 /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/php-subsystem/index.php(37): Flarum\CliPhpSubsystem\Upgrade\Replacement->handle()
#3 {main}
    at PhpSubsystemProvider.handlePhpError (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/providers/php-provider.js:48:19)
    at PhpSubsystemProvider.run (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/providers/php-provider.js:34:14)
    at /home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/steps/upgrade/twopointoh/backend/intervention-image.js:16:35
    at InterventionImage.applyReplacements (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/steps/upgrade/twopointoh/base.js:164:34)
    at applyOn (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/steps/upgrade/twopointoh/base.js:103:47)
    at async InterventionImage.run (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/steps/upgrade/twopointoh/base.js:134:13)
    at async StepManager.runStep (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/boilersmith/step-manager.js:179:23)
    at async checkAndRun (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/boilersmith/step-manager.js:92:24)
    at async StepManager.run (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/boilersmith/step-manager.js:113:21)
    at async TwoPointOh.run (/home/huoxin/.nvm/versions/node/v24.15.0/lib/node_modules/fl2/lib/base-command.js:71:21)

Before the error, the following steps were completed:

Saw that at Step 12 Prepare for JSON:API changes, a file src/Api/Resource/MoneyHistoryResource.php is created with syntax error, it is nothing behind the implements:

<?php

namespace Huoxin\MoneyWithHistory\Api\Resource;

use Flarum\Api\Context;
use Flarum\Api\Endpoint;
use Flarum\Api\Resource;
use Flarum\Api\Schema;
use Tobyz\JsonApiServer\Context as OriginalContext;

/**
 * @extends Resource\AbstractResource<object>
 */
class MoneyHistoryResource extends Resource\AbstractResource implements 
{
...
Review from AI: The Root Cause: A Misplaced Comma

When the Flarum 2.0 migration script (or a user running flarum-cli make api-resource) generates a new Api\Resource without an attached model class, the template logic mistakenly swallows the comma meant to separate the endpoints in the returned PHP array.

Take a look at the original EJS logic for the endpoints() array (lines 36-38):

        return [<% if (endpoints.includes('create')) { %>
            Endpoint\Create::make()<% if (modelClassName) { %>
                ->can('create<%= modelClassName %>'),<% } %><% } %><% if (endpoints.includes('update')) { %>
            Endpoint\Update::make()
                ->can('update'),

The trailing comma , for the Endpoint\Create::make() element was placed inside the <% if (modelClassName) { %> conditional block.

If the upgrade script encounters a scenario where it generates an API resource that is not tied to a Flarum Database Model (or if the AST parser failed to extract the model class cleanly from the legacy serializer), modelClassName evaluates to false. This results in the EJS generating the following malformed PHP output:

        return [
            Endpoint\Create::make()
            Endpoint\Update::make()
                ->can('update'),
        ];

Because Endpoint\Create::make() lacks a trailing comma, it immediately breaks the PHP array syntax. Flarum CLI uses nikic/php-parser under the hood to perform AST transformations. When the subsequent "Intervention Image Library" upgrade step kicks in, it attempts to parse all .php files in your src/ directory. The parser encounters this malformed array inside your newly generated MoneyHistoryResource.php and crashes the entire upgrade process with a syntax error.

Fix provided from AI
diff --git a/boilerplate/stubs/backend/advanced-api-resource.php b/boilerplate/stubs/backend/advanced-api-resource.php
index be6040d..b27d8b6 100644
--- a/boilerplate/stubs/backend/advanced-api-resource.php
+++ b/boilerplate/stubs/backend/advanced-api-resource.php
@@ -14,7 +14,7 @@ use Tobyz\JsonApiServer\Context as OriginalContext;
 /**
  * @extends <% if (modelClassName) { %>Resource\AbstractDatabaseResource<<%= modelClassName %>><% } else { %>Resource\AbstractResource<object><% } %>
  */
-class <%= className %> extends <% if (modelClassName) { %>Resource\AbstractDatabaseResource<% } else { %>Resource\AbstractResource<% if (interfaces.length > 0) %> implements <%= interfaces.join(', ') %><% } %>
+class <%= className %> extends <% if (modelClassName) { %>Resource\AbstractDatabaseResource<% } else { %>Resource\AbstractResource<% if (interfaces.length > 0) { %> implements <%= interfaces.join(', ') %><% } %><% } %>
 {
     public function type(): string
     {
@@ -35,13 +35,13 @@ class <%= className %> extends <% if (modelClassName) { %>Resource\AbstractDatab
     {
         return [<% if (endpoints.includes('create')) { %>
             Endpoint\Create::make()<% if (modelClassName) { %>
-                ->can('create<%= modelClassName %>'),<% } %><% } %><% if (endpoints.includes('update')) { %>
+                ->can('create<%= modelClassName %>')<% } %>,<% } %><% if (endpoints.includes('update')) { %>
             Endpoint\Update::make()
                 ->can('update'),<% } %><% if (endpoints.includes('delete')) { %>
             Endpoint\Delete::make()
                 ->can('delete'),<% } %><% if (endpoints.includes('show')) { %>
             Endpoint\Show::make()
-                ->authenticated(),<% } %><% if (endpoints.includes('list')) { %>
+                ->authenticated(),<% } %><% if (endpoints.includes('list') || endpoints.includes('index')) { %>
             Endpoint\Index::make()
                 ->paginate(),<% } %>
         ];
diff --git a/src/steps/stubs/backend/advanced-api-resource.ts b/src/steps/stubs/backend/advanced-api-resource.ts
index a539aec..9c12c49 100644
--- a/src/steps/stubs/backend/advanced-api-resource.ts
+++ b/src/steps/stubs/backend/advanced-api-resource.ts
@@ -72,6 +72,7 @@ export class GenerateAdvancedApiResourceStub extends BasePhpStubStep {
 
     const interfaceMap: Record<string, string> = {
       list: 'Resource\\Contracts\\Listable',
+      index: 'Resource\\Contracts\\Listable', // Add index as an alias for list
       show: 'Resource\\Contracts\\Findable',
       create: 'Resource\\Contracts\\Creatable',
       update: 'Resource\\Contracts\\Updatable',
@@ -79,7 +80,7 @@ export class GenerateAdvancedApiResourceStub extends BasePhpStubStep {
     };
 
     params.modelType ||= pluralKebabCaseModel(params.modelClassName as string);
-    params.interfaces = (params.endpoints as string[]).map((endpoint) => interfaceMap[endpoint]);
+    params.interfaces = (params.endpoints as string[]).map((endpoint) => interfaceMap[endpoint]).filter(Boolean);
 
     return params;
   }

Dominant language
TypeScript
Stars
47
Forks
4
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from flarum/cli

All issues in flarum/cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.