nextcloud / nextcloud/server

[Bug]: Nullable dependencies might trigger 500 return value

Open
#51,573 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage 32-feedback bug developer experience technical debt
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

⚠️ This issue respects the following points: ⚠️
Bug description

Since some time it is possible to define dependency injector requirements with nullable types. The documentation states this is intended if the dependency can’t be found or build.

The building of a nullable dependency might fail and trigger PHP interpreter failure. As a result, the page is not rendered anymore but an error 500 is returned as final word of death. Further details see below.

Steps to reproduce

To simplify, I created a MWE app to show the problem.

  1. Clone the MWE into the apps folder of a dev environment. Name it test_nullable_di as mentioned in the README.
  2. Enable the app
  3. The app installs 4 navigation endpoints. Use the one ending in /transitive. It is just http://nextcloud.local/index.php/apps/test_nullable_di/transitive in the case of Julius' dev environment. This will show a dummy test message without error.
  4. The corresponding public page is /transient-pub. Open it in a private browser tab. This will error out with a internal server error.
Expected behavior

Both pages (logged in and not logged in) should show the same dummy info message without an error message shown.

Especially the controller of the public page should show a valid page and internally use null values to represent non-resolvable dependencies resolved by the DI algorithm.

Development environment setup
Nextcloud Server version

master

Operating system

Other

PHP engine version

PHP 8.1

Web server

Apache (supported)

Database engine version

MariaDB

Is this bug present after an update or on a fresh install?

None

Are you using the Nextcloud Server Encryption module?

Encryption is Disabled

What user-backends are you using?
  • Default user-backend (database)
  • LDAP/ Active Directory
  • SSO - SAML
  • Other
Configuration report
{
    "system": {
        "debug": true,
        "profiler": true,
        "apps_paths": [
            {
                "path": "\/var\/www\/html\/apps",
                "url": "\/apps",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/apps-extra",
                "url": "\/apps-extra",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/apps-shared",
                "url": "\/apps-shared",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/apps-writable",
                "url": "\/apps-writable",
                "writable": true
            }
        ],
        "allow_local_remote_servers": true,
        "mail_from_address": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpmode": "smtp",
        "mail_sendmailmode": "smtp",
        "mail_domain": "***REMOVED SENSITIVE VALUE***",
        "mail_smtphost": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpport": "1025",
        "skeletondirectory": "\/skeleton",
        "loglevel": 0,
        "log_query": false,
        "query_log_file": "\/var\/www\/html\/data\/query.log",
        "diagnostics.logging": false,
        "diagnostics.logging.threshold": 0,
        "log.condition": {
            "apps": [
                "diagnostics",
                "admin_audit"
            ]
        },
        "passwordsalt": "***REMOVED SENSITIVE VALUE***",
        "secret": "***REMOVED SENSITIVE VALUE***",
        "trusted_domains": [
            "localhost",
            "nextcloud",
            "nextcloud.local",
            "192.168.21.6",
            "localhost",
            "dev.nc.wolf-stuttgart.net"
        ],
        "datadirectory": "***REMOVED SENSITIVE VALUE***",
        "dbtype": "mysql",
        "version": "32.0.0.0",
        "overwrite.cli.url": "http:\/\/nextcloud.local,dev.nc.wolf-stuttgart.net",
        "dbname": "***REMOVED SENSITIVE VALUE***",
        "dbhost": "***REMOVED SENSITIVE VALUE***",
        "dbport": "",
        "dbtableprefix": "oc_",
        "mysql.utf8mb4": true,
        "dbuser": "***REMOVED SENSITIVE VALUE***",
        "dbpassword": "***REMOVED SENSITIVE VALUE***",
        "updater.release.channel": "git",
        "installed": true,
        "instanceid": "***REMOVED SENSITIVE VALUE***",
        "lookup_server": "",
        "redis": {
            "host": "***REMOVED SENSITIVE VALUE***",
            "port": 6379
        },
        "memcache.local": "\\OC\\Memcache\\Redis",
        "memcache.locking": "\\OC\\Memcache\\Redis",
        "trusted_proxies": "***REMOVED SENSITIVE VALUE***",
        "maintenance": false,
        "app_install_overwrite": [
            "calendar",
            "contacts",
            "deck",
            "spreed",
            "circles",
            "forms",
            "collectives",
            "appointments",
            "polls",
            "timetracker",
            "epubviewer"
        ],
        "theme": ""
    }
}
List of activated Apps
Enabled:
  - calendar: 5.2.0-rc.1
  - cloud_federation_api: 1.15.0
  - comments: 1.22.0
  - contactsinteraction: 1.13.0
  - cookbook: 0.11.3
  - dashboard: 7.12.0
  - dav: 1.34.0
  - epubviewer: 1.7.1
  - federatedfilesharing: 1.22.0
  - federation: 1.22.0
  - files: 2.4.0
  - files_pdfviewer: 5.0.0-dev.0
  - files_reminders: 1.5.0
  - files_sharing: 1.24.0
  - files_trashbin: 1.22.0
  - files_versions: 1.25.0
  - logreader: 5.0.0-dev.0
  - lookup_server_connector: 1.20.0
  - oauth2: 1.20.0
  - profile: 1.1.0
  - provisioning_api: 1.22.0
  - recommendations: 5.0.0-dev.0
  - settings: 1.15.0
  - sharebymail: 1.22.0
  - sociallogin: 5.9.2
  - spreed: 21.0.0
  - systemtags: 1.22.0
  - test_nullable_di: 1.0.0
  - theming: 2.7.0
  - twofactor_backupcodes: 1.21.0
  - updatenotification: 1.22.0
  - user_status: 1.12.0
  - viewer: 5.0.0-dev.0
  - weather_status: 1.12.0
  - webhook_listeners: 1.3.0
  - workflowengine: 2.14.0
Disabled:
  - admin_audit: 1.22.0
  - encryption: 2.20.0
  - files_external: 1.24.0
  - hmr_enabler: 1.0.0 (installed 1.0.0)
  - profiler: 1.4.0 (installed 1.4.0)
  - snowflakestheme: 1.1.2 (installed 1.1.2)
  - testing: 1.22.0
  - timetracker: 0.0.83 (installed 0.0.83)
  - user_ldap: 1.23.0
Nextcloud Signing status
Integrity checker has been disabled. Integrity cannot be verified.
Nextcloud Logs
{
  "reqId": "zuWPyZD0K5oEhWMmtuRp",
  "level": 3,
  "time": "2025-03-18T18:23:03+00:00",
  "remoteAddr": "192.168.21.4",
  "user": false,
  "app": "index",
  "method": "GET",
  "url": "/index.php/apps/test_nullable_di/transitive-pub",
  "message": "OCA\\TestNullableDi\\Service\\InvalidService::__construct(): Argument #1 ($userId) must be of type string, null given",
  "userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0",
  "version": "32.0.0.0",
  "exception": {
    "Exception": "TypeError",
    "Message": "OCA\\TestNullableDi\\Service\\InvalidService::__construct(): Argument #1 ($userId) must be of type string, null given",
    "Code": 0,
    "Trace": [
      {
        "function": "__construct",
        "class": "OCA\\TestNullableDi\\Service\\InvalidService",
        "type": "->",
        "args": [
          null
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 98,
        "function": "newInstanceArgs",
        "class": "ReflectionClass",
        "type": "->",
        "args": [
          [
            null
          ]
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 106,
        "function": "buildClass",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          {
            "__class__": "ReflectionClass",
            "name": "OCA\\TestNullableDi\\Service\\InvalidService"
          }
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 124,
        "function": "resolve",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\InvalidService"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php",
        "line": 440,
        "function": "query",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\InvalidService"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php",
        "line": 412,
        "function": "queryNoFallback",
        "class": "OC\\AppFramework\\DependencyInjection\\DIContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\InvalidService"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 74,
        "function": "query",
        "class": "OC\\AppFramework\\DependencyInjection\\DIContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\InvalidService",
          true
        ]
      },
      {
        "function": "OC\\AppFramework\\Utility\\{closure}",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "*** sensitive parameters replaced ***"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 98,
        "function": "array_map",
        "args": [
          {
            "__class__": "Closure"
          },
          [
            "*** sensitive parameters replaced ***"
          ]
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 106,
        "function": "buildClass",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          {
            "__class__": "ReflectionClass",
            "name": "OCA\\TestNullableDi\\Service\\TransitiveService"
          }
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 124,
        "function": "resolve",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\TransitiveService"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php",
        "line": 440,
        "function": "query",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\TransitiveService"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php",
        "line": 412,
        "function": "queryNoFallback",
        "class": "OC\\AppFramework\\DependencyInjection\\DIContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\TransitiveService"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 74,
        "function": "query",
        "class": "OC\\AppFramework\\DependencyInjection\\DIContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Service\\TransitiveService",
          true
        ]
      },
      {
        "function": "OC\\AppFramework\\Utility\\{closure}",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "*** sensitive parameters replaced ***"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 98,
        "function": "array_map",
        "args": [
          {
            "__class__": "Closure"
          },
          [
            {
              "__class__": "ReflectionParameter",
              "name": "userId"
            },
            "*** sensitive parameters replaced ***"
          ]
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 106,
        "function": "buildClass",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          {
            "__class__": "ReflectionClass",
            "name": "OCA\\TestNullableDi\\Controller\\TransitivePageController"
          }
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php",
        "line": 124,
        "function": "resolve",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Controller\\TransitivePageController"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php",
        "line": 440,
        "function": "query",
        "class": "OC\\AppFramework\\Utility\\SimpleContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Controller\\TransitivePageController"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php",
        "line": 412,
        "function": "queryNoFallback",
        "class": "OC\\AppFramework\\DependencyInjection\\DIContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Controller\\TransitivePageController"
        ]
      },
      {
        "file": "/var/www/html/lib/private/AppFramework/App.php",
        "line": 140,
        "function": "query",
        "class": "OC\\AppFramework\\DependencyInjection\\DIContainer",
        "type": "->",
        "args": [
          "OCA\\TestNullableDi\\Controller\\TransitivePageController"
        ]
      },
      {
        "file": "/var/www/html/lib/private/Route/Router.php",
        "line": 307,
        "function": "main",
        "class": "OC\\AppFramework\\App",
        "type": "::",
        "args": [
          "OCA\\TestNullableDi\\Controller\\TransitivePageController",
          "indexPub",
          {
            "__class__": "OC\\AppFramework\\DependencyInjection\\DIContainer"
          },
          {
            "_route": "test_nullable_di.transitivepage.indexpub"
          }
        ]
      },
      {
        "file": "/var/www/html/lib/base.php",
        "line": 1043,
        "function": "match",
        "class": "OC\\Route\\Router",
        "type": "->",
        "args": [
          "/apps/test_nullable_di/transitive-pub"
        ]
      },
      {
        "file": "/var/www/html/index.php",
        "line": 24,
        "function": "handleRequest",
        "class": "OC",
        "type": "::",
        "args": []
      }
    ],
    "File": "/var/www/html/apps-extra/test_nullable_di/lib/Service/InvalidService.php",
    "Line": 9,
    "message": "OCA\\TestNullableDi\\Service\\InvalidService::__construct(): Argument #1 ($userId) must be of type string, null given",
    "exception": {
      "xdebug_message": "<tr><th align='left' bgcolor='#f57900' colspan=\"5\"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> TypeError: OCA\\TestNullableDi\\Service\\InvalidService::__construct(): Argument #1 ($userId) must be of type string, null given in /var/www/html/apps-extra/test_nullable_di/lib/Service/InvalidService.php on line <i>9</i></th></tr>\n<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>\n<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>\n<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0001</td><td bgcolor='#eeeeec' align='right'>360752</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='/var/www/html/index.php' bgcolor='#eeeeec'>.../index.php<b>:</b>0</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0732</td><td bgcolor='#eeeeec' align='right'>3121152</td><td bgcolor='#eeeeec'>OC::handleRequest(  )</td><td title='/var/www/html/index.php' bgcolor='#eeeeec'>.../index.php<b>:</b>24</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>3</td><td bgcolor='#eeeeec' align='center'>0.1148</td><td bgcolor='#eeeeec' align='right'>3414208</td><td bgcolor='#eeeeec'>OC\\Route\\Router->match( <span>$url = </span><span>&#39;/apps/test_nullable_di/transitive-pub&#39;</span> )</td><td title='/var/www/html/lib/base.php' bgcolor='#eeeeec'>.../base.php<b>:</b>1043</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>4</td><td bgcolor='#eeeeec' align='center'>0.1241</td><td bgcolor='#eeeeec' align='right'>3709856</td><td bgcolor='#eeeeec'>OC\\AppFramework\\App::main( <span>$controllerName = </span><span>&#39;TransitivePageController&#39;</span>, <span>$methodName = </span><span>&#39;indexPub&#39;</span>, <span>$container = </span><span>class OC\\AppFramework\\DependencyInjection\\DIContainer { private ${OC\\AppFramework\\Utility\\SimpleContainer}container = class Pimple\\Container { private $values = [...]; private $factories = class SplObjectStorage { ... }; private $protected = class SplObjectStorage { ... }; private $frozen = [...]; private $raw = [...]; private $keys = [...] }; private string $appName = &#39;test_nullable_di&#39;; private $middleWares = []; private $server = class OC\\Server { private ${OC\\AppFramework\\Utility\\SimpleContainer}container = class Pimple\\Container { ... }; protected $appContainers = [...]; protected $hasNoAppContainer = [...]; protected $namespaces = [...]; private $webRoot = &#39;&#39; } }</span>, <span>$urlParams = </span><span>[&#39;_route&#39; =&gt; &#39;test_nullable_di.transitivepage.indexpub&#39;]</span> )</td><td title='/var/www/html/lib/private/Route/Router.php' bgcolor='#eeeeec'>.../Router.php<b>:</b>307</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>5</td><td bgcolor='#eeeeec' align='center'>0.1250</td><td bgcolor='#eeeeec' align='right'>3722728</td><td bgcolor='#eeeeec'>OC\\AppFramework\\DependencyInjection\\DIContainer->query( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Controller\\\\TransitivePageController&#39;</span>, <span>$autoload = </span>??? )</td><td title='/var/www/html/lib/private/AppFramework/App.php' bgcolor='#eeeeec'>.../App.php<b>:</b>140</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>6</td><td bgcolor='#eeeeec' align='center'>0.1250</td><td bgcolor='#eeeeec' align='right'>3722728</td><td bgcolor='#eeeeec'>OC\\AppFramework\\DependencyInjection\\DIContainer->queryNoFallback( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Controller\\\\TransitivePageController&#39;</span> )</td><td title='/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php' bgcolor='#eeeeec'>.../DIContainer.php<b>:</b>412</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>7</td><td bgcolor='#eeeeec' align='center'>0.1250</td><td bgcolor='#eeeeec' align='right'>3722728</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->query( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Controller\\\\TransitivePageController&#39;</span>, <span>$autoload = </span>??? )</td><td title='/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php' bgcolor='#eeeeec'>.../DIContainer.php<b>:</b>440</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>8</td><td bgcolor='#eeeeec' align='center'>0.1250</td><td bgcolor='#eeeeec' align='right'>3722728</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->resolve( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Controller\\\\TransitivePageController&#39;</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>124</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>9</td><td bgcolor='#eeeeec' align='center'>0.1250</td><td bgcolor='#eeeeec' align='right'>3722936</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->buildClass( <span>$class = </span><span>class ReflectionClass { public string $name = &#39;OCA\\\\TestNullableDi\\\\Controller\\\\TransitivePageController&#39; }</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>106</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>10</td><td bgcolor='#eeeeec' align='center'>0.1250</td><td bgcolor='#eeeeec' align='right'>3724432</td><td bgcolor='#eeeeec'><a href='http://www.php.net/function.array-map' target='_new'>array_map</a>( <span>$callback = </span><span>class Closure { virtual $closure = &quot;$this-&gt;OC\\AppFramework\\Utility\\{closure}&quot; }</span>, <span>$array = </span><span>[0 =&gt; class ReflectionParameter { public string $name = &#39;userId&#39; }, 1 =&gt; class ReflectionParameter { public string $name = &#39;transitiveService&#39; }]</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>98</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>11</td><td bgcolor='#eeeeec' align='center'>0.1252</td><td bgcolor='#eeeeec' align='right'>3728136</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->OC\\AppFramework\\Utility\\{closure:/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php:61-98}( <span>$parameter = </span><span>class ReflectionParameter { public string $name = &#39;transitiveService&#39; }</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>98</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>12</td><td bgcolor='#eeeeec' align='center'>0.1253</td><td bgcolor='#eeeeec' align='right'>3728240</td><td bgcolor='#eeeeec'>OC\\AppFramework\\DependencyInjection\\DIContainer->query( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\TransitiveService&#39;</span>, <span>$autoload = </span><span>TRUE</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>74</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>13</td><td bgcolor='#eeeeec' align='center'>0.1253</td><td bgcolor='#eeeeec' align='right'>3728240</td><td bgcolor='#eeeeec'>OC\\AppFramework\\DependencyInjection\\DIContainer->queryNoFallback( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\TransitiveService&#39;</span> )</td><td title='/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php' bgcolor='#eeeeec'>.../DIContainer.php<b>:</b>412</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>14</td><td bgcolor='#eeeeec' align='center'>0.1253</td><td bgcolor='#eeeeec' align='right'>3728240</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->query( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\TransitiveService&#39;</span>, <span>$autoload = </span>??? )</td><td title='/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php' bgcolor='#eeeeec'>.../DIContainer.php<b>:</b>440</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>15</td><td bgcolor='#eeeeec' align='center'>0.1253</td><td bgcolor='#eeeeec' align='right'>3728240</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->resolve( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\TransitiveService&#39;</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>124</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>16</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3728736</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->buildClass( <span>$class = </span><span>class ReflectionClass { public string $name = &#39;OCA\\\\TestNullableDi\\\\Service\\\\TransitiveService&#39; }</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>106</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>17</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3730112</td><td bgcolor='#eeeeec'><a href='http://www.php.net/function.array-map' target='_new'>array_map</a>( <span>$callback = </span><span>class Closure { virtual $closure = &quot;$this-&gt;OC\\AppFramework\\Utility\\{closure}&quot; }</span>, <span>$array = </span><span>[0 =&gt; class ReflectionParameter { public string $name = &#39;invalidService&#39; }]</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>98</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>18</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3730592</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->OC\\AppFramework\\Utility\\{closure:/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php:61-98}( <span>$parameter = </span><span>class ReflectionParameter { public string $name = &#39;invalidService&#39; }</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>98</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>19</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3730696</td><td bgcolor='#eeeeec'>OC\\AppFramework\\DependencyInjection\\DIContainer->query( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\InvalidService&#39;</span>, <span>$autoload = </span><span>TRUE</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>74</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>20</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3730696</td><td bgcolor='#eeeeec'>OC\\AppFramework\\DependencyInjection\\DIContainer->queryNoFallback( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\InvalidService&#39;</span> )</td><td title='/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php' bgcolor='#eeeeec'>.../DIContainer.php<b>:</b>412</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>21</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3730696</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->query( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\InvalidService&#39;</span>, <span>$autoload = </span>??? )</td><td title='/var/www/html/lib/private/AppFramework/DependencyInjection/DIContainer.php' bgcolor='#eeeeec'>.../DIContainer.php<b>:</b>440</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>22</td><td bgcolor='#eeeeec' align='center'>0.1256</td><td bgcolor='#eeeeec' align='right'>3730696</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->resolve( <span>$name = </span><span>&#39;OCA\\\\TestNullableDi\\\\Service\\\\InvalidService&#39;</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>124</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>23</td><td bgcolor='#eeeeec' align='center'>0.1270</td><td bgcolor='#eeeeec' align='right'>3731000</td><td bgcolor='#eeeeec'>OC\\AppFramework\\Utility\\SimpleContainer->buildClass( <span>$class = </span><span>class ReflectionClass { public string $name = &#39;OCA\\\\TestNullableDi\\\\Service\\\\InvalidService&#39; }</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>106</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>24</td><td bgcolor='#eeeeec' align='center'>0.1271</td><td bgcolor='#eeeeec' align='right'>3731488</td><td bgcolor='#eeeeec'><a href='http://www.php.net/ReflectionClass.newInstanceArgs' target='_new'>newInstanceArgs</a>( <span>$args = </span><span>[0 =&gt; NULL]</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>98</td></tr>\n<tr><td bgcolor='#eeeeec' align='center'>25</td><td bgcolor='#eeeeec' align='center'>0.1271</td><td bgcolor='#eeeeec' align='right'>3731576</td><td bgcolor='#eeeeec'>OCA\\TestNullableDi\\Service\\InvalidService->__construct( <span>$userId = </span><span>NULL</span> )</td><td title='/var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php' bgcolor='#eeeeec'>.../SimpleContainer.php<b>:</b>98</td></tr>\n"
    },
    "CustomMessage": "OCA\\TestNullableDi\\Service\\InvalidService::__construct(): Argument #1 ($userId) must be of type string, null given"
  }
}
Additional info

The problem originally arose in the forum.

I tracked it down possibly to a combination of caching and the DI with nullable types.

The MWE has multiple cases that might be interesting to implement in tests:

  • Keep the version as it is right now (regular case)
  • Commenting out the userId (or making it nullable) in the InvalidService makes the app work in any case.
  • Uncommenting this line in the InvalidService will hide the error from you but not solve it. This is sort of a bug of a bug.

The reason for the problem is that the DI algorithm traverses the dependency tree down from the top (aka the TransitivePageController class. In the regular case the DI algorithm first evaluates $userId to null and caches this in the Pimple container. Then, DI continues to TransitiveService and InvalidService as expected.

However, when building the InvalidService first all arguments are collected. As the $userId is cached, it is directly reused with the cached null value. Upon executing the controller, PHP checks the argument types and errors out (as it expected a non-nullable string there).

When the $unknownArgument is commented out, the DI bails out throwing a QueryException. This bubbles up until the complete DI branch of TransientService, InvalidService, and its dependencies are (correctly) cut off to null in TransientPageController.

I am pretty sure that commenting out the userId in the TransitivePageConteoller did make the app run without 500 but I can no longer reproduce this in my environment. Maybe there was the OpCache involved. IDK.

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.

Research direction

Start with lib/private/AppFramework/Utility/SimpleContainer.php and lib/private/AppFramework/DependencyInjection/DIContainer.php, following the stack trace from nullable dependency resolution to the failing InvalidService construction. Reproduce the issue with the linked test_nullable_di MWE and compare the logged transitive-pub failure with the working transitive endpoint. Done means both pages render the same dummy information without a 500 response.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.