0

I've forked CodeIgniter 3 to work on PHP 8.2 compatibility. I'm using PHPStan for static analysis to try and identify all the referencees to undefined class properties, which seems to be the primary shortcoming. Unfortunately, I'm getting very different output on MacOS than on Ubuntu, and I don't know why. It would appear that phpstan on MacOS is failing to recognize the definition of one class, CI_DB, and this is leading to about 800 more errors being reported. My question is why are there more errors on MacOS than on Ubuntu and how do I remedy this problem?

NOTE: Both machines have PHP 8.2 installed:

MacOS::

$ php -v
PHP 8.2.3 (cli) (built: Feb 24 2023 10:25:18) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.3, Copyright (c), by Zend Technologies

Ubuntu:

$ php -v
PHP 8.2.3 (cli) (built: Feb 14 2023 16:57:50) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.3, Copyright (c), by Zend Technologies

To reproduce the problem, you can run this script from the command line:

mkdir ci3-dev
cd ci3-dev
### fetch CI3
curl -o ci.zip https://codeload.github.com/bcit-ci/CodeIgniter/zip/refs/heads/develop
unzip ci.zip
mv CodeIgniter-develop codeigniter
rm ci.zip
### install composer 
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
### install phpstan
./composer.phar require --dev phpstan/phpstan
### configure and run phpstan
echo -e "parameters:" >> ci.neon
echo -e "\tlevel: 2" >> ci.neon
echo -e "\tpaths:" >> ci.neon
echo -e "\t\t- codeigniter" >> ci.neon
echo -e "\texcludePaths:" >> ci.neon
echo -e "\t\tanalyse:" >> ci.neon
echo -e "\t\t\t- codeigniter/tests" >> ci.neon
echo -e "\t\t\t- codeigniter/user_guide_src" >> ci.neon
vendor/bin/phpstan clear-result-cache -c ci.neon
vendor/bin/phpstan analyse -c ci.neon > phpstan.txt

NOTE: This code may get out of date if composer or CI3 gets updated. You might need to manually retrieve CI3 or adjust the composer installation steps if this script has problems.

The output of the phpstan analysis can be found in ci3-dev/phpstan.txt. On my Ubuntu machine, this reports 1044 errors. On my MacOS machine, it reports 1830 errors. I've got a zip file with phpstan-ubuntu.txt, phpstan-mac.txt, and a diff of the two files, phpstan-diff.txt but these files are about 200K each, so too large to post here. I offer this one example difference to try and express what I think the primary problem is, namely that MacOS somehow doesn't see the declaration of the class CI_DB. The ubuntu output for system/libraries/Profiler.php:

 ------ -----------------------------------------------------
  Line   system/libraries/Profiler.php
 ------ -----------------------------------------------------
  166    Call to an undefined method object::elapsed_time().
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
 ------ -----------------------------------------------------

is quite short, whereas the MacOS output is quite a bit longer because it somehow can't find CI_DB:

 ------ ---------------------------------------------------------------------
  Line   system/libraries/Profiler.php
 ------ ---------------------------------------------------------------------
  166    Call to an undefined method object::elapsed_time().
  207    Class CI_DB not found.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  215    Class CI_DB not found.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  247    Access to property $queries on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  248    Access to property $query_times on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  260    Access to property $database on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  261    Access to property $queries on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  264    Access to property $queries on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  271    Access to property $queries on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  273    Access to property $query_times on an unknown class CI_DB.
          Learn more at https://phpstan.org/user-guide/discovering-symbols
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
  521    Access to an undefined property object::$lang.
 ------ ---------------------------------------------------------------------
S. Imp
  • 2,833
  • 11
  • 24
  • Glad to see I am not the only feeling like they're going crazy. Been fighting this for a couple days now and still don't have a solution. – risingfish Aug 30 '23 at 21:00

1 Answers1

1

I'm not sure about the difference between Linux and macOS, but I see the behaviour on macOS as more expected.

The class CI_DB is defined in a very non-conventional way in codeigniter/system/database/DB.php inside function &DB declaration:

if ( ! class_exists('CI_DB', FALSE))
    {
        /**
         * CI_DB
         *
         * Acts as an alias for both CI_DB_driver and CI_DB_query_builder.
         *
         * @see CI_DB_query_builder
         * @see CI_DB_driver
         */
        class CI_DB extends CI_DB_query_builder {}
    }

That's a far cry from modern PHP project standards that use PSR-4, or at least class declarations as top-level symbols in their files. That's why PHPStan doesn't see the class out of the box.

But I was able to get quite far by looking at CodeIgniter is bootstrapped, define the global constants, and then load the file manually. You can try it out by using the following code in a file called phpstan-bootstrap.php:

// copied and modified from codeigniter/index.php

define('ENVIRONMENT', 'development');

// The name of THIS file
define('SELF', __DIR__ . '/codeigniter/index.php');

// Path to the system directory
define('BASEPATH', __DIR__ . '/codeigniter/system/');

// Path to the front controller (this file) directory
define('FCPATH', __DIR__ . '/codeigniter/');

// Name of the "system" directory
define('SYSDIR', basename(BASEPATH));

define('APPPATH', __DIR__ . '/codeigniter/application/');

define('VIEWPATH', __DIR__ . '/codeigniter/application/views/');


require_once __DIR__ . '/codeigniter/system/core/Common.php';
require_once __DIR__ . '/codeigniter/system/database/DB.php';
DB();

And by putting this in your ci.neon in the parameters section:

    bootstrapFiles:
        - phpstan-bootstrap.php

On my computer the analysis now fails with:

PHP Warning:  mysqli::real_connect(): (HY000/2002): No such file or directory in /Users/ondrej/Downloads/ci3-dev/codeigniter/system/database/drivers/mysqli/mysqli_driver.php on line 211
Warning: mysqli::real_connect(): (HY000/2002): No such file or directory in /Users/ondrej/Downloads/ci3-dev/codeigniter/system/database/drivers/mysqli/mysqli_driver.php on line 211
RuntimeException thrown in /Users/ondrej/Downloads/ci3-dev/codeigniter/system/database/DB_driver.php on line 434 while loading bootstrap file /Users/ondrej/Downloads/ci3-dev/phpstan-bootstrap.php: Unable to connect to the database.

Unfortunately I cannot get any further because I don't have any database on my machine, but if you manage to solve this problem, you should be able to get further in your analysis.

Ondřej Mirtes
  • 5,054
  • 25
  • 36
  • Thank you for your knowledgeable post. I had definitely noticed the strange class declaration. I attempted your bootstrap suggestion but this did not eliminate the rampant "class CI_DB not found" errors. I moved that class declaration into its own file and this *did* eliminate those errors. I also tried moving the class declaration to the top of system/database/DB.php and that also worked. – S. Imp Feb 26 '23 at 20:17