2

I'm creating a factory class in my project, where this class gets a Report Type as a string. This string has the name of the concrete class that implements a Report Interface.

The issue I'm having is that when I'm instantiating this class, I get a Class not found error.

Here follows the factory code.

namespace App\Term\Reports;

class Factory
{
    public static function build($type)
    {
        $obj = new CableBySensor();  // Works!

        // $type == 'CableBySensor'
        $obj2 = new $type;         // Class not found :(

        // ... validates if the class exists ...
        // ... and if it implements the Report Interface ...
        // ... throw exception if class doesn't exist or doesn't implements interface
        // ... then returns the corresponding object.
    }
}

Both methods are virtually the same thing.

First: Why do I have to specify the full qualified name of the class in the string to make it work? The class CableBySensor resides in the same namespace as Factory.

This started giving me trouble because I also want to validate that the class being instantiated implements a ReportsInterface.

Second: How do I overcome this? Should I call the factory like this $myReport = Factory::build('App\Term\Reports\' . $className); or should I use the __NAMESPACE__ constant inside the Factory class such as this: $obj = new __NAMESPACE__ . '\' . $className?

Thank you.

7
  • Let's take a step back here, how come you're passing $type as a string? Additionally would you opose the idea of having switch($type){case "CableBySensor": $obj2 = new CableBySensort(); break;}. Commented Mar 9, 2016 at 13:36
  • 1
    Have you tried to see if by using NAMESPACE it works? Commented Mar 9, 2016 at 13:39
  • I suggest your read online about parametrised factory pattern. I personally would not write a factory class and then use it like you did. Commented Mar 9, 2016 at 13:41
  • Hello all and thank you for your comments. I was following this example. -- @Halcyon It's just that I thought I would have to modify the Factory and add a case to the switch statement every time I have a new report type. The way I did, it would work with any class that implemented the interface (or so I thought :D). I will validate if the class implements the Report Interface. -- @YehiaAwad Using NAMESPACE works. -- @tomazahlin I will, thank you! Commented Mar 9, 2016 at 14:00
  • I'm not entirely sure the author of the article understands how to use factories. This seems very scary to me: "A factory is capable of creating different types of objects without necessarily knowing what type of object is actually being created.". Normally you'd use a factory if instantiating an object is complex or if you need to pass dependencies that you don't want to pass to the thing that needs to make an instance (so you give it an 'abstract' factory instead). Commented Mar 9, 2016 at 14:08

1 Answer 1

2

Indifferently whether the factory approach is useful here or not, the problem is with trying to instantiate from a dynamic variable.

Or as akhoondi at php.net pointed out:

One must note that when using a dynamic class name [...] the "current namespace" [...] is global namespace.

There are possibly 3 solutions:

  • pass the fully qualified class name to your factory method (arghh...)

    $instance = Factory::build('Acme\CableBySensor');
    
  • Or, do a check in your build method and prefix the namespace if necessary (as suggested here) (sounds not so fool proof to me)

    public static function build($type)
    {
        if ($type[0] !== '\\') {
            $type = '\\' . __NAMESPACE__ . '\\' . $type;
        }
        $obj = new $type;
        ...
    }
    
  • Or, if you have PHP 5.5+ why not use class name resolution via ::class? Personally, I would go for that one whenever possible:

    $instance = Factory::build(CableBySensor::class);
    
Sign up to request clarification or add additional context in comments.

1 Comment

First question is answered. About the second. I'm going with the second, but with a slight adjustment. As much as I'd like to go with the third option, at that point I only have a string with the name like cable_by_sensor (which is the option the user selected). So, I apply studly_case() to $type (then I have the actual class name, as a convention). After that I add the __NAMESPACE__ in front, then validate if the class implements the interface I want, then I return a new instance of the object or eles I throw an exception.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.