What are these mysterious things in defining methods? - php

Back to development after spending some years in a management position, I am dealing with a PHP code, which has some definitions that I cannot understand (looks like I am far beyond of PHP progress on these years). Can someone let me know what campaignDTO and ParamDTO do in this definition?
What will be returned from this method?
/**
* Creates a campaign
* #param campaignDTO $campaign
* #param ParamDTO $param
* #throws \Exception
* #return campaignDTO
*/
public function createCampaign(campaignDTO $campaign, ParamDTO $param)
{
}

Type declarations as per docs:
Type declarations allow functions to require that parameters are of a
certain type at call time. If the given value is of the incorrect
type, then an error is generated: in PHP 5, this will be a recoverable
fatal error, while PHP 7 will throw a TypeError exception.

These are type-hints for run-time validation. It tells the code to expect objects of class type campaignDTO and ParamDTO, or a class that extends from these.
If you pass in an array, or a string, or something that is not a class that is or extends capaignDTO then the code will throw an error.
The function, as it is, returns nothing.
According to the code-comment, it will return an object of type campaignDTO, which looks like the first parameter.

Related

Which PHP exception to throw when last method in chain requires other method called first

I am writing a PHP query builder-style library that uses method chaining to add constraints to the query with a final call to get() at the end to execute the request, similar to Laravel's query builder, but using a public JSON API via Guzzle for the data. For example:
// valid
Foo::queryType()->take(5)->skip(50)->get();
// invalid, missing query type
Foo::take->(5)->get();
Foo is just a facade for accessing the library. queryType() is a required initial method (can be one of many, e.g. queryTypeA(), queryTypeB()) that sets a protected class variable in Foo. I want to throw an Exception if get() is called without that variable being set first. And I want to be precise in what gets thrown.
PHP's documentation lists a few options:
BadMethodCallException:
Exception thrown if a callback refers to an undefined method or if some arguments are missing.
Probably doesn't refer to missing prerequisites, and I get the feeling this is intended more for dynamic calls where the method doesn't exist.
BadFunctionCallException
Exception thrown if a callback refers to an undefined function or if some arguments are missing.
Basically same as before but for general functions, and BadMethodCallException is actually a child class of this.
InvalidArgumentException
Exception thrown if an argument is not of the expected type.
While not technically an argument, an unset protected class variable required to continue execution fits the general idea, so this one makes the most sense so far.
UnexpectedValueException
Exception thrown if a value does not match with a set of values. Typically this happens when a function calls another function and expects the return value to be of a certain type or value not including arithmetic or buffer related errors.
Not really an option; while the name implies that I might want the opposite of this - I expect a value to be set and it's not present - the description seems less of a match than others, and there's no user contributed notes to help refine.
RuntimeException
Exception thrown if an error which can only be found on runtime occurs.
My fallback. Maybe I'm overthinking this? Which PHP exception makes the most sense?
Based on the design of my library, I ended up going with BadMethodCallException. Since the outer class was simply a facade, I used the __call magic method to pass through any undefined method calls to the internal class variable.
After passing through the call, the internal class variable is set to null so as to literally undefine the method call. However so as to distinguish between undefined methods and out of order chaining, I explicitly check the class variable for null and provide the specific error.
protected $request = null;
/**
* Pass through non-explicitly defined method calls to the internal request class
* #param string $function
* #param array $args
* #return mixed
* #throws \BadMethodCallException
*/
public function __call($function, $args)
{
if ($this->request === null) {
throw new \BadMethodCallException('Must specify request type before chaining constraints.');
}
if (method_exists($this->request, $function)) {
$result = $this->request->$function(sizeof($args) ? $args[0] : null);
$this->request = null;
return $result;
}
throw new \BadMethodCallException('Method \'' . $function . '()\' does not exist.');
}

PHPDoc type hinting overlapping types

I have a class called Resource but with a fully qualified name like com.example/objects/Resource
If I write a file
use com.example/objects/Resource;
/**
* Do something
*
* #param Resource $r
*/
function myfunc( Resource $r ) {
$r->something();
}
$x = new Resource();
myfunc($x);
Then everything works fine. Because of my use statement, the PHP typehinting is able to handle the the fact that I've passed a variable of type com.example/objects/Resource even though myfunc is only comparing against Resource
The problem is that PHPStorm is not able to handle this. I'm unable to use autocomplete and I get a warning on myfunc($x) which says Expected Resource, got Resource and a warning within the function which says Method 'something' not found in the class Resource. Obviously PHPStorm is assuming I'm using the builtin resource class and not my own Resource class.
If I change the PHPDoc and the function definition to use the fully qualified name, then the previous warnings go away but I get a minor warning which says Unnecessary fully qualified name. I suppose one solution would be to use the fully qualified name and disable the minor warning, but I'd rather not have to use fully qualified names everywhere. I know it's my own fault for creating a class which has the same name as a built in type, but I'm wondering if there is anyway to make this work? Apart from renaming my Resource class?
You can either use an alias when importing the class. More information can be found here. Example:
use com.example\objects\Resource as MyResource;
/**
* Do something
*
* #param MyResource $r
*/
function myfunc( MyResource $r ) {
$r->something();
}
$x = new MyResource();
myfunc($x);
You could also specify the whole namespace and class name instead of just the class name. Example:
/**
* Do something
*
* #param com.example\objects\Resource $r
*/
function myfunc( com.example\objects\Resource $r ) {
$r->something();
}
$x = new com.example\objects\Resource();
myfunc($x);
If you're in a namespace and using this approach, make sure to use a leading \ on your full class name specifier (e.g. $x = new \com.example\objects\Resource();).

Is there an array-to-callable function?

Setting
Within SomeClass there is f2member taking two integer arguments and producing its sum. The test passes showing that the call actualy works and retrieves the expected result. Which is calling $gwith two parameters 1 and 1 returning 2.
Important: This works only for php 5.4.11 and upwards compatibility check
class SomeClass extends PHPUnit_Framework_TestCase
{
function f2member($a,$b)
{
return $a + $b;
}
/**
* #test
*/
public function test()
{
$g = array($this,'f2member');
$this->assertEquals(2, $g(1,1)); // squiggle line under $g
}
}
Problem
However, this produces a warning inside phpStorm on every method invocation and a squiggle line under $g:
Function name must be callable - string, Closure or class implementing __invoke, currently array
The origin of the warning is clear to me and now i am looking for ways to avoid these warnings.
A requirement is, that i dont want to change the style of calling the function. Another thing i don't want to do is to deactivate this warning.
I would rather prefer to wrap something around it, which provides the necessary information to the type system.
Attempt
I already encountered several solutions to remove the warnings. One is to define a user defined function, which only documents the required target type.
/**
* #param array $arr
*
* #return callable
*/
function callable_for($arr)
{
return $arr;
}
This returns an array, but also explicitly tells the type system what comes out of the callable_for function. With this type annotation in place phpStorm now stops complaining about this warning, although it still returns an array.
$g = callable_for(array($this,'f2member'));
Question
Isn't there something out of the box like my callable_for in php to achieve this? If the answer is no, then
i am looking for the most concise solution we can find.
I already tried looking on SO, php.net and google. Maybe, I just searched for the wrong word combinations, here are just two samples:
array to callable php
create callable method handle php
BigPicture
Just in case suspects arise this is a X/Y problem: I have another function taking a callable as a parameter. With closures it is very natural to define something, which can be invoked later on. However, how do i define a callable for a member or a static method without wrapping it in another delegation Closure? The array notation allows to be used to uniformly pass: closures or static/member method handles to my later function. I am now trying to find a concise solution to this, which comes close to this.
Thus, another advancement could be, to modify callable_for to take two arguments and wrap both responsibilities; to create the array and to document the target return type.
/**
* #param mixed $context
* #param string $method
*
* #return callable
*/
function callable_for($context, $method)
{
return array($context, $method);
}
Using this implementation raises the conciseness of $g's declaration to an acceptable level.
$g = callable_for($this,'f2member');
This function still returns an array, but the type system can use the given information to correctly treat this array for dynamic method invocation.
The question was asked four years ago but after some research I found the Closure::fromCallable method: https://www.php.net/manual/de/closure.fromcallable.php

PhP documentator document return object types

i have a \Zend\ServiceManager\ServiceManager instance in my code and this function is being returned from a function wich is using the get function from it.
like:
$serviceManager->get('webuser')->IntelSenseHere()
$serviceManager->get('email')->IntelSenseHereLOL()
$serviceManager->get('horses')->IntelSenseHereHEEERE()
Now all thouse classes can be returned from this one call... How could i document what the function call is calling?
Because i have intel sense helper from nb 7.4 rc2 and it is pretty clever and reacts to the return document variable, i would like to specify what adapters it can return.
Is there a way to specify the classes it cann return??
like:
/**
*
* #param \Zend\ServiceManager\ServiceManager $serviceManager
* #param array $user
* #return boolean, \Zend\ServiceManager\ServiceManager, horseAdapter...
*/
Is there a correct syntax for that?
Using the ServiceManager::get($service) will return the instance of the service you requested.
So, using one of your examples:
$serviceManager->get('webuser')->doSomething();
Is the same as:
$webUserService = $serviceManager->get('websuer');
$webUserService->doSomething();
You should therefore add the DocBlock annotation #return to the webUser::doSomething() method, not the ServiceManager::get()
The code completion will depend entirely on your IDE, however I believe this will work in Netbeans.

PHP instanceof over strings and non-initializable classes

I need to check whether a certain class extends or implements a particular interface.
Note that the class name is a variable string, ie, there won't be any instance of this class.
Users are supposed to select a class from a list of classes and the system is supposed to check whether the class implements a certain interface or not. The list of classes is variable (according to the PHP software currently running), some of these classes can be initialized and others cannot.
Here's the code I'm using:
function is_instance_of($obj,$cls){
if(is_string($obj))$obj=new $obj();
if(PHP_MAJOR_VERSION>4)return $obj instanceof $cls;
if(PHP_MAJOR_VERSION>3)return is_a($obj,strtolower($cls));
return false;
}
var_dump(is_instance_of('MyClass','IMyInterface')); // in theory, true
var_dump(is_instance_of('Closure','IMyInterface')); // FATAL ERROR
That last test shows up the following error:
Catchable fatal error: Instantiation of 'Closure' is not allowed in C:\Users\abcdefghijklmn\debug.php on line XX
Things I tried:
Using $obj=new #$obj(); :- error is hidden but it still faults/dies.
Using try{}catch(){} around offending block :- nothing different happens
Using 'class' instanceof 'class' (where $obj is a string) :- returns false unconditionally
Please note that the mandatory class initialization used in this method...sucks. Creating instances means unnecessary memory consumption, loss in speed and more prone to errors (imagine some weirdo class that when instantiated without parameters it proceeds to destroy your HDD ;) ).
So, if there's any other way, I'd simply love to know about it.
Edit: This is (hopefully) the final code:-
/**
* Cross-version instanceof replacement.
* #param object $obj The instance to check.
* #param stirng $cls The class/interface name to look for.
* #return boolean Whether an object extends or implements a particular class
* or interface.
* #link http://stackoverflow.com/questions/4365567/php-instanceof-over-strings-and-non-initializable-classes
*/
function is_instance_of($obj,$cls){
if(is_string($obj) || PHP_MAJOR_VERSION>4){
$rc=new ReflectionClass($obj);
return $rc->implementsInterface($cls);
}else{
if(PHP_MAJOR_VERSION>3)return is_a($obj,strtolower($cls));
return false;
}
}
Try using PHP's ReflectionClass instead, e.g.
$rc = new ReflectionClass($obj);
return $rc->implementsInterface($cls);
Use the ReflectionClass:
function is_instance_of($obj,$cls){
$ref=new ReflectionClass($obj);
return in_array($cls, array_keys($ref->getInterfaces());
}

Categories