Magento doesn't trigger setup script

I found a nice new, rarely trigger Magento bug.

In one of our projects, we have an example file to create new products:

data-upgrade-example-new-product-import.php

Beside this we had data scripts:

data-install-1.0.1.php
data-upgrade-1.0.0-1.0.1.php
data-upgrade-1.0.5-1.0.6.php

Unfortunately data-upgrade-1.0.5-1.0.6.php didn't fire.

It took me a while, but I found the problem:

// \Mage_Core_Model_Resource_Setup::_getModifySqlFiles
protected function _getModifySqlFiles($actionType, $fromVersion, $toVersion, $arrFiles)
{
    $arrRes = array();
    switch ($actionType) {

        // ... 

        case self::TYPE_DB_UPGRADE:
        case self::TYPE_DATA_UPGRADE:
            uksort($arrFiles, 'version_compare');
            foreach ($arrFiles as $version => $file) {
                $versionInfo = explode('-', $version);

                // In array must be 2 elements: 0 => version from, 1 => version to
                if (count($versionInfo)!=2) {
                    break;
                }

in $arrFiles we find an array with all files in the data dir which match a certain regex in \Mage_Core_Model_Resource_Setup::_getAvailableDataFiles. In short, when the files starts with data-.

The problem is, that data-upgrade-example-new-product-import.php doesn't meet the if (count($versionInfo)!=2) check and then break is called, which kills the complete loop, but should only be continue;.

So either we hack the core or rename the data-upgrade-example-new-product-import.php, I decided for renaming.

$file kills setup script

It is possible to change important and used variables inside the setup model while installing

I implemented a data-setup script today.

It starts with defining a variable:

$file = 'var/import/sample.csv';

The error

When magento was called, I got this error:

[message:protected] => Warning: Illegal string offset 'toVersion'  in app/code/core/Mage/Core/Model/Resource/Setup.php on line 641
[string:Exception:private] => 
[code:protected] => 0
[file:protected] => app/code/core/Mage/Core/functions.php
[line:protected] => 245
[trace:Exception:private] => Array
    [...]

I opened the Setup model and found this code snippet:

foreach ($files as $file) {
    [...]
    try {
        switch ($fileType) {
            case 'php':
                $conn   = $this->getConnection();
                $result = include $fileName;
                break;
           [...]     
        }

        if ($result) {
            $this->_setResourceVersion($actionType, $file['toVersion']);
        }
    } catch (Exception $e) {
        [...]
    }
    [...]
}

Debugging

I debbuged and at the beginning of the foreach $file is an array with the keys toVersion 0.1.10 and fileName with the path to my setup script.

After the $result $file was 'var/import/sample.csv'. It took a while until I realized, that it is the value I write into the variable, because I debugged the import script for a while. But then I wondered. WHY!?

Explanation

There is this small little line of code:

$result = include $fileName;

it includes the data/install script and the variable $file is assigned with my value. Especially the loop variable $file is overwritten.

Conclusion

Be careful with defining variables in setup scripts. I wanted to write a "bugfix" but thinking 10 minutes about the problem didn't develope a solution for the problem. There is no possibility in PHP to prevend the overwrite of this variable.

HAH! There is. I thought about changing the scope but didn't find a simple solution. but just implementing a method should do it!

Blog writing brings solutions. Thanks audience for help.

Hopefully you all know, what Rubber ducking is?