Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

[RFC]: Replace caller-supplied Literal slots in Sql\Ddl with backed enums and typed arguments

未關閉
#181 0 則留言 0 個 reaction 已指派 1 人 在 GitHub 檢視

維護者通常 2 天內回覆

@simon-mundy 已經在處理了。

開始於 2026年9月4日。

評估

這個 Issue 還沒有評估資料。

描述

enhancement
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\Literal itself says a Literal default is raw. Column::setDefault() and the DDL docs don't, and docs/book/sql-ddl/columns.md:340-346, alter-drop.md, examples.md and intro.md all show setDefault('CURRENT_TIMESTAMP') producing an unquoted default. It actually renders DEFAULT '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(): string and Index::getType(): ?string are asserted as strings in ForeignKeyTest.php:106-132 and IndexTest.php:67. Retyping them in a minor would be a BC break, so they stay until the next major.
  • SET DEFAULT is 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-int is exactly the server's bound.
  • Integer display width is itself deprecated since 8.0.17; keeping it as a typed int is 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/Lock and key validation live there).
Proposal(s)
  1. Sql\Ddl\Constraint\ReferentialAction: string { NoAction = 'NO ACTION'; Restrict = 'RESTRICT'; Cascade = 'CASCADE'; SetNull = 'SET NULL'; SetDefault = 'SET DEFAULT' } for ForeignKey::setOnDeleteRule() / setOnUpdateRule() and the constructor.
  2. Sql\Ddl\Index\IndexType: string { BTree = 'BTREE'; Hash = 'HASH' } for Index::setType().
  3. Index::$lengths typed list<positive-int> (validated in the constructor) and rendered as Literal((string) $int) in the values array, not in the spec.
  4. Integer length as int (see #178).
  5. Table option keys validated against a pattern or an enum of known options; string values keep going through quoteTrustedValue(), keyword values through RowFormat / Algorithm / Lock enums (#179).

Compatibility:

  • Setters keep accepting string for one minor, resolved with Enum::tryFrom(strtoupper(trim($value))) and rejected with InvalidArgumentException when unknown. Passing a string is deprecated in the docblock and removed at the next major.
  • Getters keep returning string for 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() under src/Sql/Ddl/ constructs an Argument\Literal from a caller string except Check (documented) and Column defaults (documented in setDefault() and the docs, with the CURRENT_TIMESTAMP examples corrected).
  • No getExpressionData() or processTableOptions() 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.
主要語言
PHP
星號
18
分支
8
平均合併
2 天 6 小時
30 天內合併 PR
30

環境準備

  • 提供 Dockerfile 或 Docker Compose 檔案
  • 沒有 Pull Request 範本
  • 沒有貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

php-db/phpdb 的其他 Issue

查看 php-db/phpdb 的全部 Issue

相似的 Issue

更多 PHP Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。