Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak with Laravel IDE helper loaded #3836

Closed
genesiscz opened this issue Jul 30, 2020 · 12 comments
Closed

Memory leak with Laravel IDE helper loaded #3836

genesiscz opened this issue Jul 30, 2020 · 12 comments

Comments

@genesiscz
Copy link
Contributor

genesiscz commented Jul 30, 2020

Bug Report

Subject Details
Rector version dev-master
Installed as composer dependency

So I am using Laravel IDE helper to have better suggestions in IDE. There's a _ide_loader.php file which has some classes which I then include as @mixin in PHPDoc of Models.

When I try to run rector on such code

<?php

namespace App\Services;

use App\Models\Cart;
use Auth;
use Session;


class CartService
{
    /**
     * @return Cart
     */
    function initNewCart()
    {
        $cart = new Cart();
        $cart->price = 0.0; // this line causes it
        return $cart;
    }
}

It bloats the memory over 4G and causes a segmentation fault because lack of memory available.

The underlying property price doesn't exist, it's only as a @property float $price and is available via Laravel's Model magic. The problem is that BaseModel, which I am extending in Cart class, is defined on two places: that generated ide helper & actual BaseModel class. If I load the ide helper file, it goes into fire.

image

image

When I don't autoload that file (which I should not ever load, it's only for ide's knowledge), it screams about the @mixin Eloquent I have in some of my models:

/**
 * @property int $id
 * ...
 * @mixin Eloquent
 */
class Store extends BaseModel {

}
 [ERROR] Could not process "app/Services/StoreResolver.php" file, due to:                                               
         "Analyze error: "Class Eloquent not found.". Include your files in "parameters > autoload_paths".              
         See https://github.com/rectorphp/rector#extra-autoloading".                                                    
    
@genesiscz
Copy link
Contributor Author

Is there a way I could explicitly disable checking @mixins?

@TomasVotruba
Copy link
Member

It might be something in Cart. If you replace Cart with some dummy empty class, does the problem remain?

Is there a way I could explicitly disable checking @Mixins?

Not sure what does it mean, I never used Laravel. Could you share reproducible minimal code so we can see error ourselves?

@TomasVotruba
Copy link
Member

Not sure what IDE helper is, but if it makes mess, you can exclude it.

@genesiscz
Copy link
Contributor Author

genesiscz commented Jul 30, 2020

It might be something in Cart. If you replace Cart with some dummy empty class, does the problem remain?
As soon as I remove the line I highlighted with comment, it works properly. IDE helper is simply recreating all the classes that have magic methods from Laravel and adds extra typehints, return types and more useful things. That's not necessary to underestand tho.

The problem is that rector, or some part of rector, is analyzing content DOCblock, finding @mixin Eloquent and tries to autoload that class, which is present only for IDE to recognize more methods because they are being called magically making it unable for IDE to hint it without the helper. It's not meant to be loaded, autoloaded, by PHP itself. Rector is trying to load it, which means autoload, the file, and is unable to continue without it. That file alone has 22 000 lines of code which can be a part of the reason why it uses so much memory.

@TomasVotruba
Copy link
Member

Hm, black magic strikes again.

What do you suggest to keep PHPStan/Rector working on other projects but not get stuck on your code?

@genesiscz
Copy link
Contributor Author

I am digging deeper to narrow it down to the smallest possible example I can make.

@genesiscz
Copy link
Contributor Author

genesiscz commented Jul 30, 2020

So here are things that worked out:

_ide_helper.php is now narrowed down to

<?php

    class Eloquent extends \Illuminate\Database\Eloquent\Model {

        }

Model.php looks like this at the top:

/**
 * 
 * @mixin Eloquent
 * @mixin \Illuminate\Database\Eloquent\Builder
 * @mixin \Illuminate\Database\Query\Builder
 */
abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
  • If I remove extends from Eloquent class, it works
  • If I remove @mixin from Model, it works
  • If I remove $cart->price = 0.0; from the provided example, it works
  • If I add @mixin in Cart, it works.

I thought it's some sort of infinite loop because class that Model is using @mixin and that Mixin is extending Model. But that's not the case if I try it in isolated environment.

Then I went to try to narrow down the part of code of rector causing this.

PHPStanNodeScopeResolver.php - I added these echoes

      $nodeCallback = function (Node $node, MutatingScope $scope) use ($smartFileInfo): void {
            echo "A";
            // the class reflection is resolved AFTER entering to class node
            // so we need to get it from the first after this one
            if ($node instanceof Class_ || $node instanceof Interface_) {
                $scope = $this->resolveClassOrInterfaceScope($node, $scope);
            }
echo "b";
            // traversing trait inside class that is using it scope (from referenced) - the trait traversed by Rector is different (directly from parsed file)
            if ($scope->isInTrait()) {
                $traitName = $scope->getTraitReflection()->getName();
                $this->traitNodeScopeCollector->addForTraitAndNode($traitName, $node, $scope);

                return;
            }
            echo $smartFileInfo->getFilename();
echo "c";
            // special case for unreachable nodes
            if ($node instanceof UnreachableStatementNode) {
                $originalNode = $node->getOriginalStatement();
                $originalNode->setAttribute(AttributeKey::IS_UNREACHABLE, true);
                $originalNode->setAttribute(AttributeKey::SCOPE, $scope);
            } else {
                $node->setAttribute(AttributeKey::SCOPE, $scope);
            }
echo "c2";
            $this->resolveDependentFiles($node, $scope);
            echo "c3";

        };

and also var_dumped the nodes:

        var_dump($nodes);
        /** @var MutatingScope $scope */
        $this->nodeScopeResolver->processNodes($nodes, $scope, $nodeCallback);

This is what I get if I have Eloquent mixin in Model:

Rector dev-master@7dae020
Config file: rector.php

[parsing] app/Services/CartService.php
array(1) {
  [0]=>
  object(PhpParser\Node\Stmt\Namespace_)#1776 (3) {
    ["name"]=>
    object(PhpParser\Node\Name)#1792 (2) {
      ["parts"]=>
      array(2) {
        [0]=>
        string(3) "App"
        [1]=>
        string(8) "Services"
      }
      ["attributes":protected]=>
      array(4) {
        ["startLine"]=>
        int(3)
        ["startTokenPos"]=>
        int(4)
        ["endLine"]=>
        int(3)
        ["endTokenPos"]=>
        int(4)
      }
    }
    ["stmts"]=>
    array(4) {
      [0]=>
      object(PhpParser\Node\Stmt\Use_)#1775 (3) {
        ["type"]=>
        int(1)
        ["uses"]=>
        array(1) {
          [0]=>
          object(PhpParser\Node\Stmt\UseUse)#1615 (4) {
            ["type"]=>
            int(0)
            ["name"]=>
            object(PhpParser\Node\Name)#1687 (2) {
              ["parts"]=>
              array(3) {
                [0]=>
                string(3) "App"
                [1]=>
                string(6) "Models"
                [2]=>
                string(4) "Cart"
              }
              ["attributes":protected]=>
              array(4) {
                ["startLine"]=>
                int(5)
                ["startTokenPos"]=>
                int(9)
                ["endLine"]=>
                int(5)
                ["endTokenPos"]=>
                int(9)
              }
            }
            ["alias"]=>
            NULL
            ["attributes":protected]=>
            array(4) {
              ["startLine"]=>
              int(5)
              ["startTokenPos"]=>
              int(9)
              ["endLine"]=>
              int(5)
              ["endTokenPos"]=>
              int(9)
            }
          }
        }
        ["attributes":protected]=>
        array(4) {
          ["startLine"]=>
          int(5)
          ["startTokenPos"]=>
          int(7)
          ["endLine"]=>
          int(5)
          ["endTokenPos"]=>
          int(10)
        }
      }
      [1]=>
      object(PhpParser\Node\Stmt\Use_)#1796 (3) {
        ["type"]=>
        int(1)
        ["uses"]=>
        array(1) {
          [0]=>
          object(PhpParser\Node\Stmt\UseUse)#1789 (4) {
            ["type"]=>
            int(0)
            ["name"]=>
            object(PhpParser\Node\Name)#1791 (2) {
              ["parts"]=>
              array(1) {
                [0]=>
                string(4) "Auth"
              }
              ["attributes":protected]=>
              array(4) {
                ["startLine"]=>
                int(6)
                ["startTokenPos"]=>
                int(14)
                ["endLine"]=>
                int(6)
                ["endTokenPos"]=>
                int(14)
              }
            }
            ["alias"]=>
            NULL
            ["attributes":protected]=>
            array(4) {
              ["startLine"]=>
              int(6)
              ["startTokenPos"]=>
              int(14)
              ["endLine"]=>
              int(6)
              ["endTokenPos"]=>
              int(14)
            }
          }
        }
        ["attributes":protected]=>
        array(4) {
          ["startLine"]=>
          int(6)
          ["startTokenPos"]=>
          int(12)
          ["endLine"]=>
          int(6)
          ["endTokenPos"]=>
          int(15)
        }
      }
      [2]=>
      object(PhpParser\Node\Stmt\Use_)#1807 (3) {
        ["type"]=>
        int(1)
        ["uses"]=>
        array(1) {
          [0]=>
          object(PhpParser\Node\Stmt\UseUse)#1798 (4) {
            ["type"]=>
            int(0)
            ["name"]=>
            object(PhpParser\Node\Name)#1808 (2) {
              ["parts"]=>
              array(1) {
                [0]=>
                string(7) "Session"
              }
              ["attributes":protected]=>
              array(4) {
                ["startLine"]=>
                int(7)
                ["startTokenPos"]=>
                int(19)
                ["endLine"]=>
                int(7)
                ["endTokenPos"]=>
                int(19)
              }
            }
            ["alias"]=>
            NULL
            ["attributes":protected]=>
            array(4) {
              ["startLine"]=>
              int(7)
              ["startTokenPos"]=>
              int(19)
              ["endLine"]=>
              int(7)
              ["endTokenPos"]=>
              int(19)
            }
          }
        }
        ["attributes":protected]=>
        array(4) {
          ["startLine"]=>
          int(7)
          ["startTokenPos"]=>
          int(17)
          ["endLine"]=>
          int(7)
          ["endTokenPos"]=>
          int(20)
        }
      }
      [3]=>
      object(PhpParser\Node\Stmt\Class_)#1810 (7) {
        ["flags"]=>
        int(0)
        ["extends"]=>
        NULL
        ["implements"]=>
        array(0) {
        }
        ["name"]=>
        object(PhpParser\Node\Identifier)#1823 (2) {
          ["name"]=>
          string(11) "CartService"
          ["attributes":protected]=>
          array(4) {
            ["startLine"]=>
            int(9)
            ["startTokenPos"]=>
            int(24)
            ["endLine"]=>
            int(9)
            ["endTokenPos"]=>
            int(24)
          }
        }
        ["stmts"]=>
        array(1) {
          [0]=>
          object(PhpParser\Node\Stmt\ClassMethod)#1818 (7) {
            ["flags"]=>
            int(0)
            ["byRef"]=>
            bool(false)
            ["name"]=>
            object(PhpParser\Node\Identifier)#1887 (2) {
              ["name"]=>
              string(11) "initNewCart"
              ["attributes":protected]=>
              array(4) {
                ["startLine"]=>
                int(11)
                ["startTokenPos"]=>
                int(30)
                ["endLine"]=>
                int(11)
                ["endTokenPos"]=>
                int(30)
              }
            }
            ["params"]=>
            array(0) {
            }
            ["returnType"]=>
            NULL
            ["stmts"]=>
            array(3) {
              [0]=>
              object(PhpParser\Node\Stmt\Expression)#1800 (2) {
                ["expr"]=>
                object(PhpParser\Node\Expr\Assign)#2023 (3) {
                  ["var"]=>
                  object(PhpParser\Node\Expr\Variable)#1820 (2) {
                    ["name"]=>
                    string(4) "cart"
                    ["attributes":protected]=>
                    array(4) {
                      ["startLine"]=>
                      int(13)
                      ["startTokenPos"]=>
                      int(36)
                      ["endLine"]=>
                      int(13)
                      ["endTokenPos"]=>
                      int(36)
                    }
                  }
                  ["expr"]=>
                  object(PhpParser\Node\Expr\New_)#1880 (3) {
                    ["class"]=>
                    object(PhpParser\Node\Name\FullyQualified)#1815 (2) {
                      ["parts"]=>
                      array(3) {
                        [0]=>
                        string(3) "App"
                        [1]=>
                        string(6) "Models"
                        [2]=>
                        string(4) "Cart"
                      }
                      ["attributes":protected]=>
                      array(5) {
                        ["startLine"]=>
                        int(13)
                        ["startTokenPos"]=>
                        int(42)
                        ["endLine"]=>
                        int(13)
                        ["endTokenPos"]=>
                        int(42)
                        ["originalName"]=>
                        object(PhpParser\Node\Name)#1933 (2) {
                          ["parts"]=>
                          array(1) {
                            [0]=>
                            string(4) "Cart"
                          }
                          ["attributes":protected]=>
                          array(4) {
                            ["startLine"]=>
                            int(13)
                            ["startTokenPos"]=>
                            int(42)
                            ["endLine"]=>
                            int(13)
                            ["endTokenPos"]=>
                            int(42)
                          }
                        }
                      }
                    }
                    ["args"]=>
                    array(0) {
                    }
                    ["attributes":protected]=>
                    array(4) {
                      ["startLine"]=>
                      int(13)
                      ["startTokenPos"]=>
                      int(40)
                      ["endLine"]=>
                      int(13)
                      ["endTokenPos"]=>
                      int(44)
                    }
                  }
                  ["attributes":protected]=>
                  array(4) {
                    ["startLine"]=>
                    int(13)
                    ["startTokenPos"]=>
                    int(36)
                    ["endLine"]=>
                    int(13)
                    ["endTokenPos"]=>
                    int(44)
                  }
                }
                ["attributes":protected]=>
                array(4) {
                  ["startLine"]=>
                  int(13)
                  ["startTokenPos"]=>
                  int(36)
                  ["endLine"]=>
                  int(13)
                  ["endTokenPos"]=>
                  int(45)
                }
              }
              [1]=>
              object(PhpParser\Node\Stmt\Expression)#1804 (2) {
                ["expr"]=>
                object(PhpParser\Node\Expr\Assign)#1814 (3) {
                  ["var"]=>
                  object(PhpParser\Node\Expr\PropertyFetch)#1995 (3) {
                    ["var"]=>
                    object(PhpParser\Node\Expr\Variable)#1799 (2) {
                      ["name"]=>
                      string(4) "cart"
                      ["attributes":protected]=>
                      array(4) {
                        ["startLine"]=>
                        int(14)
                        ["startTokenPos"]=>
                        int(47)
                        ["endLine"]=>
                        int(14)
                        ["endTokenPos"]=>
                        int(47)
                      }
                    }
                    ["name"]=>
                    object(PhpParser\Node\Identifier)#1806 (2) {
                      ["name"]=>
                      string(5) "price"
                      ["attributes":protected]=>
                      array(4) {
                        ["startLine"]=>
                        int(14)
                        ["startTokenPos"]=>
                        int(49)
                        ["endLine"]=>
                        int(14)
                        ["endTokenPos"]=>
                        int(49)
                      }
                    }
                    ["attributes":protected]=>
                    array(4) {
                      ["startLine"]=>
                      int(14)
                      ["startTokenPos"]=>
                      int(47)
                      ["endLine"]=>
                      int(14)
                      ["endTokenPos"]=>
                      int(49)
                    }
                  }
                  ["expr"]=>
                  object(PhpParser\Node\Scalar\DNumber)#1802 (2) {
                    ["value"]=>
                    float(0)
                    ["attributes":protected]=>
                    array(4) {
                      ["startLine"]=>
                      int(14)
                      ["startTokenPos"]=>
                      int(53)
                      ["endLine"]=>
                      int(14)
                      ["endTokenPos"]=>
                      int(53)
                    }
                  }
                  ["attributes":protected]=>
                  array(4) {
                    ["startLine"]=>
                    int(14)
                    ["startTokenPos"]=>
                    int(47)
                    ["endLine"]=>
                    int(14)
                    ["endTokenPos"]=>
                    int(53)
                  }
                }
                ["attributes":protected]=>
                array(4) {
                  ["startLine"]=>
                  int(14)
                  ["startTokenPos"]=>
                  int(47)
                  ["endLine"]=>
                  int(14)
                  ["endTokenPos"]=>
                  int(54)
                }
              }
              [2]=>
              object(PhpParser\Node\Stmt\Return_)#1805 (2) {
                ["expr"]=>
                object(PhpParser\Node\Expr\Variable)#1803 (2) {
                  ["name"]=>
                  string(4) "cart"
                  ["attributes":protected]=>
                  array(4) {
                    ["startLine"]=>
                    int(15)
                    ["startTokenPos"]=>
                    int(58)
                    ["endLine"]=>
                    int(15)
                    ["endTokenPos"]=>
                    int(58)
                  }
                }
                ["attributes":protected]=>
                array(4) {
                  ["startLine"]=>
                  int(15)
                  ["startTokenPos"]=>
                  int(56)
                  ["endLine"]=>
                  int(15)
                  ["endTokenPos"]=>
                  int(59)
                }
              }
            }
            ["attributes":protected]=>
            array(4) {
              ["startLine"]=>
              int(11)
              ["startTokenPos"]=>
              int(28)
              ["endLine"]=>
              int(16)
              ["endTokenPos"]=>
              int(61)
            }
          }
        }
        ["attributes":protected]=>
        array(4) {
          ["startLine"]=>
          int(9)
          ["startTokenPos"]=>
          int(22)
          ["endLine"]=>
          int(17)
          ["endTokenPos"]=>
          int(63)
        }
        ["namespacedName"]=>
        object(PhpParser\Node\Name)#1817 (2) {
          ["parts"]=>
          array(3) {
            [0]=>
            string(3) "App"
            [1]=>
            string(8) "Services"
            [2]=>
            string(11) "CartService"
          }
          ["attributes":protected]=>
          array(0) {
          }
        }
      }
    }
    ["attributes":protected]=>
    array(5) {
      ["startLine"]=>
      int(3)
      ["startTokenPos"]=>
      int(2)
      ["endLine"]=>
      int(17)
      ["endTokenPos"]=>
      int(63)
      ["kind"]=>
      int(1)
    }
  }
}
AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartServ
ice.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3 (here it hangs)

If i remove that mixin in Model class, I get

File "/mnt/c/Users/admin/Dropbox/_php/RestaurantBack/vendor/autoload.php" is about to be loaded in "AutoloadIncluder::includeCwdVendorAutoloadIfExists()" on line 102"
File "/mnt/c/Users/admin/Dropbox/_php/RestaurantBack/vendor/rector/rector/../../autoload.php" is about to be loaded in "AutoloadIncluder::autoloadProjectAutoloaderFile()" on line 136"
Rector dev-master@7dae020
Config file: rector.php

[parsing] app/Services/CartService.php
array(1) {
    <<<the same var dump as above>>>
}
CartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartServ
ice.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2c3AbCartService.phpcc2
c3AbCartService.phpcc2c3AbCartService.phpcc2c3 (here it continues and finishes)

Unfortunately I wasn't narrow it down futher because it looks like It's hanging in NodeScopeResolver.php which is in phar, which I found even you had problem with finding solution to :-)

@genesiscz
Copy link
Contributor Author

genesiscz commented Jul 30, 2020

Maybe @ondrejmirtes could help a bit here, please?

It would be plenty enough to just have an ability to skip the @mixin from analyzing I think

@canvural
Copy link

canvural commented Aug 3, 2020

PHPStan has an undocumented feature that you can tell it to ignore some @mixin tags. We use it at Larastan like this. Maybe this can help, if just the @mixin \Eloquent is the problem.

@genesiscz
Copy link
Contributor Author

genesiscz commented Aug 3, 2020

Just found about that haha. Thanks!
How do I set it up for rector usage tho? Is installing Larastan/setting in phpstan.neon enough?

@canvural
Copy link

canvural commented Aug 3, 2020

I don't know how rector works. If you have a possiblity to have your own PHPStan config, just add

parameters:
    mixinExcludeClasses:
        - Eloquent

@TomasVotruba
Copy link
Member

Closing as resolved with #3836 (comment)

TomasVotruba added a commit that referenced this issue May 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants