How to write a .phpstorm.meta.php file for CommandBus - php

I am using a command bus in my project and it works fine except for code completion.
In PhpStorm I always need to hint what will be the type of result from it.
So recently I decided to make a .phpstorm.meta.php file that will handle the code completion typehints for me.
This is what I made based on documentation I found here:
https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#map
<?php
declare(strict_types=1);
namespace PHPSTORM_META {
override(\Acme\BusBundle\Service\QueryBusWrapperInterface::dispatch(0), map([
\App\Query\Action\User\GetUserById => App\Model\User\UserInterface,
]));
}
For reference, here is how this command bus is being used:
class A
{
protected $queryBus;
public function __construct(QueryBusWrapperInterface $queryBus)
{
$this->queryBus = $queryBus;
}
public function foo()
{
$userId = 1; // Get it from somewhere
/** #var UserInterface $user */
$user = $this->queryBus->dispatch(new GetUserById($userId));
$user-> // PhpStorm should hint here
}
}
Problem is, that it does not seem to work at all. If I remove the #var comment PhpStorm no longer hints anything.
Could someone hint me towards what am I doing wrong.
Am I missing something in the meta file?

This isn't going to work, unfortunately.
You can :
Either use the argument type as the method return type directly (with type(0));
Or you can take the literal value of the argument (a string) and use map() to bind it a certain return type (like map(["myString" => \My\Other\Type::class])). Please note that both the key and the value are strings here.
What you can't do is to take the type of the argument (new GetUserById() in your case) and assign different return types depending on what type that is.
I couldn't find an existing request for that on our tracker, so maybe it's worth submitting one: https://youtrack.jetbrains.com/newIssue?project=WI

Related

Why doesn't PhpStorm code completion work on such a simple block of code?

I've noticed lately that code completion has been less effective in PhpStorm and I'm not sure if my settings are messed up or I'm just missing something.
Here's an example of what I'm trying to do:
class Database {
public function doStuff() {}
}
class DatabaseTest {
private $conn;
/**
* DatabaseTest constructor.
* #param $dbc
*/
public function __construct($dbc) {
$this->conn = $dbc;
}
public function test() {
$this->conn->
}
}
$dbc = new Database();
$databaseTest = new DatabaseTest($dbc);
The problem here is code completion in the test function will not work correctly
PhpStorm will not add types to the auto generated docblock for the constructor even though it should be able to deduce the type.
Even if the docblock isn't correct or the parameter isn't type hinted, shouldn't PhpStorm be able to determine the object type based upon the argument sent?
I realize that if I type hint the constructor parameter or the doc block I'll get the expected results but I feel like PhpStorm should have been able to figure this one out.
I'm using PhpStorm 2018.1.6
I'm sure I'm overlooking something and thank you in advance for your help.
The reason PHPStorm doesn't autocomplete in this case is because, given your code and the absence of type-hinting/typed #param in the constructor, there could be several Database instances where $this->conn is of different types.
Let's pretend you add this at the end of your sample code:
$dummy = new \stdClass;
$databaseTest2 = new DatabaseTest($dummy);
Now what should PHPStorm autocomplete $this->conn (within the class) as? It could be either a Database instance or a \stdClass, or just anything else really.
Edit: Well, guess it technically could parse all constructor calls and consider it as a Database|\stdClass|...|otherClasses, but then it would then also have to check all $this->conn assignments as well (since it can be of any type)... Doubt it'd be worth it (not to mention the CPU time it'd take if there's a lot of code).

PhpStorm not recognizing return type as extension of another

Scenario
I'm using PHP7 type hinting to say that my function has return type City.
public function getCityById(int $city_id) : City { ... }
In this function, I return the outcome of running a finder.
return $this->city_finder->findById($city_id);
But PhpStorm complains here, because the findById() function returns AbstractModel.
But I have class City extends AbstractModel, so this isn't a problem. PhpStorm doesn't seem to recognize this, however, and highlights the return statement with a warning.
I don't want to mute this type of warning (disable inspection), because it is an important warning to have.
Question
Can I make PhpStorm recognize that this return statement will satisfy the return type?
Additional info
One workaround is to extract a variable, and annotate it, like so:
/** #var City $city */
$city = $this->city_finder->findById($city_id);
return $city;
At this point, it stops warning me about it, but it seems like the extra line should be avoidable since it only exists to mute a warning in the IDE.
The findById() function is protected against returning the wrong type, because a Finder class is generated on a per-model basis.
$this->city_finder = $this->orm->getFinder(City::class);
//...
$city = $city_finder->findById(...);
PHPStorm is correct.
Your findById() returns AbstractModel and it is more broad that City that you're trying to return. In a case if you'll receive from findById() some other class that is also inherited from AbstractModel, but is not a City or its descendant - you will receive fatal error from PHP and it is what PHPStorm warns you about.
You can workaround this by either adding annotation, as you do already or by explicitly checking if ($city instanceof City) {return $city; }. It may look slightly bloated by will be safe in runtime.
PHPStorm is technically right here: according to the contracts of your functions, the statement might return the wrong value.
$result = $this->city_finder->findById($city_id);
The only thing we can know about $result here is what findById promises us, and that's that it's an AbstractModel. It might be a City, but it might equally be a User, or a Widget, or an anonymous object defined as class extends AbstractModel {}.
The underlying problem is that $this->city_finder is an instance of some abstract repository; while you know that it will always handle City objects, that's not actually built into your type system. If it was a more specific class, it could declare that it returned City objects from its findById method, and the code would be type safe.
Based on your comment that your ORM takes in the class name as a parameter ($finder = $orm->getFinder(City::class);), if PHP supported "generics" or "template meta-programming", you might write something like this:
class ORM {
public function getFinder<T>(): Finder<T> { ... }
}
class Finder<T> {
public function findById(int $id): T { ...}
}
$finder = $orm->getFinder<City>();
$city = $finder->findById($city_id);
The analyser would know that $finder was an instance of Finder<City>, and therefore that findById would return a City.
Without generics, though, there is no way to tell the analyser that if given City::class in its constructor, the Finder class will always return instances of that class.

CakePHP $this Syntax

I've been trying to learn CakePhp for a while now but I still can't get alot of stuff. I've been reading a lot and watching videos. I just want to ask a very simple question.
I've been trying to mess with the bookmarks tutorial and i'm watching a video. In the video he baked a component called Validate. In the cmd he typed
bin/cake/bake component Validate
Then a ValidateComponent.php appeared in the component folder in the controller. Now he used the ValidateComponent.php by going to the BookmarksController and adding to the initialize method
$this->loadComponent('Validate');
I just want to ask where did the word validate come from? shouldn't it be ValidateComponent? and where does he get the loadComponent from? I've seen him using $this->method(); or $this->method('string', [array]); I just want to know how the syntax works and what each word means. Sorry for the long explanation. I'm really trying to learn and i'm really confused. thank you very much.
ValidateComponent.php
<?php
namespace App\Controller\Component;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
/**
* Validate component
*/
class ValidateComponent extends Component
{
/**
* Default configuration.
*
* #var array
*/
protected $_defaultConfig = [];
public function validLimit($limit, $ default)
{
if (is_numeric($limit)){
return $limit;
}
return $default;
}
}
part of BookmarksController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Validate');
}
I can't seem to find where he got the word 'Validate'
Every controller in your application extends a base Controller Controller or AppController which extends Controller
Controller have bunch of methods, One of these methods is the loadComponent() (See Source)
public function loadComponent($name, array $config = [])
{
list(, $prop) = pluginSplit($name);
$this->{$prop} = $this->components()->load($name, $config);
}
Why Validate instead of ValidateComponent?
Short answer: suffix.
See predefined suffix in App class
CakePHP uses suffix to load classes, When you hit loadComponent() You go to ComponentRegistery class to Register the component, ComponentRegistery will call App class to load class. __loadClass()
Almost everything in CakePHP has a suffix, In your case ValidateComponent has the Component suffix.
return App::className($class, 'Controller/Component', 'Component'); (Source)
I hope this will make more sense to you
$this isn't specifically anything to-do with cake but part of PHP itself. In object oriented context $this refers simply to the current class.
$this->something refers to an object within the current scope. This could be within the current class or from an extends or use.
$this->something(); refers similarly to a method or function within the current scope.
If are using an IDE such as netbeans you can usually click through these references to see the object they refer to so for example if you do in fact use Netbeans you could ctrl-click on $this->loadComponent('Validate'); to see the actual function it refers to.
Regarding where does 'Validate' come from, it's a string you are passing to that object. On the other end it will be used in the function, probably in a switch or if statement to return something.
Eg:
Public function loadComponent($type){
If($type == 'Validation'){
//do something
}
}

Typehinting through Zend_Registry::get() - how to make navigation to declaration understandable to PhpStorm?

You can pass anything to Zend_Registry::set('myWidget', $someWidget), so that it's available later on.
However, when you retrieve it elsewhere, PhpStorm IDE has no clues to the type of 'myWidget'.
<?php
class AwesomeWidget {
public function doBazFlurgle() {
// doesn't matter what exactly happens here
return mt_rand();
}
}
?>
<?php
class FooController {
public function init() {
$someWidget = new AwesomeWidget();
Zend_Registry::set('awesome', $someWidget);
}
public function barAction() {
$this->init();
$awesome = Zend_Registry::get('awesome');
$awesomeNumber = $awesome->doBazFlurgle();
}
}
Navigate to declaration on the ->doBazFlurgle() call gets me a "Cannot find declaration to go to".
I could add a /** #var AwesomeWidget $awesome */ annotation, but that would require editing in many places in a sizable codebase
I could also add a return type annotation to Zend_Registry, but that does not look very maintainable (there are many instances of different classes stored this way).
I could search for the string doBazFlurgle through Find In Path..., but that is not entirely convenient (many keystrokes as opposed to a single Ctrl+click)
I noticed that NetBeans is capable of jumping to the method definition in this exact situation; is there a simple way of doing the same in PHPStorm without going through "search the entire codebase for doBazFlurgle"? I have searched available IDE actions, plugins, and fora; all in vain.
There is a way: as pointed out by #LazyOne, making a list of "what is returned from where" helps the IDE make sense of such code; this is somewhat documented on Jetbrains' website:
<?php
/** #link https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata */
// note that this is not valid PHP code, just a stub for the IDE
namespace PHPSTORM_META {
$STATIC_METHOD_TYPES = [
\Zend_Registry::get('') => [
'awesome' instanceof \AwesomeWidget, // this is the class as seen in the question
'fooSetting' instanceof \Zend_Config, // this came in from application settings
'quuxData' instanceof \ArrayAccess, // an arraylike object
]
];
}
Including this file (named .phpstorm.meta.php by convention) in the project has resolved the issue. This file is not valid PHP - PhpStorm only uses it for typehinting. That way, Zend_Registry::get('awesome')->doBazFlurgle() is correctly resolved as calling the method on an instance of \AwesomeWidget.
There is a workaround:
Position cursor into doBazFlurgle method call (click or cursor movement)
Select word (Ctrl+W)
Navigate to symbol (Ctrl+Alt+Shift+N)
method will be offered in dropdown
Select method (Enter)
Although this is not quite as clumsy as a generic string search, it is still not quite as convenient as the usual navigate to declaration.

How to know if a function is called first time or not

I want to create a function such that if it is called the first time, it behaves differently and for rest of the time it behaves differently. Now to do this I know I can use a "state" variable. Some other techniques were also given here:
Check if function has been called yet
However I somehow got a hint from a colleague that debug_backtrace() can be used to solve this problem. I read about it but cannot understand how ? This function gives a stack trace of the function call. How can this tell if the function has been called first time or not ?
The exact code that baffles me is here:
/**
* Holds the states of first timers
* #var array
*/
private static $firstTimeCSS=array();
private static $firstTimeJS=array();
/**
* Tells whether it is the first time this function is called on
* ANY CLASS object or not. Useful for one-time scripts and styles
*
* #param string $class name optional. Usually you should send __CLASS__ to this, otherwise the instance ($this) class would be used.
* #return boolean
*/
final protected function IsFirstTime($class=null)
{
$t=debug_backtrace();
if ($t[1]['function']=="JS")
$arr=&self::$firstTimeJS;
else
$arr=&self::$firstTimeCSS;
if ($class===null)
$class=$this->Class;
if (isset($arr[$class]))
return false;
else
{
$arr[$class]=true;
return true;
}
}
I personally don't see how this is possible or why you would want to do it this way. I suspect debug_backtrace() is a lot more expensive than a static variable, to begin with.
The only backtrace characteristic that seems to change between calls is, as you pointed out, the line number (from where the function was called). And, that wouldn't even change if you ran the functions in, say, a loop, since they would all be called from the same line on each iteration.
Demonstration 1 (individual calls): CodePad
Demonstration 2 (loop): CodePad
If I were you, I'd stick with a state variable; as for your colleague, you could perhaps ask him to show you a code which demonstrates his methodology if you're curious as to how it works (I know I am!).
Edit (from comments): Basically, your colleague's debug_backtrace() method stores a boolean value in an array using the key of the class which is called.
In plain English, here's what happens:
Is the calling function called "JS"?
If so, store in a JS-labelled array; otherwise, use a CSS-labelled array.
Check if a class was specified; if it wasn't, use this class.
If we have a boolean value for the given class in the labelled array, it's not the first time.
Otherwise, set the boolean value for the given class to true.
I know what you're thinking: This makes no sense, it doesn't even store the calling function's name! And you'd be right; this method is not extensible, and has a huge overhead.
If you want to do what this method does, just use a static variable in the class in question to keep track of whether or not functions have been called. Your colleague's method—sorry to say—is inefficient and ineffective.
Take A hidden input field and
<input type="hidden" id="start_function_count" value="0">
and then call a function
<li onclick="myFunction('start_function_count')">
js function
MyFunction(count_id) {
var function_count = $("#"+count_id).val();
if(function_count == 0){
// CODE HERE for 1st time function call
// SET HIDDEN FIELD
$("#"+count_id).val(1);
} else{
// SECOnd time code;
}
}
just use a static field in the function. This static field will only be initialized once and not overwritten on new function calls.
If you use this in class methods, do take care that each inherited child class will have it's own version. So a static function field updated in ParentClass won't update the static function field in ChildClass extends ParentClass.
See it in action https://ideone.com/iR7J5O
function beDifferentFirstTime()
{
static $firstTime = true;
if($firstTime) {
$firstTime = false;
echo "I will say this only once, so listen carefully\n";
}
echo "The cabbage is in the cart. I repeat, the cabbage is in the cart.\n";
}
beDifferentFirstTime();
beDifferentFirstTime();

Categories