Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Doctor Laravel Package

ahmed-bhs/doctrine-doctor

Doctrine Doctor is a runtime analysis tool for Doctrine ORM integrated into the Symfony Web Profiler. It detects real-world issues like N+1 queries, slow queries, missing indexes, hydration overhead, and injection risks, with actionable backtraces and suggestions.

View on GitHub
Deep Wiki
Context7
## Getting Started
Install via Composer:
```bash
composer require vendor/package-name --dev

Register the package in config/app.php under providers (if not auto-discovered). For first-time use, run:

php artisan vendor:package-name:analyze

Focus on database security and performance checks first—new analyzers (OverprivilegedDatabaseUserAnalyzer, HardcodedDatabaseCredentialsAnalyzer) target critical production risks. Use the --focus flag to target specific analyzers:

php artisan vendor:package-name:analyze --focus=database

Implementation Patterns

Database Security Workflows

  1. Credential Hardcoding Detection:

    • Scan for DBAL configurations with embedded credentials (e.g., username: 'root', password: '...' in config/database.php or migrations).
    • Fix: Move credentials to .env and use env() helpers. Example:
      // Before (flagged)
      $conn = DriverManager::getConnection(['url' => 'mysql://root:password@localhost/db']);
      
      // After
      $conn = DriverManager::getConnection(['url' => env('DB_DSN')]);
      
    • Integrate with CI/CD to block merges if hardcoded credentials are detected.
  2. Privilege Escalation Detection:

    • Analyze Doctrine entities for queries using privileged DB users (e.g., root).
    • Fix: Use least-privilege accounts (e.g., app_user@localhost) and grant only required permissions:
      CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'secure_password';
      GRANT SELECT, INSERT ON `app_db`.`users` TO 'app_user'@'localhost';
      
    • Pair with HardcodedDatabaseCredentialsAnalyzer to ensure credentials for least-privilege users are also secured.
  3. N+1 Query Optimization:

    • Use the NPlusOneAnalyzer to detect repeated findBy() calls (e.g., fetching User entities by email in loops).
    • Fix: Batch queries with IN or use DQL:
      // Before (flagged)
      foreach ($emails as $email) {
          $user = $entityManager->find(User::class, $email); // Non-key lookup!
      }
      
      // After
      $users = $entityManager->createQuery(
          'SELECT u FROM App\Entity\User u WHERE u.email IN (:emails)'
      )->setParameter('emails', $emails)->getResult();
      
    • For request-level caching, use Symfony’s Cache component or Doctrine’s SecondLevelCache.

Sensitive Data Protection

  • Public Getters:
    • The SensitiveDataExposureAnalyzer now flags getters for sensitive fields (e.g., getPassword()) without @ORM\Column(options={"fieldName": "hashed_password"}) or explicit protection.
    • Fix: Use accessors or annotations:
      #[ORM\Column(type: 'string', options: ["fieldName" => "hashed_password"])]
      private string $password;
      
      public function getPassword(): string { return $this->password; } // Now flagged if no protection
      
    • Combine with PropertyTypeMismatchAnalyzer to ensure type safety (e.g., string vs. int for IDs).

Type Safety

  • Concrete Fixes:
    • The PropertyTypeMismatchAnalyzer now suggests fixes for:
      • Nullability mismatches (e.g., #[Assert\NotNull] vs. ?string).
      • Doctrine type mismatches (e.g., string in PHP vs. integer in DB).
    • Example Fix:
      // Before (flagged)
      #[ORM\Column(type: 'string')]
      private ?int $userId; // Mismatch
      
      // After
      #[ORM\Column(type: 'integer', nullable: true)]
      private ?int $userId;
      

Gotchas and Tips

Database Analyzers

  1. False Positives:

    • OverprivilegeDatabaseUserAnalyzer may flag test databases or local dev setups. Exclude them via:
      // config/vendor-package-name.php
      'analyzers' => [
          'OverprivilegedDatabaseUserAnalyzer' => [
              'exclude_databases' => ['test_db', 'homestead'],
          ],
      ],
      
    • HardcodedDatabaseCredentialsAnalyzer ignores env() calls but may miss dynamic DSNs (e.g., sprintf('mysql://%s:%s@...', $user, $pass)). Use static analysis tools like PHPStan in parallel.
  2. Performance Tradeoffs:

    • The NPlusOneAnalyzer has a higher runtime cost. Run it separately:
      php artisan vendor:package-name:analyze --only=NPlusOneAnalyzer
      
    • For large projects, limit scope with --path=src/Entity or --exclude=tests/.

Sensitive Data

  • False Negatives:
    • The analyzer won’t catch sensitive data in private methods or closures. Supplement with:
      #[PHPDoc\Property(written: PHPDoc\When::NEVER)]
      private string $apiKey;
      
    • For dynamic sensitive data (e.g., generated tokens), use runtime checks:
      public function getToken(): string {
          if ($this->isDebug()) {
              throw new \RuntimeException('Token exposure in debug mode!');
          }
          return $this->token;
      }
      

Type Mismatches

  1. Doctrine-Specific Quirks:

    • The analyzer may not detect mismatches in inheritance mappings (e.g., parent id as string vs. child id as int). Manually verify SingleTableInheritance or ClassTableInheritance setups.
    • For custom types, ensure Type::getClassName() matches the PHP type hint.
  2. Nullability:

    • The analyzer prioritizes Doctrine annotations over PHP 8 attributes. If using both, prefer:
      #[ORM\Column(nullable: true)]
      #[Assert\NotNull]
      private ?string $name;
      
    • Conflicts may arise with #[Assert\NotBlank] on nullable fields. Resolve by adding @Assert\NotBlank(groups: ["Default", "!Create"]) for conditional validation.

Extension Points

  • Custom Analyzers: Add new analyzers by extending BaseAnalyzer and register them in config/vendor-package-name.php:
    'analyzers' => [
        App\Custom\MyAnalyzer::class,
    ],
    
  • Fix Templates: Override suggestion templates in resources/views/vendor/package-name/analyzers/. Example for NPlusOneAnalyzer:
    {# Custom template for IN queries #}
    {% if entityManager %}
        Use a DQL IN query:
        ```php
        $qb = $entityManager->createQueryBuilder();
        $qb->select('u')
           ->from('{{ entityClass }}', 'u')
           ->where('u.{{ property }} IN (:values)')
           ->setParameter('values', {{ values|json_encode }});
        ```
    {% endif %}
    
  • CI Integration: Fail builds on critical issues (e.g., database credentials or overprivileged users):
    # .github/workflows/analysis.yml
    jobs:
      analyze:
        runs-on: ubuntu-latest
        steps:
          - run: php artisan vendor:package-name:analyze --fail-on=database,security
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware