Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/MartinGeorgiev/Doctrine/DBAL/Type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace MartinGeorgiev\Doctrine\DBAL;

enum Type
{
public const BIGINT_ARRAY = 'bigint[]';
}
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the file exists and view its actual content
if [ -f "src/MartinGeorgiev/Doctrine/DBAL/Type.php" ]; then
  echo "=== File content ==="
  cat -n "src/MartinGeorgiev/Doctrine/DBAL/Type.php"
  echo ""
  echo "=== File size ==="
  wc -l "src/MartinGeorgiev/Doctrine/DBAL/Type.php"
else
  echo "File not found at src/MartinGeorgiev/Doctrine/DBAL/Type.php"
  # Try to find it
  fd "Type.php" --type f
fi

Repository: martin-georgiev/postgresql-for-doctrine

Length of output: 222


🌐 Web query:

PHP 8.1 enum syntax public const vs case backing values

💡 Result:

Short answer: they're different things.

  • case (backed or pure) defines an enum member (an instance). Backed cases are declared with a scalar type and value (enum E: string { case A = 'a'; }) and each instance has a read‑only ->value plus static from/tryFrom helpers. Backed enums require the enum type (string|int) and every case must have a unique scalar. [1][2]

  • public const inside an enum is just a class constant (a scalar or a reference). It is not an enum case and does not satisfy an enum typehint. A const can reference a case (aliasing it) — e.g. public const X = self::SomeCase; — but that const holds the case reference or scalar you assign, not a case instance value unless you explicitly use the case->value (PHP 8.2 added easier use of case values in consts). [3][1]

Examples:

  • Backed case:
    enum Status: string { case Active = 'active'; }
    $v = Status::Active->value; // 'active' — Status::from('active') returns Status::Active. [2]

  • Enum const alias / scalar const:
    enum Status { case Active; public const LIVE = self::Active; public const TAG = 'live'; }
    Status::Active is an enum instance; Status::TAG is a string and does NOT satisfy a Status typehint. [1][3]

If you want to map external scalars (DB values, API keys) to enum instances, use a backed enum (case = 'value') so you can safely convert with from/tryFrom. [2][3]

Sources: PHP manual (enum constants + backed enums) and related docs. [1][2][3]

References
[1] PHP manual — Enumeration constants.
[2] PHP manual — Backed enumerations.
[3] Shopware / community notes on using enums and consts (shows const vs case behavior and PHP 8.2 improvement).


🌐 Web query:

PHP enum public const vs backed enum case DBAL doctrine type constants

💡 Result:

Short answer

  • Use enum cases (backed enums) when the value must round‑trip to storage (they have a scalar .value). Use public const in an enum when you need additional named constants that are not enum cases (aliases, calculated constants). [1][2]

Why (technical)

  • Backed enum case: a language-level case with an associated int|string value (e.g. enum Status: string { case Active = 'A'; }). It serializes/deserializes easily and is intended for DB mapping. [2][1]
  • public const inside an enum: just a plain constant (can reference cases) — useful for aliases or other constants but not a case and has no .value semantics. [1]

Doctrine/DBAL implications and common patterns

  • Doctrine ORM (2.11+) can map PHP 8.1+ backed enums automatically (it recognizes enum types and provides enumType mapping) — prefer backed enums for direct, safe mapping. [3][4]
  • DBAL has an enum type for MySQL or you can implement a custom Doctrine\DBAL\Types\Type. If you don’t use backed enums, custom Type classes often keep the allowed DB values as private/consts inside the Type (e.g. private const STATUS_VISIBLE = 'visible') and convert to/from PHP values in convertToPHPValue/convertToDatabaseValue. That pattern is for when you want DB values decoupled from PHP enum cases. [5][6][7]
  • Recommendation:
    • If you control both PHP and DB and want simple round-trip mapping: use backed enums and let Doctrine map them (cleanest). [3][4]
    • If you must support DB-specific ENUM semantics, multiple DB platforms, or need different DB strings than enum values, implement a custom DBAL Type and keep a list of allowed DB constants in the Type (or map between enum cases and DB strings explicitly). [5][7]

References

  • PHP enum constants & examples (manual). [1]
  • PHP enumerations RFC / backed enums explanation. [2]
  • Doctrine ORM release notes (PHP 8.1 enum support). [3]
  • Doctrine ORM basic mapping / typed property & enum mapping docs. [4]
  • Doctrine cookbook: MySQL ENUM and Type pattern. [5]
  • Doctrine DBAL types reference (enum mapping behavior). [6]
  • Doctrine custom mapping types guide (how to implement Type classes). [7]

🌐 Web query:

PHP enum with only public constants without cases valid syntax

💡 Result:

Yes — PHP enums may have zero cases and may contain constants, so an enum that only defines public constants (no case statements) is valid syntax. Example:

References: PHP manual — Basic enumerations (zero-case enums valid) [1]; Enumeration constants (enum may include public constants) [2].


Based on my verification, the original review comment contains a critical factual error. The code is syntactically valid PHP—enums can contain zero cases and public constants. However, the architectural concerns are valid. Here's the rewritten review comment:


This enum design pattern is not recommended for DBAL type mapping, even though it's syntactically valid.

An enum with only public constants (no cases) is valid PHP 8.1+ syntax, but it's an unusual pattern that defeats the purpose of using an enum. For DBAL type constants, choose one of these approaches:

Option 1: Backed enum with cases (type-safe, matches Doctrine conventions)

-enum Type
+enum Type: string
 {
-    public const BIGINT_ARRAY = 'bigint[]';
+    case BIGINT_ARRAY = 'bigint[]';
 }

Trade-off: Callers use Type::BIGINT_ARRAY->value in DBAL type annotations.

Option 2: Abstract class with constants (simpler, original approach)

-enum Type
+abstract class Type
 {
     public const BIGINT_ARRAY = 'bigint[]';
 }

Trade-off: No enum type safety, but constants are used directly without ->value.

Align with the maintainer's preference on which trade-off is acceptable. For DBAL custom types, backed enums (Option 1) are more idiomatic and provide type safety; abstract classes (Option 2) are simpler if enum semantics aren't needed.

🤖 Prompt for AI Agents
In src/MartinGeorgiev/Doctrine/DBAL/Type.php around lines 7 to 10, the current
enum contains only public constants (no cases) which is syntactically valid but
an anti-pattern for DBAL type mapping; replace this with either a backed enum
with cases so callers use Type::BIGINT_ARRAY->value (preferred for type-safety
and Doctrine conventions) or convert to an abstract class with public constants
so callers use Type::BIGINT_ARRAY directly; pick the maintainer-preferred
approach and update all call sites and docblocks accordingly.

4 changes: 3 additions & 1 deletion src/MartinGeorgiev/Doctrine/DBAL/Types/BigIntArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace MartinGeorgiev\Doctrine\DBAL\Types;

use MartinGeorgiev\Doctrine\DBAL\Type;

/**
* Implementation of PostgreSQL BIGINT[] data type.
*
Expand All @@ -17,7 +19,7 @@ class BigIntArray extends BaseIntegerArray
/**
* @var string
*/
protected const TYPE_NAME = 'bigint[]';
protected const TYPE_NAME = Type::BIGINT_ARRAY;

protected function getMinValue(): int
{
Expand Down