Skip to content

Instantly share code, notes, and snippets.

@pniederlag
Last active August 29, 2015 14:14
Show Gist options
  • Save pniederlag/c61c2bd75a02c78ab71d to your computer and use it in GitHub Desktop.
Save pniederlag/c61c2bd75a02c78ab71d to your computer and use it in GitHub Desktop.
makeInstance adressing issue
Demonstrate the usage of makeInstance and/or php namespaces adressing background
<?php
namespace test {
class foo {
}
// fixed
$testFoo = new foo();
//$anotherFoo = test/foo(); // fatal
$testYetAnotherFoo = new \test\foo();
// dynamic
// $foo = 'foo'; // fatal
$fooFullyQualifiedName = '\\test\\foo';
$fooQualifiedName = 'test\\foo';
$testFooFullyQualified = new $fooFullyQualifiedName;
var_dump(get_class($testFooFullyQualified));
$testFooQualified = new $fooQualifiedName;
var_dump(get_class($testFooQualified));
}
namespace {
// fixed
// $testFoo = new foo(); // fatal
$anotherFoo = new test\foo();
$testYetAnotherFoo = new \test\foo();
echo "" . PHP_EOL;
// dynamic
$fooFullyQualifiedName = '\\test\\foo';
$fooQualifiedName = 'test\\foo';
$testFooFullyQualified = new $fooFullyQualifiedName;
var_dump(get_class($testFooFullyQualified));
$testFooQualified = new $fooQualifiedName;
var_dump(get_class($testFooQualified));
echo "" . PHP_EOL;
echo "Lessons learned:" . PHP_EOL;
echo "In *global* scope you can use 1) full qualified name *or* 2) qualified names" . PHP_EOL;
echo "php internally always represents the classname only *qualified*" . PHP_EOL;
echo "which is fine because from php perspective this is *global* namespace scope" . PHP_EOL;
echo "" . PHP_EOL;
}
/*
* in plain PHP
*/
// qualified relative (subnamespace) class instantiation
$fileUtility = new File\BasicFileUtility();
// fully qualified (absolute)
$fileUtility = new \TYPO3\CMS\Core\Utility\File\BasicFileUtility();
/*
* TYPO3 makeInstance or any other plain dynamic language context (variables as class names etc. pp')
*/
$fileUtility = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\\BasicFileUtility');
// Thx to @helhum for clearification/explanation
// we are using a dynamic language feature (classname as variable), thus it will be always interpreted in *global* namespace.
// So both fully qualified(leading slash) as well as qualified names do work from php perspective
// As internally the class name is stored qualified (without leading slash) it is easier to force to supply names qualified only.
@helhum
Copy link

helhum commented Feb 5, 2015

Consider the following PHP code:

<?php
namespace test;

use import\of\something;

spl_autoload_register(function($className) {
    var_dump('class loading request: ' . $className);
    switch ($className) {
        case 'test\\foo';
            class foo {};
        break;
        case 'test\\bar';
            class bar {};
        break;
        case 'foo';
            eval('class foo {};');
        break;
    }
});

/* **************************************************************
 * literal class names
 * **************************************************************
 */

$a = new foo();
var_dump(get_class($a)); // outputs: string(31) "class loading request: test\foo", string(8) "test\foo"

$a = new \test\foo();
var_dump(get_class($a)); // outputs string(8) "test\foo"

//$a = new test\foo(); // fatals with "Class 'test\test\baz' not found"

/* **************************************************************
 * class names in strings
 * **************************************************************
 */

$testClassRelative = 'foo';
$testClassAbsolute = 'test\\foo';
$testClassAbsoluteWithoutLeadingSlash = 'test\\bar';
$testClassAbsoluteLeadingSlash = '\\test\\bar';
$absentTestClassAbsoluteLeadingSlash = '\\test\\baz';

$b = new $testClassRelative();
var_dump(get_class($b)); // outputs string(8) "foo"

$b = new $testClassAbsolute();
var_dump(get_class($b)); // outputs string(8) "test\foo"

$b = new $testClassAbsoluteLeadingSlash();
var_dump(get_class($b)); // outputs string(8) "test\foo" (@see https://bugs.php.net/bug.php?id=50731)

//$b = new $absentTestClassAbsoluteLeadingSlash(); // fatals with "Class '\test\baz' not found"


/* **************************************************************
 * class_exists calls
 * **************************************************************
 */

var_dump(class_exists($testClassAbsoluteLeadingSlash)); // outputs bool(true)
var_dump(class_exists($testClassAbsoluteWithoutLeadingSlash)); // outputs bool(true)
var_dump(class_exists($absentTestClassAbsoluteLeadingSlash)); // outputs string(31) "class loading request: test\baz" bool(true)
var_dump(class_exists($absentTestClassAbsoluteLeadingSlash)); // outputs string(31) "class loading request: test\baz" bool(true)

You'll see that PHP internally works without leading backslash when passing fully qualified class names around as strings. It isn't super consistent in that. In fact in global scope you can write \foo\bar and foo\bar and it will behave exactly the same.

DEV without TYPO3 Background most likely would think this is "relative"

This has exactly NOTHING to do with TYPO3 background! Devs need to learn and understand that class names passed as strings are in global scope in PHP thus always fully qualified (and preferably without leading slash as it would be redundant). This is true for class_exists and frankly even for new statements.

PHP itself is more graceful than we are now in TYPO3. It strips off the leading slash most of the time, except in bugs that occurred for that reason and obviously in some error messages.

To avoid such bugs, decided to not be graceful and add ltrim statements in every single place we deal with class names as strings but instead throw exceptions in key places so that userland code is forced to not be redundant.

makeInstance will add the leading '' internally

Wrong! makeInstance in 6.2 trims the leading \ and throws an exception in 7.x to avoid further troubles.
It is syntactically and semantically correct in PHP to not use a leading slash for fqcn in global scope.

Hope this made some things clear now

@pniederlag
Copy link
Author

First of all many thx for your comment and very nice and exact explanations.

DEV without TYPO3 Background most likely would think this is "relative"

This has exactly NOTHING to do with TYPO3 background! Devs need to learn and understand that class names passed as strings are in global scope in PHP thus always fully qualified

full ACK,

So I blame PHP for

  • allowing both fully qalified(leading slash) as well as qualified names on global scope
  • representing class names qualified(sic!) internally, instead of fully qualified

Still to me the fully qualified name is more intuitive (while it might be redundant). But as now I got the whole picture of all the implications of fully qualified vs. qualified I got your point.

@pniederlag
Copy link
Author

btw, Updated the gist and put my essence "lessons learned" into demo.php.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment