[RFC]: Replace caller-supplied Literal slots in Sql\Ddl with backed enums and typed arguments
I maintainer di solito rispondono entro 1 giorno
@simon-mundy ci sta già lavorando.
Dal 4/9/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Proposed Version
0.6.0 for the enums, typed arguments and string-compat setters; the string-getter retype waits for the next major.
Basic Information
Every place under src/Sql/Ddl/ where a caller-supplied string becomes an Argument\Literal or is concatenated into a spec string gets a backed enum (closed value sets) or a typed int (numeric slots), with a one-minor string-compat path. This is the core counterpart of phpdb-mysql#79.
Background
The DDL layer renders three kinds of argument: Argument\Identifier (quoted as an identifier), Argument\Value (quoted or bound) and Argument\Literal (inserted verbatim). Going through every getExpressionData() under src/Sql/Ddl/ (and processTableOptions() for table options), these are the places where a Literal, or a bare string concatenated into the spec, comes from the caller rather than a class constant:
| Class | Slot | Origin | Rendered probe |
|---|---|---|---|
Index |
column prefix length | untyped array $lengths, concatenated into the spec |
INDEX `i`(`email`(20) INJECT)) |
Index |
USING type |
setType(string) -> Literal |
USING HASH; DROP |
ForeignKey |
ON DELETE / ON UPDATE rule |
string setters -> Literal |
ON DELETE CASCADE; DROP |
Integer |
display width | options['length'] (untyped via the constructor array, bool|string via setOption()), concatenated |
INTEGER NOT NULL (11) (also a positional bug, #178) |
Check |
expression | untyped constructor with a string|ExpressionInterface docblock -> Literal |
an expression slot by design; an Expression is accepted at construction and fails at render (#177) |
CreateTable / AlterTable processTableOptions() |
table option key | setOption(string $name, ...), strtoupper() only |
ENGINE = INNODB; DROP TABLE Y; -- = 'x' |
Example: setOption('row_format', 'DYNAMIC'), 'algorithm' and 'lock' with a plain string render a quoted value MySQL rejects with 1064. Only a Literal works, and nothing tells the caller that.
Safe by construction and not proposed to change: Column::$type (protected, set by the subclass), AbstractLengthColumn / AbstractPrecisionColumn lengths (built from ?int), AbstractTimestampColumn's fixed ON UPDATE CURRENT_TIMESTAMP, all identifier slots, Column defaults given as an Argument\Literal, and table-option values given as a Sql\Literal. Those last two are raw by definition.
Considerations
- Docs bug. Only
Argument\Literalitself says aLiteraldefault is raw.Column::setDefault()and the DDL docs don't, anddocs/book/sql-ddl/columns.md:340-346,alter-drop.md,examples.mdandintro.mdall showsetDefault('CURRENT_TIMESTAMP')producing an unquoted default. It actually rendersDEFAULT 'CURRENT_TIMESTAMP', which MySQL rejects (1067). Worth its own issue; listed here because it is the other side of the same invariant. - Getters.
getOnDeleteRule(): string,getOnUpdateRule(): stringandIndex::getType(): ?stringare asserted as strings inForeignKeyTest.php:106-132andIndexTest.php:67. Retyping them in a minor would be a BC break, so they stay until the next major. SET DEFAULTis valid grammar that InnoDB doesn't honour (on 8.0.46 the DDL is accepted and RESTRICT is enforced at DML time). Keep it in the enum, document the caveat.- Index prefix length: MySQL rejects 0 with 1391, so
positive-intis exactly the server's bound. - Integer display width is itself deprecated since 8.0.17; keeping it as a typed
intis for the other platforms, and the MySQL decorator will drop it. - Overlaps: #169 (typing the option arrays), phpdb-mysql#79 (the adapter side), the statements umbrella, #179 (
RowFormat/Algorithm/Lockand key validation live there).
Proposal(s)
Sql\Ddl\Constraint\ReferentialAction: string { NoAction = 'NO ACTION'; Restrict = 'RESTRICT'; Cascade = 'CASCADE'; SetNull = 'SET NULL'; SetDefault = 'SET DEFAULT' }forForeignKey::setOnDeleteRule()/setOnUpdateRule()and the constructor.Sql\Ddl\Index\IndexType: string { BTree = 'BTREE'; Hash = 'HASH' }forIndex::setType().Index::$lengthstypedlist<positive-int>(validated in the constructor) and rendered asLiteral((string) $int)in the values array, not in the spec.Integerlength asint(see #178).- Table option keys validated against a pattern or an enum of known options; string values keep going through
quoteTrustedValue(), keyword values throughRowFormat/Algorithm/Lockenums (#179).
Compatibility:
- Setters keep accepting
stringfor one minor, resolved withEnum::tryFrom(strtoupper(trim($value)))and rejected withInvalidArgumentExceptionwhen unknown. Passing a string is deprecated in the docblock and removed at the next major. - Getters keep returning
stringfor the same period. Add enum-returning siblings now (getOnDeleteAction(): ReferentialAction,getOnUpdateAction(): ReferentialAction,Index::getIndexType(): ?IndexType) and retype the string getters at the next major.
Test plan:
- No
getExpressionData()undersrc/Sql/Ddl/constructs anArgument\Literalfrom a caller string exceptCheck(documented) andColumndefaults (documented insetDefault()and the docs, with theCURRENT_TIMESTAMPexamples corrected). - No
getExpressionData()orprocessTableOptions()concatenates a caller value into the spec string. - Each enum has a unit test for accepted values, the string-compat path and rejection.
- The probe strings above render quoted/escaped or throw; none reaches SQL verbatim.
- Existing string-getter tests pass unchanged.
Appendix/Additional Info
- #91 (backed enums for metadata constants), #169 (untyped option arrays), phpdb-mysql#79 (the same fix on the adapter side).
- From an internal DDL audit (not published). The full slot table there includes the adapter side; the core rows are reproduced above.
- Lingua principale
- PHP
- Stelle
- 18
- Fork
- 8
- Merge medio
- 6g 11h
- PR unite (30g)
- 14
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Nessun modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di php-db/phpdb
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
RFC
Difficoltà 3/5 1-2 giorni Idoneità per principianti 66/100
I maintainer di solito rispondono entro 1 giorno
-
RFC
Difficoltà 5/5 Più di una settimana Idoneità per principianti 38/100
I maintainer di solito rispondono entro 1 giorno
-
RFC
Difficoltà 3/5 1-2 giorni Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
RFC
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di php-db/phpdb
Issue simili
-
Bug Enhancement Performance
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Feature Status: Needs Triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
I maintainer di solito rispondono entro 1 giorno
-
frontend low-priority
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
mplodowski/dynamicpdf-plugin#336 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
AdvancedCustomFields/acf#1044 ·
-
Add ZammadForse già presa @Arslan-TR l’ha presa oggi. Apertarequest
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
endoflife-date/endoflife.date#11298 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno