PHP: Instantiating a class from a variable oddly fails
Asked Answered
D

1

2

I'm trying to instantiate a class (a Laravel3 Eloquent model) by a variable, and I'm getting an error saying that the class is not found.
When I hardcode the class name, though, it works just fine.
(FYI, in the code below $contact_type is expected to either be Phone, Fax, or Email.)

Here's what I'm playing with at the moment:

    foreach( $input AS $contact_type => $contact_info )
    {
        foreach( $contact_info AS $data )
        {
            $obj = new $contact_type( (array)$data);
            echo'<pre>Obj: ',print_r($obj),'</pre>';  // <----- For Testing
        }
    }

When I run the code as above, it throws a "Class 'Phone' not found" error.
When I replace new $contact_type() with new Phone() (or Fax or Email), it works just fine.
I bet there's something simple I'm just looking over :) What am I missing?
Please help!

Dialyser answered 3/1, 2014 at 16:41 Comment(9)
Are you in a namespace by any chance?Tallowy
@Tallowy - Beat me to it. I bet that's it. Can't possibly think of anything else.Squiggle
How's namespace related here? $obj = new Phone() should not work either if namespace are relatedLampas
@RoyalBg Actually when you have aliased the namespace or class in a USE statement, it would work when written manually, but not work as a variable.Supporter
It's something new to me. Thanks. But why, where would be the difference?Lampas
@Supporter - Not just when useing. Classes in the current namespace aren't resolved when using a string either.Squiggle
@JosephSilber, I've not found that to be true, I have code that uses a variable for the name, and as long as it's in the same namespace and not aliased, it works just fine.Supporter
Are they resolved if you build the namespace with string? Sorry for asking without testing. Will test a bit later. And I still wonder why they are not resolved using a string, when in namespaceLampas
@Supporter - codepad.viper-7.com/38iklSSquiggle
H
6

The relevant manual entries that covers this are here and here. To quote the second link:

If a string containing the name of a class is used with new, a new instance of that class will be created. If the class is in a namespace, its fully qualified name must be used when doing this.

This works for me:

class Phone {}
$classtype = 'Phone';
$phone = new $classtype();
var_dump($phone);

Produces the output:

object(Phone)#1 (0) {
}

Look to make sure you're not in a namespace (include the Phone class' namespace in the string if you are). Or, you can also try using reflection:

class Phone {}
$classtype = 'Phone';
$reflectionClass = new ReflectionClass($classtype);
$phone = $reflectionClass->newInstanceArgs();
var_dump($phone);

If the Phone class is in a namespace, this also works:

Phone.php

<?php namespace Contact;

class Phone {}

test.php

<?php

include 'Phone.php';

$classtype = 'Contact\Phone';
$phone = new $classtype();
var_dump($phone);

I'm not 100% sure why, although I suspect that when evaluating a variable class name the current namespace mappings aren't visible. Consider this code:

<?php namespace Foo;

use SomePackage\SomeClass as WeirdName;

$test = new WeirdName();

Compare that with:

<?php namespace Foo;

use SomePackage\SomeClass as WeirdName;

$class = 'WeirdName';
$test = new $class();

When PHP decides to allocate memory for a new instance, how will it know to map the class name alias of WeirdName to SomePackage\Someclass? That alias is only valid for the current file, and the code that actually performs that operation isn't even in userland code, much less the same file.

Honeyed answered 3/1, 2014 at 16:46 Comment(3)
The issue was namespacing. I have use Person\Model\Eloquent\Phone at the top of my script (which is why it works when I call for a new Phone()), but for some reason, when instantiating like new $var() I have to define the full namespace in variable ... If anyone can explain why, I'm VERY interested in learning.Dialyser
I'm not 100% sure why, but I updated my answer with what I suspect is the causeHoneyed
Tnx, I fixed it too by defining the namespace in the variable.. which is not such a bad thing when dealing with several classes (not having to use them all separately). Although, when using a dynamic classname with Route::model(), this can bug your php artisan route:list command. Because it can't find the class empty string :pExcerpt

© 2022 - 2024 — McMap. All rights reserved.