How to tell PhpStorm about implementation details? (magic methods) - phpstorm

I have an object "User" that has attributes whose accessability is declared as protected but which can be set directly via a magic __set-method.
Now PhpStorm signals this apparent inconsistency with a big red column on the right side.
Is it possible to explain to PhpStorm what is going on so this is not shown as an error any more?
EDIT :
I use PhpStorm 2.1.4
okay here is some code that exemplifies the issue (together with the so far suggested workaround from Alexey which sadly doesn't do it for me):
c.php:
<?php
/**
* #property mixed $a
*/
class c1
{
protected $a;
public function __construct() { $this->a = __CLASS__; }
public function __get($n) { return $this->{$n}; }
}
/**
* #property $a mixed
*/
class c2
{
protected $a;
public function __construct() { $this->a = __CLASS__; }
public function __get($n) { return $this->{$n}; }
}
test.php
<?php
require "c.php";
$c1 = new c1();
var_dump($c1->a);
$c2 = new c2();
var_dump($c2->a);
and the output:
string 'c1' (length=2)
string 'c2' (length=2)
and how it looks like in PhpStorm:
my goal:
either having PhpStorm "understand" the design or just getting rid of those annoying red marks everywhere while not impairing the error detection apart from this issue.

This is now working in PHPStorm 3 :)
Unfortunately this is a open request in our tracker, see
http://youtrack.jetbrains.net/issue/WI-4468
The only way to avoid this warnings now, is to add #property to $user's class declaration. i.e.
/**
* #property $name string
*/
class User {
protected $name;
}
$user = new User();
$user->name = "me";

Related

Larvel Eloquent Model save function is not saving but not error

My problem at the moment is that I want to save some values to the database but the don't get saved and I don't get an error..
Both, either $product-save(); or $product-update(array(...)) are not working and I cannot tell why.. My ASIN Model looks fine and is filled with the right fillable attributes...
You guys know why it isn't working?
My Laravel Version: Laravel Framework 5.5.36
This is my class so far:
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\ASIN;
class CheckPrice extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'post:CheckPrice';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle() {
$product = ASIN::find(1410);
$product->price = "HELLO";
$product->amountSaved = "HELLO";
$product->percentageSaved = "HELLO";
$product->url = "HELLO";
$product->imgUrl = "HELLO";
$product->save();
//$product->update(array("price" => "HELLO", "amountSaved" => "HELLO", "percentageSaved" => "HELLO", "url" => "HELLO", "imgUrl" => "HELLO"));
$this->printProduct(ASIN::find(1410));
}
My ASIN Model so far:
namespace App;
use Illuminate\Database\Eloquent\Model;
class ASIN extends Model
{
protected $connection = 'facebook_amazon';
public $table = "ASINS";
protected $fillable = [
'ASIN',
'price',
'amountSaved',
'percentageSaved',
'category',
'url',
'imgUrl',
'showApp'
];
}
Kind regards and Thank You!
Use this in the handle methode
$product = App\ASIN::find(1410);
Or while impoting ASIN model use this if you want to keep the handle methode same
use App\ASIN as ASIN;
Use Laravel logs:
if(!$product->save()){
foreach($products->errors() as $error){
Log::info($error);
}
}
Hope this help.

PhpStorm no autocomplete when using getInstance method of a Class

Can somebody tell me why the autocompletion doesn't work when I'm using a getInstance() method instead of new ClassName?
Following is the getInstance() method of the class:
// Define The Namespace For Our Library
namespace JUnstoppable;
class JUnstoppable
{
// Instance Of The Class
protected static $instance = array ();
public static function getInstance ($forPlatformName = 'joomla')
{
$forPlatformName = strtolower($forPlatformName);
if (!isset(static::$instance[$forPlatformName]))
{
static::$instance[$forPlatformName] = new \JUnstoppable\JUnstoppable($forPlatformName);
}
return static::$instance[$forPlatformName];
}
public function __construct ($platformName = 'joomla')
{
}
public function doExecute ($test = 'lalala')
{
return $test;
}
}
Can somebody tell me why the autocompletion doesn't work when I'm using a getInstance() method instead of new ClassName?
That's because IDE does not know what can be inside your $instance static property and therefore it cannot figure out what getInstance() returns. From IDE point of view it's just plain array (elements of any types) and not an array of JUnstoppable instances.
You can place caret on $test and invoke View | Quick Documentation to see what IDE knows about that variable. If it does not say JUnstoppable there then no wonders.
Just add proper type hint for return value of getInstance() method via PHPDoc's #return tag:
/**
* My super method.
*
* #param string $forPlatformName Optional parameter description
* #return JUnstoppable
*/
public static function getInstance ($forPlatformName = 'joomla')
You can specify concrete class (JUnstoppable in this case) .. or static if this method will be used by child classes as well and they will return different instances.
Alternatively (or better say: in addition) you can typehint $instance property which IDE will use to figure out what getInstance() method returns:
/** #var JUnstoppable[] Instance Of The Class */
protected static $instance = array ();

Is it possible for PhpStorm to infer the return type of this method?

I imagine I might need to add a special annotation somewhere, but I'm crossing my fingers that PhpStorm is smart enough to resolve return types given awkward inheritance patterns.
For example, I have some code that resembles this:
<?php
class Collection extends \ArrayObject
{
public function __construct(array $items)
{
foreach ($items as $key => $value) {
if (isset(static::$requiredType) && !$item instanceof static::$requiredType)
$this->offsetSet($key, $value);
}
}
public function getFirst()
{
return $this->offsetGet(0);
}
}
class MessageCollection extends Collection
{
protected static $requiredType = 'Message';
}
class UserCollection extends Collection
{
protected static $requiredType = 'User';
}
I'd like it if when I call UserCollection::getFirst() it inferred that a User was returned, while when I call MessageCollection::getFirst() it inferred that a Message was returned. Is there some annotation I could put in somewhere to achieve this result?
My first thought was something like this:
/**
* #return Message|User|XXXX|YYYY|ZZZZ|AAAA|BBBB|CCCC|DDDD
*/
public function getFirst()
{
return $this->offsetGet(0);
}
but I imagine that would get a little ridiculous to the point of being useless as I add more collection classes.
Try this:
/**
* #method \User getFirst()
*/
class UserCollection extends Collection
{
protected static $requiredType = 'User';
}

How to Extend Yii2-user dektrium profile model to be able to adding more fields

I need to override the default Profile model. I have managed to add the fields i need but there is something i am missing since. On insert and update these fields are not getting update to the database.
I have created the necessary migrations so i have these fields in the database already
What am i missing> see below my app/models/Profile.php
<?php
namespace app\models;
/**
* Description Profile
*
* This form #overrides dektrium\user\models\Profile
*/
use dektrium\user\models\Profile as BaseProfile;
use yii\web\UploadedFile;
use Yii;
use dektrium\user\models\User;
class Profile extends BaseProfile {
/**
* public variables to be added to the model
*/
public $profile_pic;
public $expertise_id;
public $country_id;
public function rules() {
$rules = parent::rules();
$rules['profile_pic'] = ['profile_pic', 'file'];
$rules['expertise_id'] = ['expertise_id', 'integer'];
$rules['country_id'] = ['country_id', 'integer'];
return $rules;
}
/**
* #inheritdoc
*/
public function attributeLabels() {
$labels = parent::attributeLabels();
$labels['profile_pic'] = \Yii::t('user', 'Profile Picture');
$labels['bio'] = \Yii::t('user', 'Biography');
$labels['expertise_id'] = \Yii::t('user', 'Expertise');
$labels['country_id'] = \Yii::t('user', 'Country');
return $labels;
}
}
First thing, remove this lines:
public $profile_pic;
public $expertise_id;
public $country_id;
If you already added those fields in the table, you dont need to declare them. As you can see, none of the others properties are being declared either. This is already being done by extending the model from ActiveRecord and declaring the tableName

Yii2 missing required parameter in a constructor

I've created a new XmlResponseFormatter and now I want to change the rootTag.
class newXmlResponseFormatter extends XmlResponseFormatter
{
/**
* #var string the name of the root element.
*
*/
public $rootTag;
public function __construct($rootTag) {
parent::__construct();
$this->rootTag = $rootTag;
}
}
From a controller I set that value:
$xmlFormater = new newXmlResponseFormatter('newRootTag');
In the controller that value is available, and it sets in $rootTag but it threw the following exception:
exception 'yii\base\InvalidConfigException' with message 'Missing required parameter "rootTag" when instantiating "app\components\override\newXmlResponseFormatter".' in /var/www/html/Admin/vendor/yiisoft/yii2/di/Container.php:451
Does anyone know what can be a problem?
Thanks in advance!
First parameter in XmlResponseFormatter is $config, because XmlResponseFormatter extends Object class. You are violated liskov substitution principle.
You should rewrite your constructor like this:
class newXmlResponseFormatter extends XmlResponseFormatter
{
/**
* #var string the name of the root element.
*
*/
public $rootTag;
/**
* newXmlResponseFormatter constructor.
*
* #param string $rootTag
* #param array $config
*/
public function __construct($rootTag, $config = [])
{
$this->rootTag = $rootTag;
parent::__construct($config);
}
}
In yii2 you should call parent constructor after your code, and call parent init before your code.
$config need for simple configure model like this:
new newXmlResponseFormatter(['rootTag' => 'newRootTag']);