mysqli misses some methods in new phpstorm eap 6

Hello,
since now this worked fine.
When $mysqlStatement was of type mysqli, i could use $mysqlStatement->execute() or $mysqlStatement->bind_param(.....) and so on, no errors.
Since the newest EAP 6 of phpstorm, all methods are marked as "Method 'execute' not found in class....".

Did I do a mistake, or am I missing something, or is that a new bug that hasn´t been there in the last version?
Thank you a lot.

8 comments

Hi Adrian,

Can you please provide some small code example so I can copy-paste to confirm the issue?

Quite possibly that there is an issue with a stub for specific method (which has incorrect @return parameter).

In any case: please try "File | Invalidate Caches..." in case you have not tried it yet.

1

Hi Andrji,
the following (parts) of code worked before without any errors (I left some things to make it smaller and cleaner for demonstration purposes). The errors appear since the newsest phpStorm EAP 6 version 126.92.



if (($mysqlStatement = $initVars->myDBC->sqliMain->prepare('INSERT INTO members_friendinvitations (member_invitor_uid, member_invited_uid, invitation_code1, invitation_code2, invitation_datetime) VALUES (?, ?, ?, ?, FROM_UNIXTIME(?))')) !== false) {
                $mysqlStatement->bind_param('iissi', $initVars->memberUid, $initVars->athleteUid, $invitationCode1, $invitationCode2, time());
                $mysqlStatement->execute();
                $mysqlStatement->close();
                $dbError = false;
            } else {
                $myagTools->sqliErrorHandler($initVars->myDBC->sqliMain, __METHOD__, __LINE__);
                $dbError = true;
            }


$initVars is an instance of a "agInit" class; it contains some of my most important variables for my project. I know that it is not best practice to call public variables of that class (e.g. myDBC, sqliMain), but it worked fine and was easy to code - and - it made no errors in phpStorm.

Right now I have to set  /** @var mysqli_stmt $mysqlStatement */ in front of the if.... then the errors under 'bind_param', 'execute', 'close' vanish. okay.
But still I have the error under prepare: method 'prepare' not found in class. But sqliMain is of type mysqli, and it is a public member of myDBC....

That worked before fine.

If I use this:
$myDBC->sqliMain->prepare
instead of
$initVars->myDBC->sqliMain->prepare

everything works fine.... hmmm.... it seems to me that phpStorm does not recognize the type of sqliMain if it is accessed through another class....





Thanks a lot.

Nachricht geändert durch Adrian Grund

0

Hi Adrian,

Since I do not have all the classes/variables involved in your example, I have tried to simplify it like below and  it works just fine (no warnings in this regard)

$m = new mysqli();
if (($mysqlStatement = $m->prepare('INSERT INTO members_friendinvitations (member_invitor_uid, member_invited_uid, invitation_code1, ...


screen01.png
I assume that "File | Invalidate Caches" did not helped you then (...

My only suggestion here then ... is to invoke Quick Documentation (Ctrl+Q) for each chain element and see what PhpStorm thinks about it (is return type is correct etc etc): $initVars->myDBC->sqliMain->prepare

If I use this:
$myDBC->sqliMain->prepare
instead of
$initVars->myDBC->sqliMain->prepare


Can you please provide a simplified version of these classes (only properties/methods that are used in this problematic example -- must contain any PHPDocs if you have any ect)

0

Thank you,
here is the relevant code:

class agInit
{

    public $myDBC = null;

    /**
     * Constructor
     *
     * @param string $brd brd
     *
     * @return void
     * @version SVN: $Id: agInit.class.php 342 2013-02-02 23:35:06Z svnadrian $
     */
    function agInit($brd)
    {

        include_once 'database/agDBConnectionsModel.php';

...
...

        $this->myDBC = new agDBConnections(...some variables for initiating db connection....);

    }




class agDBConnections
{

   public $sqliMain = null;

    /**
     * Constructor
     *
     * @param string $definitionsDbSERVER    the server name
     * and some more.......
     * @return void
     * @version SVN: $Id: agDBConnections.class.php 330 2012-09-25 06:50:19Z svnadrian $
     */
    public function agDBConnections(...some variables for initiiating db connection)
    {

        $this->sqliMain = new mysqli($agDbSERVER, $agDbUSER, $agDbPASS, $agDbDATABASE);
        $this->sqliMain->set_charset('utf8');

     }

}





in index.php:


$brd = '/';
$initVars = new agInit($brd);



$initVars->myDBC->sqliMain->prepare(.....)

That worked before.
When I do like in your example: $myDBC = new AGDBConnections(....)
and then: $myDBC->sqliMain->prepare(....) everything works fine.

So in my opinion the "transportation" of the type mysqli or something like that through the class agInit does not work anymore....

(sorry my english is so bad so I cannot dexribe the error better).

Thank you a lot! Adrian.
0

Here is the code after "cleaning up" (removing unrelated stuff). I have also added PHPDoc comments for $myDBC and $sqliMain fields:

<?php

// CLASSES



class agInit
{
    /** @var agDBConnections */
    public $myDBC = null;


    /**
     * Constructor
     *
     * @param string $brd brd
     */
    function agInit($brd)
    {
        $this->myDBC = new agDBConnections();
    }
}


class agDBConnections
{
    /** @var mysqli */
    public $sqliMain = null;


    /**
     * Constructor
     *
     * and some more.......
     */
    public function agDBConnections()
    {
        $this->sqliMain = new mysqli($agDbSERVER, $agDbUSER, $agDbPASS, $agDbDATABASE);
        $this->sqliMain->set_charset('utf8');
     }
}


// CODE
$brd = '/';
$initVars = new agInit($brd);


if (($mysqlStatement = $initVars->myDBC->sqliMain->prepare('INSERT INTO members_friendinvitations (member_invitor_uid, member_invited_uid, invitation_code1, invitation_code2, invitation_datetime) VALUES (?, ?, ?, ?, FROM_UNIXTIME(?))')) !== false) {
    $mysqlStatement->bind_param('iissi', $initVars->memberUid, $initVars->athleteUid, $invitationCode1, $invitationCode2, time());
    $mysqlStatement->execute();
    $mysqlStatement->close();
    $dbError = false;
}
else {
    $myagTools->sqliErrorHandler($initVars->myDBC->sqliMain, __METHOD__, __LINE__);
    $dbError = true;
}

  • If I put everything in single file, it works fine (no warnings)
  • If I put classes in one file and code in another -- I do see warnings.


Despite the fact that I have manually provided correct type for those fields, PhpStorm still adds |null (default value/type). This means that $myDBC in eyes of PhpStorm looks like /** @var agDBConnections|null */. Somehow, when declaration and usage in different files, PhpStorm looses / ignores one of the types.


Solution -- get rid of default null value, so PhpStorm has only ONE class to check against (null as a type has no (and cannot have) methods at all):

class agInit
{
    /** @var agDBConnections */
    public $myDBC;
....


class agDBConnections
{
    /** @var mysqli */
    public $sqliMain;

...


I'm pretty sure that I have seen more than one ticket for this, but I still would suggest to file new one to the Issue Tracker -- hopefully more tickets will tell devs that this incorrect behaviour needs to be fixed once and for all.

0

Andriy,
thanks a lot. You are the hero of the day and saved my time!
It works.
But there´s still one question. I only sent you a part of that class; I have more public variables that capsule other classes, and they work fine, though I init this variables at the beginning WITH null.... hmmm... but I can live with that.

Thanks a lot!

May I ask you one last question?
For database connections to mysql databases... what do you prefer? Do you save the database connection in a variable at the beginning of the script and pass this database connection variable as a parameter to the functions that need database access? Or do you use a static database class / singleton pattern where you can use that connection "global" in all functions?
I think the easier way is second, the "good practice" the first? What do you mean? Thank you a lot.

0
But there´s still one question. I only sent you a part of that class; I have more public variables that capsule other classes, and they work fine, though I init this variables at the beginning WITH null.... hmmm... but I can live with that.

No idea. As we can clearly see from this particular example .. PhpStorm has some issues here.

For database connections to mysql databases... what do you prefer? Do you save the database connection in a variable at the beginning of the script and pass this database connection variable as a parameter to the functions that need database access? Or do you use a static database class / singleton pattern where you can use that connection "global" in all functions?

It all depends on your application:

  • how it's written (what approaches / patterns are used)
  • if you plan to reuse this code in another app (is your app modular (so one module can easily be replaced by another one) or rock solid)
  • do you plan to cover your code with unit tests
  • do you support single DB engine (MySQL only) or multiple (SQLite, PgSQL, SQL Server etc)
  • do you plan to connect to different DBs (same engine) but on different servers (for example: convert something from one format to another; export / import etc)


If you plan to use unit testing, then Singleton is rather bad idea (you can google for opinions, e.g. http://googletesting.blogspot.co.uk/2008/11/clean-code-talks-global-state-and.html ; http://googletesting.blogspot.co.uk/2008/08/by-miko-hevery-so-you-join-new-project.html ). The same is if your app is modular -- Dependency Injections / Service Locator / Registry are better here. With Dependency Injection approach you can use fake DB connection to test your around-db code without making actual DB operations -- e.g. to test how it will behave in specific critical situations (e.g. missing field; no db connection; db with such name is not found etc).

Generally speaking, I would:

  • use PDO if I you plan to support more than one / change completely SQL engine.
  • Instead of passing DB connection details to a function, use something like Registry to retrieve DB connection (e.g. App::getDB() or Registry::get('DB') to get default/main DB connection , and  App::getDB('special') or Registry::get('DB/special') to get secondary/specific connection) .. or pass such DB connection info when creating such class (Dependency Injection), so that methods in that class will use that DB connection without asking outside static classes for more info). If I write the main app (or the unique code -- e.g. Product Search), then I would use 1st one; if it will be some module that I can later easily reuse in different project (on the same / similar framework, that may store DB connection differently -- e.g. export product feed into Froogle/Shopping.com format), I would use 2nd one.


There are advantages and disadvantages in each approach -- you need to choose the one that will suit you / required task the best. Using lots of classes / executing additional abstraction calls will make your code slower (compared to straight-forward and not-so-flexible approach) and will require more server resources. On another hand -- such code easier to modify/extend/test etc.

0

Andriy,
thanks a lot for your long, detailed and very clear answer. Seems that I see clearer now. More and more I learn, that there is no black and white in coding... it depends on the circumstances.
Best wishes, Adrian.

0

Please sign in to leave a comment.