Skip to content

v8.5.26

Choose a tag to compare

@marcing marcing released this 14 Sep 15:38
· 12 commits to main since this release
3683fd0

⚠️ Breaking: insert() and update() columns are PHP values now

A bare string passed as a column is a bound value, not SQL. SQL has to say
so: new Ovos\Pdo\Expression('NOW()') — the convention the model layer already
used, now true of the builder as well.

It fails loudly rather than quietly: strict mode rejects the literal text in a
DATETIME or INT column. The call sites this affected across the projects
vendoring dev-release/8.5 have already been adjusted.

The deprecated commented insertUpdateQuery() is gone; the builder has
ON DUPLICATE KEY UPDATE.

Store: the query carries its values

A condition, join or HAVING given as a tuple [sql, ...values] records its
values beside the SQL, and getValues() hands them back in the order the SQL
emits them. Store\Mysql::statement() prepares, binds by position with the
PHP types
(a false stays 0, a null stays NULL) and executes, with
fetchModels(), fetchModel(), fetchRows(), fetchList(), fetchScalar()
and affected() beside it. executeFind(), fetchReferenced() and
fetchByReference() bind their IN lists — an IN list of names works now,
where it used to be unquoted SQL.

The gaps the console's stores kept hitting are closed: Delete can alias and
join (the setters lived on Select while Delete::getSql() already emitted
them); INSERT IGNORE; one ordered join list, so leftJoin() and
innerJoin() stop erasing each other; whereIn(field, values, bind: true) and
orWhereIn(); orderBy() and limit(?int) on Select, Update and Delete;
Insert::row() / rows() / onDuplicateKeyUpdate(); a group opening with
orWhere() no longer starts on a dangling OR; limit() / offset() are
ints, since native prepares refuse a string-bound LIMIT.

executeQuery() and runQuery() answer false without a source rather than
raising.

Console sender: enough for the console to replay the request that failed

  • The request body, its content type and the safe headers. A JSON API call
    has an empty $_POST — the body is a stream PHP never parses into fields — so
    the console could only replay such a write as a bare method and URL, which is
    a different request wearing the same name. The body now travels (16 KB, never
    multipart, skipped on GET), scrubbed by Logger::removeText(), which reads
    the same secret names out of the punctuation a body is written in and
    honours a project's own addRemove().
  • announceRelease() — the deploy step tells the console a release shipped,
    synchronously, answering false rather than throwing when the sender is off
    or the console is out of reach.
  • context.userId, context.status, runtime / entry / kind
    beside the legacy type, and an environment field carrying the deploy
    stage beside the release. The log_level gate keys on kind, so security
    events pass like 404s do.
  • auth_success joins the closed security vocabulary — the login that
    worked after the ones that failed.
  • The release falls back to the .release stamp when none is configured.
  • Username and e-mail masks now say how long the original value was.

Rollup

Request durations ride the fragment as histograms, and a text/event-stream
response counts as traffic rather than as a duration — an SSE stream that stays
open for an hour is not an hour-long request.

Container, Dir, Strings, Config

  • Container::injectMissing() — complete an object built with new without
    overwriting what it already holds. injectObject() resolves every
    #[Inject] property, so an object given part of its state by hand loses it;
    this resolves only what is still uninitialised.
  • Dir::isAbsolute() — POSIX, Windows drive and UNC roots in one place, so
    "resolve this configured path against the base directory or not" stops
    depending on which OS runs the code.
  • Dir::getFiles() returns one order on every filesystem.
  • Strings::displaySafe() — the control-byte wall for untrusted display
    text, for surfaces with no escaping layer: terminals, logs, plain-text mail.
    ESC, CR/LF, backspace, DEL and NUL out; markup passes untouched, because
    encoding belongs to the renderer.
  • Config — an unknown ENV names itself, and .env reaches APCu only once
    environments.yml has resolved.

The repository is public

Released under PolyForm Noncommercial 1.0.0 with a SECURITY.md and private
vulnerability reporting. The README is written to be read rather than adopted,
the test fixtures no longer name real people, addresses or articles, and the
installation instructions no longer open with an SSH deploy key — the packages
are fetched over HTTPS.