phplaraveleloquentphpstan

Phpstan Call to an undefined method Illuminate\Database\Eloquent\Builder::join()


My php version is 7.1.31 and laravel version is 4.2. And i want to have phpstan as my static analysis tool. But when i run it. it outputs so many errors.

example are:

  1. Call to an undefined method Illuminate\Database\Eloquent\Builder::join()
  2. Call to an undefined method Illuminate\Database\Eloquent\Builder::select()
  3. Call to an undefined static method \Post::where()
  4. Call to an undefined static method Illuminate\Support\Facades\Route::get().
  5. Call to an undefined static method Illuminate\Support\Facades\Route::post().

sample code

$results = Post::join('users', 'posts.user_id', '=', 'users.id')
          ->select('users.name', 'users.email', 'posts.title')
          ->get(); 

this is my sample config file:

parameters:

    paths:
        - application

    level: 8


Solution

  • The real issue is you're using Laravel 4.2. In the current laravel version (11.x at the time of writing this), the code would not trip PHPStan because the Eloquent Builder implements the following interface.

    <?php
    
    namespace Illuminate\Contracts\Database\Query;
    
    /**
     * This interface is intentionally empty and exists to improve IDE support.
     *
     * @mixin \Illuminate\Database\Query\Builder
     */
    interface Builder
    {
    }
    

    In laravel 4.2, the Eloquent Builder does not implement an interface, or extend the base Builder.

    Every method not found in the Eloquent Builder is sought in the defined macros, and then delegated to the base Builder (which is a property)

    For comparison:

    laravel 4.2's Eloquent Builder

    <?php
    namespace Illuminate\Database\Eloquent;
    
    ...
    
    class Builder
    {
        /**
         * The base query builder instance.
         *
         * @var \Illuminate\Database\Query\Builder
         */
        protected $query;
    
        ...
    
        /**
         * Dynamically handle calls into the query instance.
         *
         * @param  string  $method
         * @param  array   $parameters
         * @return mixed
         */
        public function __call($method, $parameters)
        {
            if (isset($this->macros[$method]))
            {
                array_unshift($parameters, $this);
    
                return call_user_func_array($this->macros[$method], $parameters);
            }
            elseif (method_exists($this->model, $scope = 'scope'.ucfirst($method)))
            {
                return $this->callScope($scope, $parameters);
            }
    
            $result = call_user_func_array(array($this->query, $method), $parameters);
    
            return in_array($method, $this->passthru) ? $result : $this;
        }
    
        ...
    }
    

    laravel 11.x's Eloquent Builder

    <?php
    
    namespace Illuminate\Database\Eloquent;
    
    ...
    use Illuminate\Contracts\Database\Eloquent\Builder as BuilderContract;
    ...
    
    /**
     * @template TModel of \Illuminate\Database\Eloquent\Model
     *
     * @property-read HigherOrderBuilderProxy $orWhere
     * @property-read HigherOrderBuilderProxy $whereNot
     * @property-read HigherOrderBuilderProxy $orWhereNot
     *
     * @mixin \Illuminate\Database\Query\Builder
     */
    class Builder implements BuilderContract
    {
        ...
    
        /**
         * The base query builder instance.
         *
         * @var \Illuminate\Database\Query\Builder
         */
        protected $query;
    
        ...
    }
    

    In fact it even adds the @mixin annotation again.


    A quick and dirty fix you could do would be to edit the vendor class to add the /** * @mixin \Illuminate\Database\Query\Builder */ annotation on top of the Illuminate\Database\Eloquent\Query\Builder class. Of course, if you update your dependencies and the laravel version changes (or you (re)install them, this line will need to get re-added.

    Editing vendor code should be avoided, but

    1. This is a phpdoc block and carries no actual functionality.
    2. If your project has laravel 4.2, I don't think you're updating dependencies all that often enough for this to break.