0

but, composer DOES autoload same baseclass in the same folder.

My error: Fatal error: Class 'VendorName\ParentFolder\Enums\BasicEnum not found in C:\VendorName\www\src\ParentFolder\Enums\MyEnumeration.php on line 5.

MyEnumeration.php:

<?php
  namespace VendorName\ParentFolder\Enums;


  abstract class MyEnumeration extends BasicEnum {
      const ConstantOne = 1;
      const ConstantTwo = 2;
      const ConstantThree = 3;
}

and BasicEnum.php:

<?php
namespace VendorName\ParentFolder;

abstract class BasicEnum {
    private static $constCacheArray = NULL;

    private function __construct() { }

    private static function getConstants() {
        if (self::$constCacheArray == NULL) {
            self::$constCacheArray = [];
        }
        $calledClass = get_called_class();
        if (!array_key_exists($calledClass, self::$constCacheArray)) {
            $reflect = new ReflectionClass($calledClass);
            self::$constCacheArray[$calledClass] = $reflect->getConstants();
        }
        return self::$constCacheArray[$calledClass];
    }

    public static function isValidName($name, $strict = false) {
        $constants = self::getConstants();

        if ($strict) {
            return array_key_exists($name, $constants);
        }

        $keys = array_map('strtolower', array_keys($constants));
        return in_array(strtolower($name), $keys);
    }

    public static function isValidValue($value, $strict = true) {
        $values = array_values(self::getConstants());
        return in_array($value, $values, $strict);
    }
}

My folder structure:

+ VendorName
  + www
    + src
      + ParentFolder
        + Enums
              MyEnumeration.php
          BasicEnum2.php

And, the autoload is built via:

composer dump-autoload

with composer.json:

{
    "autoload": {
        "psr-4": { "VendorName\\": "src/"}

    }
}

My .php page:

<h1>Composer Autoload Test</h1>
<p>Trying to load a class where base class is in parent folder.
<p>(Using the PHP.net BasicEnum example).
<br/>
<?php
    require __DIR__ . '/vendor/autoload.php';

    use VendorName\ParentFolder;
    use VendorName\ParentFolder\Enums;



    echo '<br/><br/>';
    if (class_exists('MyEnumeration')) {
        echo 'MyEnumeration exists';
    }
    else {
        echo 'MyEnumeration does NOT exist';   // This line prints out in browser.
    }
    echo '<br/><br/>';

    echo '<br/><br/>';
    if (class_exists('VendorName\ParentFolder\Enums\MyEnumeration')) {  // This line blows up because BaseEnum is not found in MyEnumeration.php.
        echo 'VendorName\ParentFolder\Enums\MyEnumeration exists';
    }
    else {
        echo 'VendorName\ParentFolder\Enums\MyEnumeration does NOT exist';
    }
    echo '<br/><br/>';


    echo '<br/><br/>';
    if (defined('VendorName\ParentFolder\Enums\MyEnumeration::ConstantOne')) {
        echo 'VendorName\ParentFolder\Enums\MyEnumeration::ConstantOne exists';
    }
    else {
        echo 'VendorName\ParentFolder\Enums\MyEnumeration::ConstantOne does NOT exist';
    }
    echo '<br/><br/>';  


    echo 'NotExist:' . (MyEnumeration::isValidName('NotExist') ? 'true' : 'false') . '<br/>';
    echo 'ConstantOne:' . (MyEnumeration::isValidName('ConstantOne') ? 'true' : 'false') . '<br/>';
    echo 'ConstantTwo:' . (MyEnumeration::isValidName('ConstantTwo') ? 'true' : 'false') . '<br/>';
    echo 'ConstantThree:' . (MyEnumeration::isValidName('ConstantThree') ? 'true' : 'false') . '<br/>';

?>

Lastly, I have to full qualify the MyEnumeration with the namespace or it's not found even though I've got a 'use' statement and using composer autoload.

2 Answers2

0

It looks like you have
file src\ParentFolder\Enums\BasicEnum2.php
with class BasicEnum in namespace VendorName\ParentFolder

but you must have file src\ParentFolder\Enums\BasicEnum.php (without 2)
and in your MyEnumeration.php script you have to have this use block:

use VendorName\ParentFolder\BasicEnum;
cn007b
  • 16,596
  • 7
  • 59
  • 74
  • It's a typeO, it is BasicEnum.php. i.e. classname = filename, same case, too (per PSR-4, which I am new to). –  Sep 18 '17 at 22:11
  • Do you have `use VendorName\ParentFolder\BasicEnum;` in `MyEnumeration.php`? – cn007b Sep 18 '17 at 22:13
  • I did not, but Matt Gibson got back to me first. :-( Last token in 'use' is the classname, not sub-namespace of class. Gotta re-read namespace doc again (and, clear my head of how it works in other langs ;-) It seems 'use' should be short-hand for ANY class in that namespace. Now, I'm confused per autoload saying any class is loaded. What's the point if I have to 'use' every single class? –  Sep 18 '17 at 22:39
  • @PHPDev75 Namespaces and the autoloader are only really related by convention here. The autoloader saves you having to `include` files manually in PHP (and makes it easier for code to be reused in systems like Composer). The namespaces are to separate out code so that if *you* write a `BasicEnum` class it doesn't clash with someone else's `BasicEnum` class, as you'll each put them in your specific namespace. The relation of autoloading to namespace in PSR-4 is really just a smart-ish hack to make it easy to write an autoloader class that can find arbitrary classes quickly. – Matt Gibson Sep 18 '17 at 22:44
  • @MattGibson So, if I have eleventy teen classses in MyNamespace1, I'd have to 'use' all eleventy teen? I guess, I could write my own autoloader to do that for me, but I'm thinking surely someone's already done that by now since that's how it works in other languages - including just the namespace, every class in that namespace is resolvable. –  Sep 18 '17 at 23:24
  • If you want to refer to them without typing the namespace out every time, yes. This doesn't seem too laborious in practice; I'm thinking perhaps in PHP you don't tend to branch out of your own namespace too often in the one file. (Or maybe it's just because I use editors like Eclipse that can `use` automatically or do autocompletion of namespaced classes...) (Writing your own autoloader wouldn't help, as an autoloader can't change the way namespaces work. You can autoload until you're blue in the face and all it'll do is include files. It won't change the namespaces at all.) – Matt Gibson Sep 19 '17 at 09:44
0

Your MyEnumeration class is in the namespace VendorName\ParentFolder\Enums. Hence it cannot find BasicEnum as a symbol as that's in the namespace above, \VendorName\ParentFolder. You can specify it absolutely in your child class:

abstract class MyEnumeration extends \VendorName\ParentFolder\BasicEnum {

You'll also need to indicate the global namespace for ReflectionClass in your BasicEnum:

$reflect = new \ReflectionClass($calledClass);

Note also that you can't just import everything from a namespace using use. You need to use particular classes. For example, in your php page, specify:

use VendorName\ParentFolder\Enums\MyEnumeration;

...to allow the use of MyEnumeration without a qualifier.

Matt Gibson
  • 37,886
  • 9
  • 99
  • 128
  • ah, NOW 'use' statement documentation makes sense where last token is the alias. It -is- the classname. I was thinking last subnamespace. This must be related to why the class_exists needs fully qualified class name? If so, I'm missing the point of autoloader. I thought it said it'd load a class name anywhere under src without fully qualified name. –  Sep 18 '17 at 22:22
  • > $reflect = new \ReflectionClass($calledClass); - not sure where this goes. That BasicEnum is from PHP.Net doc. –  Sep 18 '17 at 22:23
  • There's a line of code in the BasicEnum class that uses `ReflectionClass` without a scope qualifier, i.e. `$reflect = new ReflectionClass($calledClass);` (note the lack of a backslash.) I'm going to guess that the code you got from php.net wasn't already in a namespace? As you've now put that code inside a namespace, you'll need the backslash to "get back to" global scope and find the [ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) class. Otherwise it'll be trying to find \VendorName\ParentFolder\ReflectionClass, which doesn't exist. – Matt Gibson Sep 18 '17 at 22:27
  • I got that BasicEnum code here: https://secure.php.net/manual/en/class.splenum.php –  Sep 18 '17 at 22:30
  • This does fix my posted error. I think SO is a single break/fix per post, right? Hopefully, not pushing my luck to ask why the last four lines with isValidName can't find MyEnumeration (Class 'MyEnumeration' was not found ... ): –  Sep 18 '17 at 22:33
  • Yeah; like I said, it's not in a namespace there, so it can "see" `ReflectionClass` just fine. The code and `ReflectionClass` are both in the global namespace. Now you've put it into the namespace `VendorName\ParentFolder` you'll need to say `\ReflectionClass` instead of `ReflectionClass` to "break out" of your namespace and find it. – Matt Gibson Sep 18 '17 at 22:33
  • And, I know why last four lines don't work, same reason the other 'use' didn't. This answers my question. –  Sep 18 '17 at 22:35
  • Per global namespace, remembering reading / was global namespace. Didn't realize I'd need it coming directly off PHP.net. –  Sep 18 '17 at 22:37
  • @PHPDev75 Bear in mind that everything below the "User contributed notes" heading on the PHP.net pages are effectively "comments" on the manual, and can be added by anyone. There's often fragments and snippets that need rather more work to be entirely useful. – Matt Gibson Sep 18 '17 at 22:41