PHP - get_class_methods() Problem - php

I've this sample code:
class A
{
public function A_A() { /* ... */ }
public function A_B() { /* ... */ }
}
class B extends A
{
public function B_A() { /* ... */ }
public function B_B() { /* ... */ }
public function B_C()
{
return get_class_methods($this);
}
}
$a = new A();
$b = new B();
Doing this:
echo '<pre>';
print_r($b->B_C());
echo '</pre>';
Yields the following output:
Array
(
[0] => B_A
[1] => B_B
[2] => B_C
[3] => A_A
[4] => A_B
)
How can I make it return only the following methods?
Array
(
[0] => B_A
[1] => B_B
[2] => B_C
)
I've a method in class A that should call all the methods in class B, the problem is of course that it leads to a infinite loop due to the values returned by get_class_methods().

You might need full strength Reflection. However, before you go there, it might be worth trying something like this.
array_diff(get_class_methods($this), get_class_methods(get_parent_class($this)))

You can't. Part of the functionality of extending a class is that you get all of the methods of the class you extended, in the new class, identical to as if you defined them in the new class itself.

Related

Accessing the properties of an object of an object dynamically in PHP

Given that I'm getting different object results depending of the API I query, here are two examples out of 35 I have to implement:
stdClass Object
(
[mid] => 7127.75
[bid] => 7126.6
[ask] => 7128.9
[last_price] => 7128.8
[low] => 7000.0
[high] => 7492.1
[volume] => 53255.4195502
[timestamp] => 1510265647.9803913
)
stdClass Object
(
[Success] => 1
[Message] =>
[Data] => stdClass Object
(
[AskPrice] => 7095
[BidPrice] => 7070
[Low] => 7001
[High] => 7540
[Volume] => 17.38943459
[LastPrice] => 7090
[Change] => -1.02
[Open] => 7163
[Close] => 7090
)
[Error] =>
)
I want to build mapping variable array to access the object easily.
$xmap["a"]["bid"] = "bid";
$xmap["b"]["bid"] = "Data->BidPrice";
Let's assume $content has the first example object, this will work:
print $content->{$xmap["a"]["bid"]}; // the result is 7128.9
As for the second example object, it does not:
print $content->{$xmap["b"]["bid"]}; // the result is PHP Notice: Undefined property: stdClass::$Data->BidPrice in ...
Can this be done or am I stuck with if statements!
First, convert all the objects into assoc. arrays by using json_encode/decode. You'll find this code multiple times here in stackoverflow
$arr = json_decode(json_encode($obj), true);
Second, I recommend a dot-notation for the key-path, plus a tiny function to find the value in a multidimensional array.
Example:
function fromArray($key, $arr) {
$keys = explode('.', $key);
foreach($keys as $k) {
if (!isset($arr[$k]))
return array(); // return empty array
$arr = $arr[$k];
}
return $arr; // can be a scalar value or array
}
$key = 'my.super.array.content';
$array = ['my' => [
'super' => [
'array' =>[
'content'=>4711
]
]
]
];
var_dump($array, fromArray($key, $array));
/*
array(1) {
["my"]=>
array(1) {
["super"]=>
array(1) {
["array"]=>
array(1) {
["content"]=>
int(4711)
}
}
}
}
int(4711)
*/
I found the dot-notation very useful when dealing with complex structures.
You can convert your objects to arrays and build a large mapping array that needs to be maintained and then explode it and loop through it to access the other arrays; or you can try using patterns. I'm thinking Adapter, but maybe another is a better fit. This is using your second object as an example, but just add as many as needed:
class ContentAdapter {
public function __get($name) {
return $this->obj->{$xmap[$name]};
}
}
class ContentAdapter_API_B extends ContentAdapter {
public $xmap = ['bid' => 'BidPrice', 'ask' => 'AskPrice'];
public function __construct($obj) {
$this->obj = $obj->data;
}
}
Now it is consistant regardless of the object since each has an adapter:
$content = new ContentAdapter_API_B($content);
echo $content->bid;
Using your first object you can either create a child as well (ContentAdapter_API_A in case the structure ever changes) or instantiate directly:
$content = new ContentAdapter($content);
echo $content->bid;
Or obviously just use it as is:
echo $content->bid;
An alternate way without inheritance is to use getters:
class ContentAdapter_API_B {
public function __construct($obj) {
$this->obj = $obj->data;
}
public function getBid() { return $this->obj->BidPrice; }
}
So long as the methods are consistent then it will always work:
$content = new ContentAdapter_API_B($content);
echo $content->getBid;

getting warning when use in_array and multidimensional-array

I make a array and fill it by variables(index and value) by code in php(yii framework). but my program don't work good.I cannot use my array after fill.
in model Lookup.php
private static $row_flags= array();
private static $table_flags= array();
public static function checkStatus($value,$row,$column) {
$thresholds= hreshold::model()->findAll(array('select'=>$row.','.$column));
self::$row_flags['browsing'][$row][$column]=($value < $thresholds[0]->$row) ?"green":(($value > $thresholds[0]->$column)?"red":"yellow");
return self::$row_flags['browsing'][$row][$column];
}
public static function getRowFlag() {
return self::$row_flags;
}
public static function row_color($table,$row){
return in_array("yellow",self::$row_flags['browsing'][$row]);
}
view.php
<?php
lookup::checkStatus(3001,'http','access');
lookup::checkStatus(3001,'http','access');
lookup::checkStatus(3001,'http','core');
lookup::checkStatus(3001,'http','blackbox');
lookup::checkStatus(3001,'http','gateway');
lookup::checkStatus(3001,'http','internet');
print_r(lookup::getRowFlag());
?>
Array
(
[browsing] => Array
(
[http] => Array
(
[access] => yellow
[core] => yellow
[blackbox] => yellow
[gateway] => yellow
[internet] => yellow
)
)
)
if I call Lookup::row_color('browsing','http') in view I have
warning: in_array() expects parameter 2 to be array, null given
Maybe column is the troublemaker:
Instead of l(array('select'=>$row.','.$column));
Use l(array('select'=>$row.','.'select'=>$column));
Use this:
public static function row_color($table,$row){
if (is_array(self::$row_flags['browsing'][$row])) {
return in_array("yellow",self::$row_flags['browsing'][$row]);
}
return FALSE;
}

After mocking object there are missing functions?

Admittedly I am very new to working with Mock objects but have come across a strange behavior that I hope someone can explain to me.
I have two classes class A and class B. B extends A and looks something like this.
abstract class A {
private $property
public __constructor( $arg ) {
//sets and does constructor stuff
}
public function a() {
//does stuff
}
public function b() {
//does more stuff
}
public function c() {
//calls b and does stuff
}
}
class B extends A {
const MyCONST = //some literal
public __constructor() {
parent::__constructor( self::MyCONST );
}
}
Since my class A is abstract I am doing my testing on class B. When testing function c I need to stub function b so I have done the following in my test.
$objectUnderTest = $this->getMockBuilder( 'B' )
->setMethods( array( 'b' ) )
->getMock();
$objectUnderTest->expects( $this->once() )
->method( 'b' )
->will( $this->returnValue( 5 ) );
$result = $objectUnderTest->c();
//then some assertions
The problem I am running into is that when the following test code is executed PHPUnit it complains that it can not find method c of $objectUnderTest. Now from what I understand even if I had created the mock object incorrectly and function c had become a stub method as opposed to a mock method it would still be invokeable right? Some debugging to confirm what PHPUnit was telling me I outputted to the console the results of get_class_methods() on the mock object as well as on a normal instance of my class B. This is what it outputted for each.
//class B
array(
(int) 0 => '__construct',
(int) 1 => 'a',
(int) 2 => 'b',
(int) 3 => 'c'
)
//mocked B
array(
(int) 0 => '__clone',
(int) 1 => 'b',
(int) 2 => 'expects',
(int) 3 => 'staticExpects',
(int) 4 => '__phpunit_getInvocationMocker',
(int) 5 => '__phpunit_getStaticInvocationMocker',
(int) 6 => '__phpunit_hasMatchers',
(int) 7 => '__phpunit_verify',
(int) 8 => '__phpunit_cleanup'
)
Does anyone know what happened to my other methods for this class or explain this behavior?
I've faced the same problem while trying to run your code, so I included file with classes (A and B) to the test class. It helped me overcome this problem.

Anonymous function returns class properties? PHP

I was reading a WordPress tutorial in which the author used something like this (I simplified it):
class WPObject {
public $ID;
public $title;
public $content;
public $status;
public function __construct($wp_post) {
$modifiers = [
'key' => function($k, $v) {
return (substr($k, 0, 5) === "post_") ? substr($k, 5) : $k;
}
];
}
}
The function is supposed to remove the post_ prefix from the wp queried object. The question I have is regarding the function I posted above. That anonymous function seems to return an object with with the properties. When I did a print_r on it I get...
Array
(
[key] => Closure Object
(
[this] => WPObject Object
(
[ID] =>
[title] =>
[content] =>
[status] =>
)
[parameter] => Array
(
[$k] =>
[$v] =>
)
)
)
I'm still learning about anonymous functions and was wondering how/why it does this? If you call an anonymous function from an object, does it create an instance of that object or something?
Also, sorry if I'm using incorrect terminology. Don't have anonymous functions, closures, lambda functions straightened out yet.
Not a new instance, it has a reference to the same object in which it is created since PHP 5.4 I believe. So the closure itself can call properties or methods on that class as if being in that class.
class foo {
public $bar = 'something';
function getClosure(){
return function(){
var_dump($this->bar);
};
}
}
$object = new foo();
$closure = $object->getClosure();
//let's inspect the object
var_dump($object);
//class foo#1 (1) {
// public $bar =>
// string(9) "something"
//}
//let's see what ->bar is
$closure();
//string(9) "something"
//let's change it to something else
$object->bar = 'somethingElse';
//closure clearly has the same object:
$closure();
//string(13) "somethingElse"
unset($object);
//no such object/variables anymore
var_dump($object);
//NULL (with a notice)
//but closure stills knows it as it has a reference
$closure();
//string(13) "somethingElse"

Check all function arguments

I've been wondering how I could check all my parameters efficiently in any function in a clean and concise way.
I've come up with :
function fooBar($myArg, $mySecondArg, $myOptionnalArg = "defaultValue"){
if(!isset($myArg, $mySecondArg, $myOptionnalArg){
die(__FUNCTION__ . ":missing parameter");
}
if(empty($myArg, $mySecondArg, $myOptionnalArg){
die(__FUNCTION__ . ":empty parameter");
}
//Do stuff
}
What I'm looking for is more like:
function fooBar($myArg, $mySecondArg, $myOptionnalArg = "defaultValue"){
$argArray = func_get_args();
foreach($argArray as $a){
if(empty($a)){
die(__FUNCTION__.":".get_arg_name($a)."is empty");
}
if(!isset($a)){
die(__FUNCTION__.":".get_arg_name($a)."is unset");
}
}
//Do stuff
}
Wich doesn't work since get_arg_name(); is pure fiction, and func_get_args(); returns the actual parameters, rather than the ones defined in the prototype.
Could someone give me a hint about it? Any other good/clean way to achieve that is also welcome.
Thanks
With reflection only. Some example:
function test($a, $b, $c) {
$function = new ReflectionFunction(__FUNCTION__);
$parameters = $function->getParameters();
print_r($parameters);
}
test(0, 1, 2);
You will see something like this:
Array
(
[0] => ReflectionParameter Object
(
[name] => a
)
[1] => ReflectionParameter Object
(
[name] => b
)
[2] => ReflectionParameter Object
(
[name] => c
)
)
Its possible to get what you need with using php reflection:
http://php.net/manual/en/book.reflection.php
Please, read more about how to use it.

Categories