Undefined method error in ReflectionClass but method exists - php

I am currently using phpunit for some unit testing. Due to the presence of some protected methods, I had to use a Reflection Class to change the visibility of these methods to public.
The initial methods were called successfully but somehow it gets stuck at a specific method:
Fatal error: Call to undefined method ReflectionClass::create_schema()in
/vagrant/fuelphp/fuel/app/tests/model/repository/jobpost.php on line 54
However, dumping the method via get_method() with var_dump proves that it exists in the class instance:
class ReflectionMethod#2317 (2) {
public $name =>
string(13) 'create_schema'
public $class =>
string(34) 'Model_Repository_Feed'
}
Then the real confusing bit, I decided to use hasMethod() to see if the method exists:
52 echo "If this says 1, class exists: ".$this->_target->hasMethod('create_schema');
53 try {
54 $this->_target->create_schema();
55 }
The result when running says, "yes it exists.... but it doesn't":
If this says 1, class exists: 1
Fatal error: Call to undefined method ReflectionClass::create_schema() in /vagrant/fuelphp/fuel/app/tests/model/repository/jobpost.php on line 54
To clarify this method is public and is inherited from an abstract parent class:
public function create_schema() {
$this->create_schema_exec(self::$_real_schema_name);
}
How can this issue be solved?

you need to get the object of the class that holds the method not the reflection object.
$reflection = new ReflectionClass($className);
$object = $reflection->newInstanceWithoutConstructor();
$object->methodName();

Related

PHP: Can't unserialise php object that now implements the \Serializable interface

According to the docs
when an old instance of a class that implements this interface now, which had been serialized before the class implemeted the interface, is unserialized, __wakeup() is called instead of the unserialize method, which might be useful for migration purposes.
I thought that's quite clever and useful and wanted to check it out. Unfortunately it didn't work for me and I wonder whether there's something I'm doing wrong or whether there's a bug.
Test code:
//class Foo
class Foo implements \Serializable
{
public $a = 'lorem';
public function __wakeup()
{
fprintf(STDOUT, "in %s\n", __METHOD__);
}
public function serialize()
{
fprintf(STDOUT, "in %s\n", __METHOD__);
return serialize([
$this->a,
]);
}
public function unserialize($serialized)
{
fprintf(STDOUT, "in %s\n", __METHOD__);
list(
$this->a,
) = unserialize($serialized);
}
}
//$foo = new Foo();
//var_dump(serialize($foo));
//exit;
$serialised = 'O:3:"Foo":1:{s:1:"a";s:5:"lorem";}';
//$serialised = 'C:3:"Foo":22:{a:1:{i:0;s:5:"lorem";}}';
$foo = unserialize($serialised);
var_dump($foo);
It crashes with:
Warning: Erroneous data format for unserializing 'Foo' in /in/SHaCP on line 39
Notice: unserialize(): Error at offset 13 of 34 bytes in /in/SHaCP on line 39
bool(false)
In essence, I serialised the $foo object without and with the \Serializable interface. Then added the interface and tried to unserialize() the object serialised in the previous form (note that the serialised string starting with O is without the interface, while the one starting with C is with the interface).
Is there something I'm doing wrong here? Or maybe I misunderstood the docs?
Interestingly, the code runs fine on hhvm at 3v4l.org
That IS the MAIN difference between default serialization and one from the interface - by default it serializes the entire object, but with implementation of interface you define how to serialize already created object' attributes.
As such, the resulting string is going to be different due to internal implementation - as you can see in one case it's starting with "O", and in the other it's "C". Because of that you'll have to do saving of it again.

PHP calling a class method with class name as variable string

I have a class from which I want to call a function, but the instantiated class variable is taken from a string.
class Myclass
{
public function myFunction($param1,$param2) {
return 'blah';
}
}
I am trying the following but it doesn't work. Errors from log file below.
$myclass='myclass';
${$myclass}=new ucfirst($myclass);
$args=array($stringvar,1);
$retval=call_user_func_array(array(${$myclass}, 'myFunction'), $args);
What am I doing wrong ?
Errors :
PHP Warning: call_user_func_array() expects parameter 1 to be a valid callback, first array member is not a valid class name or object in file.php on line 100
PHP Notice: Undefined variable: myclass in file.php on line 134
PHP Fatal error: Call to a member function myFunction() on a non-object in file.php on line 134
You're overcomplicating it a bit with the extra ${} bracketing on the class reference.
$myclass='myCLaSs';
$myObject = new $myclass();
$retval = $myObject->myFunction($stringvar, 1);
Or if you need to use call_user_func_array:
$args=array($stringvar,1);
$retval=call_user_func_array(array($myObject, 'myFunction'), $args);
When you use the ${} bracketing, you are referencing variables by a variable name. For example:
$myVariable = "A";
$aVariableThatIsHoldingAVariableName = "myVariable";
echo ${$aVariableThatIsHoldingAVariableName}; // outputs "A".
Applying this to your code shows the following logic happening:
Set the variable $myclass equal to the string 'myclass'
$myclass='myclass';
Set the variable ${$myclass}:
This gets the value of the variable $myclass ('myclass') and uses that as the variable name. In other words, the following variable name resolution happens: ${$myclass} => ${'myclass'} => $myclass.
So this line sets $myclass to a new Myclass(); object:
${$myclass}=new ucfirst($myclass);
The first parameter to call_user_func_array is a callable (See http://us2.php.net/manual/en/language.types.callable.php). The callback it looks like you are trying to reference here is Type 3: array($object, $method). But the same variable resolution happens. Now, ${$myclass} is going to resolve differently, because the value of $myclass is a Myclass Object. Variable names have to be strings, not objects (obviously), so ${Myclass Object} is totally invalid.
$args=array($stringvar,1);
$retval=call_user_func_array(array(${$myclass}, 'myFunction'), $args);
But since $myclass at that point is an object already, you can just do the following (as mentioned initially above):
$retval=call_user_func_array(array($myclass, 'myFunction'), $args);

calling class object from other class Not using "global" php

I am new to php so please excuse my lack of knowledge. I am using eclipse and have a project with 3 files inside the project. I am creating a find discount class which takes the class object to call a function from another class. The error:
Notice: Undefined variable: GetInfoClass line ..
Fatal error: Call to a member function getAge() on a non-object line ...
I tried to read about it but I cant seem to understand it. Please help. Thanks
formResponse:
include "GetInfo.php";
include "IfDiscount.php";
$IfDiscount= new IfDiscount();
echo $IfDiscount->findDiscount();
class IfDiscount:
class IfDiscount
{
public function findDiscount(){
$Age = $GetInfoClass->getAge();
echo $Age;}}
$GetInfoClass is not available to findDiscount() because it is out of scope. You should pass it to findDiscount() as a parameter to make it available to that method:
public function findDiscount($GetInfoClass){
$Age = $GetInfoClass->getAge();
return $Age;
}
echo $IfDiscount->findDiscount($GetInfoClass);
(You also want to return $Age, not echo it. You already explicitly echo it when you call that method.)

PHPUnit mock object methods

I am learning unit testing with PHPUnit and am running into a strange problem with mock objects that I can't resolve. I set up these dummy files as an example of what I am experiencing:
Class1
class PrimaryObj1
{
function doNothing(SecondObj $s)
{
}
}
Class2
class PrimaryObj2
{
function doNothing(SecondObj $s)
{
$s->TestMethod();
}
}
and a test file as:
class PrimaryObj1_test extends PHPUnit_Framework_TestCase
{
private $primaryObj;
function setUp()
{
$this->primaryObj = new PrimaryObj1();
}
function testDoNothing()
{
$secondObj = $this->getMock('SecondObj');
$secondObj->expects($this->once())
->method("TestMethod");
$this->primaryObj->doNothing($secondObj);
}
}
(one test file for each dummy class where everything is the same except for the class name).
When I run PHPUnit, this is what I get:
Running Tests/PrimaryObj1_test.php
1) PrimaryObj1_test::testDoNothing
Expectation failed for method name is equal to <string:TestMethod> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
Running Tests/PrimaryObj2_test.php
Fatal error: Call to undefined method Mock_SecondObj_99c898e7::TestMethod() in PrimaryObj2.php on line 5
So first it is mad that it didn't call the expected method but then when I do it gets mad cause it is undefined. I just can't win. I think this is why I'm still single...
Any thoughts on why this might be happening?
I got a response from an email list serve with the answer. This line:
$secondObj = $this->getMock('SecondObj');
should be:
$secondObj = $this->getMock('SecondObj', array('TestMethod'));
Once I made that change it worked as expected.

Catchable Fatal Error: Argument 1 passed to Foo::bar() must implement interface BazInterface, null given

There are some cases when you override a method which has type hinted input parameter like this:
class FooParent
{
public function bar(BazInterface $baz)
{
// ...
}
}
And you want to allow passing null values as input parameters.
If you remove interface type hint
class Foo extends FooParent
{
public function bar($baz)
{
// ...
}
}
you'll get an error like this:
Fatal error: Declaration of Foo::bar() must be compatible with that of FooParent::bar()
How can you allow null values without changing the parent class?
This is a real world example since parent class can be part of the third party library or framework, so changing it is not an option.
Solution is to add default null value to input parameter like this:
class Foo extends FooParent
{
public function bar(BazInterface $baz = null)
{
// ...
}
}
This is not what I expected since default value assigns default value to a variable if not provided, I didn't expect it to affect allowed input. But I saw example on http://php.net/manual/en/language.oop5.typehinting.php, so I decided to document it here. Hope someone will find it useful.

Categories