Show class methods type-hinting - php

I don't know how to phrase this one exactly but what I need is a way to load the type-hinting of a classes methods. Basically I have a base class that has a get function that looks like this:
class Base {
/**
*
* #param type $i
* #return \i
*/
public static function get($i) {
// make sure it exists before creating
$classes = get_declared_classes();
if (!in_array($i, $classes)) {
if (class_exists($i)) {
return new $i();
}
}
return $i;
}
}
Now for an example, say I had a class called test:
class test {
function derp() {
echo 'derp';
}
}
I'd instantiate the test object by something like this:
$test = base::get('test');
Now what I'd like to be able to do is as I type like this:
$test->
The methods (Currently only derp()) should be suggested, I've seen documents all around SO but they don't work :(
What's weird is that if I change the #return comment to the test class name then the suggestions work.
BUT it the classes are all not set, there could be different classes instantiated, hence why I tried #returns \i (suggested by netbeans). Is there any way to achieve this?
EDIT
The reason I need the type hinting is to allow for calling methods like the following:
base::get('test')->derp();

What always works is this:
/** #var ADDTYPEHERE $test */
$test = base::get('test');
What also works is this:
if ($test instanceof CLASS_IT_IS) {
// completion works in here
}
The solution you want can never work, since your IDE (netbeans) cannot know what class you instantiated, without any hint like one of the above.

Related

How can I create an array of mocks in PHPSpec?

I've just started using PHPSpec and I'm really enjoying it over PHPUnit, especially the no-effort mocks and stubs. Anyway, the method I'm trying to test expects an array of Cell objects. How can I tell PHPSpec to give me an array of mocks?
Simplified version of my class
<?php
namespace Mything;
class Row
{
/** #var Cell[] */
protected $cells;
/**
* #param Cell[] $cells
*/
public function __construct(array $cells)
{
$this->setCells($cells);
}
/**
* #param Cell[] $cells
* #return Row
*/
public function setCells(array $cells)
{
// validate that $cells only contains instances of Cell
$this->cells = $cells;
return $this;
}
}
Simplified version of my test
<?php
namespace spec\MyThing\Row;
use MyThing\Cell;
use PhpSpec\ObjectBehavior;
class RowSpec extends ObjectBehavior
{
function let()
{
// need to get an array of Cell objects
$this->beConstructedWith($cells);
}
function it_is_initializable()
{
$this->shouldHaveType('MyThing\Row');
}
// ...
}
I had hoped I could do the following, but it then complains that it can't find Cell[]. Using the FQN it complains about not being able to find \MyThing\Cell[].
/**
* #param Cell[] $cells
*/
function let($cells)
{
// need to get an array of Cell objects
$this->beConstructedWith($cells);
}
The only options I can work out is to pass multiple type-hinted Cell arguments and manually combine them into an array. Am I missing something simple?
Edit: I'm using PHPSpec 2.5.3 as, unfortunately the server is currently stuck at PHP 5.3 :-(
Why don't you do something like
use Prophecy\Prophet;
use Cell; // adapt it with PSR-4 and make it use correct class
class RowSpec extends ObjectBehavior
{
private $prophet;
private $cells = [];
function let()
{
$this->prophet = new Prophet();
for ($i = 0; $i < 10; $i++) {
$this->cells[] = $this->prophet->prophesize(Cell::class);
}
$this->beConstructedWith($cells);
}
// ....
function letGo()
{
$this->prophet->checkPredictions();
}
public function it_is_a_dummy_spec_method()
{
// use here your cells mocks with $this->cells
// and make predictions on them
}
}
Explanation
In let function you instantiate a Prophet object that is, basically a mocking library/framework that is used in tandem with PHPSpec (that itself use Prophecy).
I suggest to keep the instance ($this->prophet) as will be useful for next steps.
Now, you have to create your mocks, and you can do with prophet and prophesize.
Even for the mocks, I suggest to keep them into a private variable that you probably use for predictions in your methods.
letGo function is here to check explicitly the expectations you have made on cells: without, cells are only stubs or dummies.
Of course it's handy to pass through method signature a mock and to skip checkPredictions explicitly but, as soon as you need an array of mocks, I suppose that this is the only way to reach your goal.

PHP: Is there a way to get the return type of a method?

Given the following code structure is there a way that I can get the return type of FooFactory->createService method with PHP 5.6? I've tried ReflectionClass and ReflectionMethod classes but couldn't find a way.Thanks in advance.
class FooFactory implements FactoryInterface {
public function createService(/*args*/)
{
$foo = new Foo();
// ...
//inject dependencies
// ...
return $foo;
}
}
class Foo {
/*...methods...*/
}
Edit: I need to get the class name without creating an instance of the FooFactory.
Using PHP5 this is not possible.
You could (and this is more of a workaround than a solution) type hint your variable on declaration. This would expose the methods etc belonging to created service to your IDE.
Example:
/* #var \MyFooService $myFooService */
$myFooService = FooFactory->createServixce('MyFooService');
Note it is the #var declaration that will inform your editor of the variable type.
Syntax being:
/* #var [CLASS_NAME] [VARIABLE] */
UPDATE:
Just seen you dont want to create an instance of FooFactory.
By default arn't factory methods static?
So:
$fooFactory = new FooFactory();
$fooService = FooFactory->getService('FooService');
Becomes:
$fooService = FooFactory::getService('FooService');

How to indicate a parameter that "include trait" in PHPDoc

Recently I ran into an interesting situation when implementing a PHP application using PhpStorm. The following code snippet illustrates the problem.
interface I{
function foo();
}
trait T{
/**
* #return string
*/
public function getTraitMsg()
{
return "I am a trait";
}
}
class A implements I{
use T;
function foo(){}
}
class C implements I{
use T;
function foo(){}
}
class B {
/**
* #param I $input <===Is there anyway to specify that $input use T?
*/
public function doSomethingCool($input){ //An instance of "A" or "C"
$msg = $input -> getTraitMsg(); //Phpstorm freaks out here
}
}
My question is in the comment. How do I indicate that $input parameter implements I and uses T?
It's a lit bit hackly, but you can use class_uses it returns list of used traits. And add T as a #param type in PHPDoc for autocomplete
class B {
/**
* #param I|T $input <===Is there anyway to specify that $input use T?
*/
public function doSomethingCool($input){ //An instance of "A" or "C"
$uses = class_uses(get_class($input));
if (!empty($uses['T'])) {
echo $input->getTraitMsg(); //Phpstorm freaks out here
}
}
}
AFAIK you cannot type hint a trait usage in such way (#param only accepts scalar types or classes/interfaces + some keywords).
The ideal solution for you would be placing getTraitMsg() declaration into I interface.
If this cannot be done .. then you can specify that only instances of A or C can be passed here (as they utilize that trait):
/**
* #param A|C $input
*/
public function doSomethingCool($input)
{
$msg = $input->getTraitMsg(); // PhpStorm is good now
}
If names of such possible classes are unknown in advance (e.g. it's a library code and final classes could be anything in every new project or even added in current project at any time) .. then I suggest to use safeguards, which you should be using with such code anyway (via method_exists()):
/**
* #param I $input
*/
public function doSomethingCool($input)
{
if (method_exists($input, 'getTraitMsg')) {
$msg = $input->getTraitMsg(); // PhpStorm is good now
}
}
Why use safeguard? Because you may pass instance of another class K that implements I but does not use trait T. In such case code without guard will break.
Just to clarify: you could use #param I|T $input to specify that method expects instance that implements I or uses T .. but it's only works for PhpStorm (not sure about other IDEs) -- AFAIK it's not accepted by actual PHPDocumentor and does not seem to fit the PHPDoc proposed standard.
"//Phpstorm freaks out here" -- no, it's not. It just tries to signal you that your code is not correct.
The contract of method doSomethingCool() doesn't require $input to expose any method named getTraitMsg(). The docblock says it should implement interface I but the docblock is not code, it only helps PhpStorm help you with validations and suggestions.
Because you didn't type-hinted the argument $input, the code:
$b = new B();
$b->doSomethingCool(1);
is valid but it crashes as soon as it tries to execute the line $msg = $input -> getTraitMsg();.
If you want to call getTraitMsg() on $input you have to:
declare the type of $input;
make sure the declared type of $input exposes a method named getTraitMsg().
For the first step, your existing code of class B should read:
class B {
/**
* #param I $input
*/
public function doSomethingCool(I $input) {
$msg = $input -> getTraitMsg();
}
}
Please remark the type I in front of argument $input in the parameters list.
The easiest way to accomplish the next step is to declare method getTraitMsg() into the interface I:
interface I {
function foo();
function getTraitMsg();
}
Now, the code:
$b = new B();
$b->doSomethingCool(1);
throws an exception when it reaches the line $b->doSomethingCool(1); (i.e. before entering the function). It is the PHP's way to tell you the method is not invoked with the correct arguments. You have to pass it an object that implements the interface I, no matter if it is of type A or C. It can be of any other type that implements interface I and nobody cares if it uses trait T to implement it or not.

How do I stub this function in PHP

I have the following class I want to test:
<?php
namespace Freya\Component\PageBuilder\FieldHandler;
use Freya\Component\PageBuilder\FieldHandler\IFieldHandler;
/**
* Used to get a set of non empty, false or null fields.
*
* The core purpose is to use this class to determine that A) Advanced Custom Fields is installed
* and B) that we get back a set of fields for a child page.
*
* The cavete here is that this class requires you to have child pages that then have Custom Fields that
* you want to display on each of those pages. getting other page information such as content, title, featured image
* and other meta boxes is left ot the end developer.
*
* #see Freya\Component\PageBuilder\FieldHandler\IFieldHandler
*/
class FieldHandler implements IFieldHandler {
/**
* {#inheritdoc}
*
* #return bool
*/
public function checkForAFC() {
return function_exists("register_field_group");
}
/**
* {#inheritdoc}
*
* #param $childPageID - the id of the child page who may or may not have custom fields.
* #return mixed - Null or Array
*/
public function getFields($childPageId) {
$fieldsForThisPage = get_fields($childPageId);
if (is_array($fieldsForThisPage)) {
foreach ($fieldsForThisPage as $key => $value) {
if ($value === "" || $value === false || $value === null) {
unset($fieldsForThisPage[$key]);
}
}
return $fieldsForThisPage;
}
return null;
}
}
I can test all of this but one thing I want to do is stub the get_fields() function to say you will return this type of array to then be used how ever the rest of the function uses it, which in this case is looping through it.
The part I don't know how to do in php is stub a function that's being called and then say you will return x.
So how do I stub get_fields?
You cen define such function in global namespace. Take a look at the following example:
namespace {
function getFields($pageId) {
return array($pageId);
}
}
namespace MyNamespace {
class MyClass
{
public function foo(){
var_dump(getFields(5));
}
}
$obj = new MyClass();
$obj->foo();
}
And here is the output:
array(1) {
[0]=>
int(5)
}
The only issue is that this function will exist till end of script. To solve this problem you can use the tearDown method together with runkit library:
http://php.net/manual/en/function.runkit-function-remove.php
that allows you to remove user defined functions.
Unfortunatelly this library does not exist on Windows so there you won't be able to remove the definition and may consider running tests in isolation.
Edit:
You can also consider using this library (it also depends on runkit):
https://github.com/tcz/phpunit-mockfunction
You can use a trick here with the unqualified function name get_fields(). Since you don't use the fully qualified function name \get_fields() PHP will first try to find the function in the current namespace and then fall back to the global function name.
For the definition of qualified and unqualified, see: http://php.net/manual/en/language.namespaces.basics.php (it's similar to absolute and relative filenames)
So what you have to do is define the function in the namespace of the class, together with your test case, like this:
namespace Freya\Component\PageBuilder\FieldHandler;
function get_fields()
{
return ['X'];
}
class FieldHandlerTest extends \PHPUnit_Test_Case
{
...
}
Additional notes:
You can do the same with core functions, as described here: http://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/
This trick only works with functions, not classes. Classes in the global namespace always must be referenced with leading backslash.

In PHP, create object of an extending class from an instance of the base class

If I have a class Foo which extends class Bar, and an instance of Bar, is there anyway to use that instance of Bar to 'populate' a new instance of Foo?
Essentially, I am only able to retrieve Bar, but I want to use Foo in my code as it gives me lots of additional methods.
I saw quite a few solutions come up to similar questions but they all seemed to be python or c#.
I cant use ReflectionClass::newInstanceArgs as I cant access the data that went in to the constructor of Bar to create it in the first place.
The recommended way to accomplish this would be through dependency injection. Your constructor for Foo could accept an instance of Bar, and then you'd have to write the code to load the state of the new Foo object from the Bar object.
If PHP had a feature that does exactly what you describe, it would be problematic, because even if Foo extends Bar, the two classes could still be very different. I don't see how PHP could be smart enough to know how to automagically turn an instance of one class into an instance of another class.
With that said, under the correct circumstances, the (very hacky) "solution" below could do what you describe. I would not recommend actually using it. I mainly just wanted to show that you'd have to resort to some weird stuff to do this.
function convertObject($object, $newClass)
{
return unserialize(
preg_replace(
'/^O\:\d+\:"[^"]+"/',
'O:'.strlen($newClass).':"'.$newClass.'"',
serialize($object)
)
);
}
There is no built-in way to easily do what you want to. The interfaces of these classes must be redesigned a bit. Perhaps something like:
<?php
class Bar
{
...
}
class Foo extends Bar
{
public static function fromBar(Bar $bar)
{
$foo = new self();
... (copy data here) ...
return $foo;
}
}
I just wrote something this example is based on for extending custom drivers that may need additional support package(s) for the aforementioned core driver file.
// File: parent.php
class parent_class() {
protected $_protected_object; // set in constructor
protected $_protected_var = "Something dynamic";
public function __construct() {
// keep in mind that if you don't override this constructor, this will execute when extended
}
public function create_resource() {
$this->_protected_object = new SomeObjectInstance();
}
public function child_class() {
static $child = null;
if (is_null($child)) {
$file = "child.php";
if (!\file_exists($file)) {
throw new Exception("Couldn't load '$file', doesn't exist");
} else {
require_once($file);
$child = new child_class();
$child->__overload("_protected_object", $this->_protected_object)->__overload("_protected_var", $this->_protected_var);
}
}
return $child;
}
}
// File: child.php
class child_class extends parent_class {
protected function __overload($index, &$value) {
$this->$index =& $value;
return $this;
}
public function __construct() {
// REMEMBER: if you don't declare this method, the parent's constructor will execute
}
public function extended_func() {
// code
}
public function etc() {
// code
}
}
// Code instantiating objects:
$parent = new parent_class();
$parent->create_resource();
var_dump($parent);
/**
* Would output something like this:
*
* object(parent_class)#1 (2) {
* ["_protected_object":protected]=>
* object(SomeObjectInstance)#2 (*) {
* %OBJECT PROPERTIES%
* }
* ["_protected_var":protected]=>
* string(17) "Something dynamic"
* }
*/
$child = $parent->child_class();
var_dump($child);
/**
* Would output something like this:
*
* object(child_class)#3 (2) {
* ["_protected_object":protected]=>
* &object(SomeObjectInstance)#2 (*) {
* %OBJECT PROPERTIES%
* }
* ["_protected_var":protected]=>
* &string(17) "Something dynamic"
* }
*/
Notice that the second var_dump() for $child outputs the same information as $parent, except that you can now see said properties are preceded with ampersands (&), denoting that there are now references. So, if you change anything in the parent class instance in regard to those two properties, it will be reflected in the child class instance.

Categories