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.
ColumnTypeAnalyzer: added configurable excluded_fields list (default: mimeType, contentType, mediaType, fileType) to prevent false enum opportunity alerts on MIME-type fields that match enum patterns but are not enums.OrderByWithoutLimitAnalyzer: added configurable min_execution_time_ms threshold (default: 10ms); array-result queries below the threshold are now flagged as info instead of warning, with a description warning that production data growth will degrade performance.OrderByWithoutLimitAnalyzer: improved suggestion template for bounded array-result queries (WHERE clause present) — now recommends adding an index on the ORDER BY column, adding setMaxResults, or suppressing the alert via config when the collection is guaranteed small.GetReferenceAnalyzer: removed wildcard *_id column patterns that caused false positives on FK columns (e.g. deposit_request_id). Detection is now restricted to strict id primary key columns only, since FK columns return collections and are not candidates for getReference().DoctrineDoctorDataCollector: bootstrap entry points (index.php, autoload_runtime.php, autoload.php) are now excluded when searching for the first application frame in exclude_paths filtering. Previously these files appeared at the bottom of every backtrace and short-circuited vendor exclusion for framework-internal queries (e.g. EasyAdmin entity loading).InheritanceStrategyAnalyzer family: detects invalid or risky inheritance mappings, including missing discriminator maps in STI, sparse STI tables, unsupported OneToMany associations on mapped superclasses, non-root #[InheritanceType] declarations, non-nullable subclass columns in STI, deep CTI hierarchies, and thin CTI subclasses.UniqueEntityWithoutDatabaseIndexAnalyzer: detects #[UniqueEntity] constraints that are not backed by a database UNIQUE index, including Symfony validation metadata declared with attributes, YAML, and XML.DenormalizedAggregateWithoutLockingAnalyzer: detects denormalized aggregate fields updated alongside collections without optimistic or pessimistic locking.ColumnTypeAnalyzer: flags mutable Doctrine date/time column types and suggests immutable equivalents to avoid silent state corruption.EagerLoadingMappingAnalyzer: detects associations declared with fetch: 'EAGER' in entity mapping and suggests deferring fetch strategy decisions to queries.GedmoExtensionPerformanceAnalyzer: detects entities using Gedmo Loggable or Translatable patterns that implicitly generate extra database queries.LazyGhostObjectsDisabledAnalyzer: detects Doctrine ORM configurations where enable_lazy_ghost_objects is not enabled on supported Symfony versions.ManyToManyWithExtraColumnsAnalyzer: detects ManyToMany join tables containing extra columns and recommends promoting them to an explicit join entity.MissingVersionFieldForConcurrencyAnalyzer: detects entities involved in concurrent write patterns without an #[ORM\Version] field for optimistic locking.FlushInEventListenerAnalyzer: detects flush() calls inside Doctrine lifecycle callbacks that can trigger re-entrant UnitOfWork computation or infinite loops.UniqueEntityWithoutDatabaseIndexAnalyzer now supports Symfony validation metadata declared in YAML and XML in addition to PHP attributes.HardcodedDatabaseCredentialsAnalyzer and UniqueEntityWithoutDatabaseIndexAnalyzer.MissingIndexAnalyzer: no longer reports a false positive when the relevant index is already used.LazyGhostObjectsDisabledAnalyzer.MetadataAnalyzerTrait and the split analyzer interface contract.OverprivilegedDatabaseUserAnalyzer: detects privileged, empty, or passwordless database users and suggests switching to a least-privilege account.HardcodedDatabaseCredentialsAnalyzer: detects database credentials embedded directly in DBAL configuration and suggests moving them to environment variables.NPlusOneAnalyzer: identifies repeated findBy()/findOneBy()-style lookups on non-key columns and suggests batching with IN queries or request-level caching.SensitiveDataExposureAnalyzer: now also flags public getters that expose sensitive entity fields without explicit protection.PropertyTypeMismatchAnalyzer: now attaches concrete fix suggestions for PHP/Doctrine type mismatches, including nullability mismatches.CollectionInitializationAnalyzer suggestion template now uses the actual mappedBy value when available.CollectionInitializationAnalyzer: now supports PHP constructor promotion when detecting collection initialization, fixing the false positive reported in issue #67.CollectionEmptyAccessAnalyzer in favor of the AST-based collection initialization analysis path.SQLInjectionInRawQueriesAnalyzer: now detects unparameterized literals in WHERE clauses of raw SQL queries as an injection risk, instead of only flagging active attack patterns.DQLInjectionAnalyzer: now detects Doctrine-generated SQL with concatenated literals and empty bound parameters, indicating unsafe DQL string concatenation.OneToOneInverseSideAnalyzer: detects bidirectional OneToOne mappedBy sides that silently force Doctrine to execute N+1 queries on every load, even when the relation is never accessed. Suggests flipping the owning side, going unidirectional, or using a fetch join.one_to_one_inverse_side analyzer.CompositeKeyComplexityAnalyzer: use ShortClassNameTrait, proper return types, and MappingHelper for Doctrine 2/3/4 compatibility.CompositeKeyComplexityAnalyzer: detects entities using composite primary keys that limit Doctrine ORM features (no getReference(), slower identity map, complex FK mappings). Severity: WARNING for 2 columns, CRITICAL for 3+ or when referenced by other entities.composite_key_complexity analyzer.OnDeleteCascadeMismatchAnalyzer now assigns CRITICAL severity for orm_cascade_db_setnull and orm_orphan_db_setnull mismatches (previously WARNING).on_delete_cascade_mismatch now render context-aware code snippets per mismatch type instead of a generic template.JoinColumnNonPrimaryKeyAnalyzer: detects associations where referencedColumnName points to a non-primary-key column, which causes incorrect lazy-loading proxy behavior.DuplicatePrivateFieldInHierarchyAnalyzer: detects private fields with the same name in an entity and its mapped parent classes, which triggers MappingException or unpredictable Collection filtering.join_column_non_primary_key, duplicate_private_field_in_hierarchy, overprivileged_database_user, and hardcoded_database_credentials analyzers.ORM\BatchFetch, fixed isVendorCode detection.Suggested Fix, Hide suggestion).issue-body background color to #fffefc for better visual consistency.doctrine_doctor.enabled now supports Symfony parameter placeholders (e.g. %kernel.debug%) by resolving the root enabled config before strict tree validation.%kernel.debug% placeholder handling in DI extension tests.isset.initializedProperty error: use ReflectionProperty::isInitialized() for readonly property check after unserialization.rel="noopener noreferrer" on external Doctrine documentation link (target="_blank")..alert-warning, .alert-danger, and .dd-suggestion-meta-intro blocks (less aggressive text contrast).IssueInterface and DeduplicatableIssueInterface.IssueReconstructor (#32)<pre><code> to prevent entity encodingwebmozart/assert constraint to support v2.xbitbag/coding-standard dependencydoctrine/doctrine-bundle ^3.0 (drop ^2.x)doctrine/orm ^3.0|^4.0 (drop ^2.x)webmozart/assert ^1.12PhpTemplateRenderer into IssueReconstructor so template rendering works after Symfony profiler deserializationSafeContext::offsetGet() now returns null for missing keys instead of throwing, enabling safe array destructuring in templates with optional context variablestrigger_location in eager_loading templateleft_join_with_not_null template (table_name -> entity)#[\Override], typed constants, array_find())ini_set('memory_limit') runtime manipulationdoctrine/doctrine-bundle ^2.x supportdoctrine/orm ^2.x supportHow can I help you explore Laravel packages today?