Skip to content

feat(analyzer): add Magento 2 analyzer plugins#1251

Open
peterjaap wants to merge 4 commits intocarthage-software:mainfrom
peterjaap:add-magento-analyzers
Open

feat(analyzer): add Magento 2 analyzer plugins#1251
peterjaap wants to merge 4 commits intocarthage-software:mainfrom
peterjaap:add-magento-analyzers

Conversation

@peterjaap
Copy link
Copy Markdown
Contributor

📌 What Does This PR Do?

Add analyzer plugins for Magento 2 best practices:

  • use_service_contracts: flags direct repository implementation usage
  • collection_via_factory: flags direct collection instantiation
  • use_resource_model_directly: flags direct resource model save/load
  • no_set_template_in_block: flags setTemplate() in Block constructors
  • collection_mock_subclass: flags test mocks extending AbstractDb
  • ObjectManager return type provider for get()/create()

Adds 5 IssueCode variants and test cases for all plugins.

Inspired by phpstan-magento (https://github.com/bitExpert/phpstan-magento).

🔍 Context & Motivation

We would like to move away from phpcs and PhpStan, and adding these rules to Mago allows us to do this.

🛠️ Summary of Changes

Feature: Added Magento 2 rules to analyzers

📂 Affected Areas

  • Linter
  • Formatter
  • CLI
  • Dependencies
  • Documentation
  • Other (please specify):

🔗 Related Issues or PRs

Related to #177

📝 Notes for Reviewers

I added some analyzers;

./mago --config mago.toml analyze --list-codes | grep magento
  "magento-collection-mock-subclass",
  "magento-collection-via-factory",
  "magento-no-set-template-in-block",
  "magento-use-resource-model-directly",
  "magento-use-service-contracts",

Enabled like this;

diff --git a/mago.toml b/mago.toml
index 45fd2360..313a9884 100644
--- a/mago.toml
+++ b/mago.toml
@@ -14,7 +14,7 @@ tab-width = 4
 use-tabs = false
 
 [linter]
-integrations = ["phpunit"]
+integrations = ["phpunit", "magento"]
 
 [linter.rules]
 ambiguous-function-call = { enabled = false }
@@ -27,6 +27,7 @@ excessive-parameter-list = { enabled = false }
 
 [analyzer]
 # Analyzer Settings
+plugins = ["magento"]
 find-unused-definitions = true
 find-unused-expressions = false
 analyze-dead-code = true

Ran the analyzer on a test file;

<?php

namespace Test;

use Magento\Catalog\Model\Product;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Registry;

class Example
{
    public function __construct(
        private ObjectManagerInterface $objectManager, // should trigger no-object-manager-type-hint
        private Registry $registry,                    // should trigger no-registry
    ) {}

    public function bad(Product $product): void
    {
        $product->save();           // should trigger use-service-contracts
        $product->getCollection();  // should trigger collection-via-factory
        $product->getResource();    // should trigger use-resource-model-directly

        $om = ObjectManager::getInstance(); // should trigger no-object-manager-singleton
    }
}

With output;

./mago --config mago.toml analyze app/code/test.php
 INFO File `vendor/composer/autoload_classmap.php` contains invalid UTF-8. Lossy conversion applied, which may cause undefined behavior.
 INFO File `vendor/composer/autoload_static.php` contains invalid UTF-8. Lossy conversion applied, which may cause undefined behavior.
 INFO File `vendor/symfony/cache/Traits/ValueWrapper.php` contains invalid UTF-8. Lossy conversion applied, which may cause undefined behavior.
warning[deprecated-method]: Call to deprecated method: `magento\framework\model\abstractmodel::save`.
   ┌─ app/code/test.php:11:9
   │
11 │         $product->save();           // should trigger use-service-contracts
   │         ^^^^^^^^^^^^^^^^ This method is deprecated
   │
   = The method `magento\framework\model\abstractmodel::save` is marked as deprecated and may be removed or its behavior changed in future versions.
   = Help: Consult the documentation for `magento\framework\model\abstractmodel::save` for alternatives or migration instructions.

warning[magento-use-service-contracts]: Use service contracts to persist entities instead of `Magento\Catalog\Model\Product::save()`.
   ┌─ app/code/test.php:11:9
   │
11 │         $product->save();           // should trigger use-service-contracts
   │         ^^^^^^^^^^^^^^^^ Direct model persistence is deprecated
   │
   = Help: Use the corresponding repository interface (e.g. `ProductRepositoryInterface::save()`) instead.

warning[deprecated-method]: Call to deprecated method: `magento\framework\model\abstractmodel::getcollection`.
   ┌─ app/code/test.php:12:9
   │
12 │         $product->getCollection();  // should trigger collection-via-factory
   │         ^^^^^^^^^^^^^^^^^^^^^^^^^ This method is deprecated
   │
   = The method `magento\framework\model\abstractmodel::getcollection` is marked as deprecated and may be removed or its behavior changed in future versions.
   = Help: Consult the documentation for `magento\framework\model\abstractmodel::getcollection` for alternatives or migration instructions.

warning[magento-collection-via-factory]: Collections should be retrieved via factory, not via `Magento\Catalog\Model\Product::getCollection()`.
   ┌─ app/code/test.php:12:9
   │
12 │         $product->getCollection();  // should trigger collection-via-factory
   │         ^^^^^^^^^^^^^^^^^^^^^^^^^ Use the collection factory instead
   │
   = Help: Inject the collection factory (e.g. `CollectionFactory`) via constructor and use `$this->collectionFactory->create()` instead.

warning[deprecated-method]: Call to deprecated method: `magento\framework\model\abstractmodel::getresource`.
   ┌─ app/code/test.php:13:9
   │
13 │         $product->getResource();    // should trigger use-resource-model-directly
   │         ^^^^^^^^^^^^^^^^^^^^^^^ This method is deprecated
   │
   = The method `magento\framework\model\abstractmodel::getresource` is marked as deprecated and may be removed or its behavior changed in future versions.
   = Help: Consult the documentation for `magento\framework\model\abstractmodel::getresource` for alternatives or migration instructions.

warning[magento-use-resource-model-directly]: `Magento\Catalog\Model\Product::getResource()` is deprecated. Use resource models directly.
   ┌─ app/code/test.php:13:9
   │
13 │         $product->getResource();    // should trigger use-resource-model-directly
   │         ^^^^^^^^^^^^^^^^^^^^^^^ Inject the resource model via constructor instead
   │
   = Help: Inject the resource model directly via constructor DI and call its methods instead.

Add analyzer plugins for Magento 2 best practices:
- use_service_contracts: flags direct repository implementation usage
- collection_via_factory: flags direct collection instantiation
- use_resource_model_directly: flags direct resource model save/load
- no_set_template_in_block: flags setTemplate() in Block constructors
- collection_mock_subclass: flags test mocks extending AbstractDb
- ObjectManager return type provider for get()/create()

Adds 5 IssueCode variants and test cases for all plugins.

Inspired by phpstan-magento (https://github.com/bitExpert/phpstan-magento).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread crates/analyzer/src/code.rs Outdated
Comment thread crates/analyzer/src/plugin/libraries/magento/object_manager.rs
Replace Magento-specific issue codes with existing generic ones
(DeprecatedMethod, InvalidArgument) and regenerate code.rs via
the regen script instead of manual edits.
… any class

Avoids invoking the return type provider on every get()/create() call
across the codebase by using exact class targeting.
@peterjaap
Copy link
Copy Markdown
Contributor Author

@azjezz done!

@peterjaap
Copy link
Copy Markdown
Contributor Author

@azjezz no conflicts anymore

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

Successfully merging this pull request may close these issues.

2 participants