Print_r to find allowable values for a method? - php

I stumbled across this page which talks about the very handy new reflection class that ships with PHP5 and returns all the methods and properties of a class:
print_r to get object methods in PHP?
Following on from this, is there any way to determine the allowable values for the methods it returns?

You mean determine the allowed types of return values, and range of return values, for some method in some class? Very very hardly, I think. After all, there is no way of defining or hinting a return value in PHP. So I could do the following:
class .... {
function myFunction()
{
... some code ....
if (condition) return "A";
.... more code .....
if (condition2) return 2;
.... more code ....
if (condition3) return $_POST["number"];
}
}
this is a totally screwed-up example of course, but you get my point. The possible types of the return value are extremely hard to predict because I could return anything at any point.
I think the best one can do is solve this in documentation blocks. If you follow phpDoc notation:
/**
* #desc Searches for a record.
* #return int the number of found records.
*/
function myMethod()
{ ....
many IDEs are able to at least give you a hint about the expected return type when you type a call to the method.

Well, it depends on what you mean by "allowable values". If it's available in the doc-block, you can probably find it with Reflection... To find the return value:
class foo {
/**
* #returns boolean When false
*/
public function bar($x, $y = 'bar') {
}
}
$reflector = new ReflectionMethod('foo', 'bar');
$comment = $reflector->getDocComment();
if (preg_match('##returns (\\S+)#', $comment, $match)) {
echo "Method returns: ".$match[1];
}
produces:
Method Returns: boolean
Then, you'd just need to parse out what you want from that doc comment... Note it can be a type OR a class (or a grouping of multiple, boolean|null|string|DOMNode)...

Related

unsetting objects in an array based on a bool method

I'm trying to filter an array of objects implementing a specific interface (which simply defines the isComplete(): bool method) based on the result of that method. array_filter doesn't work because it can't call a method on each object to determine whether to filter it (or can it?). I've tried writing a function that takes the splatted array as an argument by reference, this doesn't work either:
function skipIncomplete(CompletableObjectInterface &...$objects): array {
$skipped = [];
foreach ($objects as $index => $item) {
if (!$item->isComplete()) {
$skipped[] = $item->id ?? $index;
unset($objects[$index]);
}
}
return $skipped;
}
The original elements passed in simply don't end up getting unset.
I'm looking for a way that doesn't include creating an entirely new Collection class to hold my CompletableObjects for complexity reasons. I really want to keep the type hint so no one can pass in a generic array, causing runtime errors when the function tries to call $item->isComplete.
Is there any way I can achieve this in PHP 7.3.15?
Added a filter, please comment as to what is wrong with this type of approach:
<?php
interface CompletableObjectInterface {
public function isComplete() : bool;
}
class Foo implements CompletableObjectInterface
{
public function isComplete() : bool
{
return false;
}
}
class Bar implements CompletableObjectInterface
{
public function isComplete() : bool
{
return true;
}
}
$foo = new Foo;
$bar = new Bar;
$incomplete = array_filter([$foo, $bar], function($obj) { return !$obj->isComplete();});
var_dump($incomplete);
Output:
array(1) {
[0]=>
object(Foo)#1 (0) {
}
}
Looks like you got a bit hung up on a wrong understanding of the ... syntax for a variable number of arguments.
You are passing in one array, and the $objects parameter will therefore contain that array in the first index, i.e. in $objects[0].
So in theory you could just change your line
unset($objects[$index]);
to
unset($objects[0][$index]);
However, I do not really see why the variable number of arguments syntax is used at all, since you apparently are just expecting one array of values (objects in this case) as an argument to the function. Therefore I'd recommend you just remove the ... from the argument list and your solution does what you wanted.
Alternatively you can of course add an outer foreach-loop and iterate over all passed "arrays of objects", if that is an use case for you.

Check for undefined PHP method before calling?

What can I use other than if(!empty( $product->a_funky_function() )) to check if the method is empty before calling it?
I've tried method_exists() and function_exists() and a whole plethora of conditions. I think the issue is that I need to have my $product variable there.
Please help.
A fairly common pattern is to have two methods on your class, along the lines of getField and hasField. The former returns the value, and the latter returns true or false depending whether or not the value is set (where "set" can mean not null, or not empty, or whatever else you might want it to mean).
An example:
class Foo
{
/** #var string */
private $field;
/**
* #return string
*/
public function getField()
{
return $this->field;
}
/**
* #return bool
*/
public function hasField()
{
return $this->getField() !== null;
}
}
This would then be used like:
if ($foo->hasField()) {
$field = $foo->getField();
...
}
Often, like in this example, the has... method often just delegates to the get... method internally, to save duplicating the logic. If the getter contains particularly heavy processing (e.g. a database lookup or API call), you might want to factor in ways to avoid performing it twice, but that's a bit out of scope.
You need absolutely to call it so it can compute the value which it will return, so I think there is no other way to know if it will return something not empty without calling it...
You have to call it.
$result = $product->getFunction();
if(!empty($result)) {
//your code here
}

With php finding out if variable is this exact class instance

I have run into trouble figuring out how to compare two variables which might contain the exact same class instance.
An abstract class (part of which is shown below) has a method fetch_mother() designed to identify the object that should contain it and return that or simply return itself because it is at the bottom of the stack. In theory, that stack should be no more than 5 deep.
Most instances represent things like categories.
With the get get_full_path() method:
Expected output was: [siteurl] /system/drafts/example-one/also-dev-notes/
Actual output was: [siteurl] /drafts/drafts/[snip]/drafts/drafts/example-one/also-dev-notes/
Which means that the sanity check kicks in and breaks a loop. It also means that I have not correctly tested for the returned object being the same as $this.
How can I confirm is $var===$this?
Code where the problem takes place:
<?php
namespace modules\content\classes;
use modules\core\interfaces as i;
use modules\core\classes as c;
abstract class content_object extends c\module_lib {
// vars
// ...
protected $mother;
protected $map
// ... code ...
public function get_object_map(){
return $this->map;
}
/**
* Get the stream holding this item
* #return \modules\content\classes\error|\modules\content\classes\content_object
*/
public function &fetch_mother(){
if(isset($this->mother) && is_object($this->mother)){
return $this->mother;
}
$mother = $this->module()->find_object_stream($this);
if(!($mother instanceof \modules\core\error) && is_object($mother) && $mother != $this){
$this->mother = $mother;
return $mother;
}else{
// I am my own mother ? \\
return $this;
}
}
protected function fetch_full_path_from_mother($path='',$sanity=10){
$map = $this->get_object_map();
$mother = $this->fetch_mother();
$path = $map . '/' . $path;
if($this==$mother || !is_object($mother) || $sanity<1){
return $path;
}
$sanity--;
return $mother->fetch_full_path_from_mother($path,$sanity);
}
public function get_full_path(){
$home = $this->get_core()->factory()->get_config('home');
return $home . $this->fetch_full_path_from_mother();
}
}
The answer here is non-obvious.
<?php
$foo = $this;
if($foo==$this){
echo 'It is';
}else{
echo 'It is not';
}
The output of the above would be It is.
That's because if the two objects are the same instance then the == comparison would be enough to determine this.
Likewise (as per the comments) spl_object_hash($mother)==spl_object_hash($this) is also true only if it is the same object. However, if another object with the same properties were created the above would be false because they are separate objects.
This question and answers deals with that exact same topic: spl_object_hash matches, objects not identical
The assumption in my question (which I did not see at first) is that the lookup function is acting as a factory and caching objects. The differential conclusion must be that a copy or second instance is being returned.
Thus, the problem must be with the fetch_mother() method.
(Further investigation did indeed show that this was the problem.)
Solutions include checking for matching properties (which in this case works as there are several unique fields pulled from the database) or comparing print_r output.
if(print_r($mother,true)==print_r($this,true)){
// code
}
That particular solution is ugly, inelegant and not very reliable.
A better solution would be to implement an object cache higher up the stack. (Which is what I will be proposing).
TL;DR: Objects with identical properties are still not the same.

PHP interfaces. Different return type

Should interfaces in PHP return the same types ?
My friend told me that it should return the same types.(for good practice)
I know that PHP is dynamic type language and it isn't possible.
For example:
interface idReturn
{
//Return INT
public function getById($id);
}
interface arrayReturn
{
//Return array
public function getByData($data);
}
Is it good practice or not?
It's not an interface thing. To avoid type checking in the consumers, a method should return one type only. It makes for easier to understand code due to the reduced number of execution pathes through the code.
For instance, this adds unnecessary complexity:
function get_users()
{
// some code returning array when users have been found
// or null when no users have been found
}
$users = get_users();
if (!is_null($users)) {
foreach ($users as $user) {
// do something with $user
}
}
If get_users() would simply return an empty array when no users have been found, you can simplify the consuming code to just read
foreach (get_users() as $user) {
// do something with $user
}
Also, by virtue of Liskov's Substitution Principle, any classes that are subtypes of a supertype need to be usable interchangeably in a consumer using the supertype. If the consumer expects an integer, you may not return a string since that might break the consumer. The same holds true for classes implementing an interface and consumers of that interface, e.g.
interface Contract
{
/** #return array */
public function fn();
}
Now assume A implements Contract and returns array, while B implements Contract and returns array or null. My consumer expecting an array will now break when B returns null:
function fn(Contract $instance)
{
// will break when B returns null
foreach ($instance->fn() as $foo) {
// do something with $foo
}
}
Since PHP cannot currently enforce a return type, it's up to the developer to indicate it in the DocBlock and to make sure the implementing or subtyped classes adhere to that return type.
With that said, if the contract defines multiple possible return types, the consumer has to make sure it can handle them. However, that again means undesirable type checking.
He probably meant this:
interface I {
public function f();
}
class A implements I {
public function f() {
return 1; // int
}
}
class B implements I {
public function f() {
return 'a'; // string
}
}
Whether or not you want this depends entirely on the contract you define for I::f. Contracts should be put in separate documentation because PHP lacks support for formally specifying them (this is not PHP-specific and it is the case in most languages, notable exceptions being D and Eiffel).

PHP callback creation internals & performance for lazy initialization

First, take a look at this PHP 5.5.8 code which implements lazy initialization of class properties with using a Trait:
trait Lazy
{
private $__lazilyLoaded = [];
protected function lazy($property, $initializer)
{
echo "Initializer in lazy() parameters has HASH = "
. spl_object_hash($initializer) . "\n";
if (!property_exists($this, $property)
|| !array_key_exists($property, $this->__lazilyLoaded))
{
echo "Initialization of property " . $property . "\n";
$this->__lazilyLoaded[$property] = true;
$this->$property = $initializer();
}
return $this->$property;
}
}
class Test
{
use Lazy;
private $x = 'uninitialized';
public function x()
{
return $this->lazy('x', function(){
return 'abc';
});
}
}
echo "<pre>";
$t = new Test;
echo $t->x() . "\n";
echo $t->x() . "\n";
echo "</pre>";
The output is as follow:
uninitialized
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
Initialization of property x
abc
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
abc
Here are my questions and things I'd like to discuss and improve, but I don't know how.
Based on the HASH values reported, it may appear that the initializer function is created only once.
But actually uniqueness is not guaranteed between objects that did not reside in memory simultaneously. So the question remains unanswered - whether the initializer gets created only once, and it matters for performance I think, but I'm not sure.
The way it's implemented now is not very safe in that if I refactor the code and change property $x to something else, I might forget to change the 'x' value as a first parameter to lazy() method. I'd be happy to use & $this->x instead as a first parameter, but then inside lazy() function I don't have a key to use for $__lazilyLoaded array to keep track of what has been initialized and what has not. How could I solve this problem? Using hash as a key isn't safe, nor it can be generated for callbacks like array($object, 'methodName')
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to access the raw $this->x property as it can be still uninitialized. So I wonder is there a better way - maybe I should save all the values in some Trait's field?
The global aim is to make it:
a) Fast - acceptable enough for small and medium software applications
b) Concise in syntax - as much as possible, to be used widely in the methods of the classes which utilize the Lazy trait.
c) Modular - it would be nice if objects still held their own properties; I don't like the idea of one super-global storage of lazily-initialized values.
Thank you for your help, ideas and hints!
So the question remains unanswered - whether the
initializer gets created only once, and it matters for performance I
think, but I'm not sure.
Well, closure instance is created only once. But anyway, performance will depend not on closure instance creation time (since it is insignificant), but closure execution time.
I'd be happy to use & $this->x instead as a first parameter, but then
inside lazy() function I don't have a key to use for $__lazilyLoaded
array to keep track of what has been initialized and what has not. How
could I solve this problem? Using hash as a key isn't safe, nor it can
be generated for callbacks like array($object, 'methodName')
I can propose the following solution:
<?php
trait Lazy
{
private $_lazyProperties = [];
private function getPropertyValue($propertyName) {
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
if(!isset($this->_propertyLoaders[$propertyName])) {
throw new Exception("Property $propertyName does not have loader!");
}
$propertyValue = $this->_propertyLoaders[$propertyName]();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
public function __call($methodName, $arguments) {
if(strpos($methodName, 'get') !== 0) {
throw new Exception("Method $methodName is not implemented!");
}
$propertyName = substr($methodName, 3);
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
$propertyInializerName = 'lazy' . $propertyName;
$propertyValue = $this->$propertyInializerName();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
}
/**
* #method getX()
**/
class Test
{
use Lazy;
protected function lazyX() {
echo("Initalizer called.\r\n");
return "X THE METHOD";
}
}
echo "<pre>";
$t = new Test;
echo $t->getX() . "\n";
echo $t->getX() . "\n";
echo "</pre>";
Result:
c:\Temp>php test.php
<pre>X THE METHOD
X THE METHOD
</pre>
c:\Temp>php test.php
<pre>Initalizer called.
X THE METHOD
X THE METHOD
</pre>
c:\Temp>
You cannot always be protected from forgetting something, but it is easier to remember when all things are close to each other. So, I propose to implement lazy loaders as methods on corresponding classes with specific names. To provide autocomplete #method annotation can be used. In a good IDE refactoring method name in annotation will allow to rename method across all project. Lazy loading function will be declared in the same class so renaming it also is not a problem.
By declaring a function with a name, starting with "lazy", in my example you both declare a corresponding accessor function, with name starting with "get" and it's lazy loader.
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to
access the raw $this->x property as it can be still uninitialized. So
I wonder is there a better way - maybe I should save all the values in
some Trait's field?
Trait fields are available in the class, that uses specific trait. Even private fields. Remember, this is composition, not inheritance. I think it's better to create private trait array field and store your lazy properties there. No need to create a new field for every property.
But I cannot say I like the whole scheme. Can you explain the use of it for you? May be we can come with better solution.

Categories