Tools

Repository Author Commit message Committed SHA  
 
phpstan-rules by dg nette/database row type narrowing (table/related/ref/insert)

Narrows return types based on a configurable table-to-entity-class
mapping (nette.database.mapping.tables), which is not yet part of the
nette/database distribution. Kept as the topmost commit so the whole
feature can be excluded or rebased independently.

Includes:
- TableRowTypeResolver shared resolver
- Explorer::table() -> Selection<EntityRow>
- ActiveRow::related() -> GroupedSelection<EntityRow>
- ActiveRow::ref() -> ?EntityRow
- Selection::insert() -> mapped EntityRow

phpstan-rules by dg added ExpectTypeReturnTypeExtension for Expect::type()

Expect::type() declares Type (Type|AnyOf since schema 2.0), while the element
it returns depends on the expression: 'int' is a NumberType with min() and
max(), 'int|string' an AnyOf. The extension resolves a constant expression by
asking Expect::type() itself, so it needs no knowledge of the expression
language and its answer follows whatever the installed nette/schema builds; a
non-constant expression, or one the installed version refuses, keeps the
declared type.

phpstan-rules by dg ExpectArrayReturnTypeExtension answers the plain array element instead of Type

Since nette/schema 1.4 an Expect::array() without a shape is an ArrayType,
not a plain Type, so the extension asks Expect::array() for the class the
same way the Expect::type() one does; up to 1.3 the answer stays Type.

phpstan-rules by dg fixed static analysis with PHPStan 2.3
phpstan-rules by dg uses dresscode
phpstan-rules by dg cs
phpstan-rules by dg cs: grouped use statements
phpstan-rules by dg cs: only compiler-optimized functions in use-function imports
code-checker by dg added AGENTS.md
code-checker by dg uses nette/command-line 2.0

The command line is defined by Command and read by Parser, the help is drawn by
HelpRenderer. The two ignored switches are defined as hidden switches. The
requirement reads dev-master until 2.0 is tagged.

Everything a person reads goes through one Console built in the bootstrap and
handed to Checker and to HelpRenderer, so the colors, the width and the answer
whether this is a terminal are decided once, by the stream itself; the static
Console::detectTerminal() the progress used to be gated on is gone. A fatal error
goes through it too, which is how it erases whatever was drawn in place.

The progress dots are a status of that console: it cuts them to the width of the
terminal, draws nothing when the output is redirected, gives the cursor back when
the process dies, and the next line written erases them, so neither the padding to
a fixed forty columns nor the carriage return is needed any more.

A message of a task is stripped of escape sequences before it is colored, since it
may quote the file being checked.

code-checker by dg uses dresscode
code-checker by dg cs: grouped use statements
code-checker by dg cs: suppression comments
code-checker by dg cs
code-checker by dg removed strictTypesDeclarationChecker
code-checker by dg tabIndentationChecker replaced by tabIndentationFixer

Indentation with spaces is converted to tabs and a tabulator inside a line
to a space. The indentation width is detected from the file, rare depths
are ignored so that a mistyped line does not shrink it. A file whose
indentation does not fit the detected width is reported as an error and
left untouched.

code-checker by dg CI: switched Coveralls upload to the official action

The php-coveralls phar is replaced by coverallsapp/github-action, which
resolves the token and the absolute paths in the Clover report on its
own, so tests/.coveralls.yml is no longer needed.

Coverage is now uploaded even when tests crash under phpdbg. Being
phpdbg-compatible is not a goal, but until now the failing step aborted
the job before the upload ever ran. The job-level continue-on-error is
gone, so genuine failures, including a rejected upload, are visible.

code-checker by dg requires PHP 8.2
command-line by dg Prompt asks the user at a terminal: choose() reads one of the choices

A command line that asks has to know whether anybody is there to answer, so the prompt
holds the console it writes the question to and the stream the answer comes from, and
asks only when both are a terminal: an answer must never come from a pipe nobody
watches, and isInteractive() tells the application beforehand.

choose() asks until the answer is one of the choices, in any case, and gives it back as
it was declared. An empty answer is the default, and without one it asks again; the end
of the input is null, so a script reading a closed stream is not left in a loop. Only
the default is shown in capitals, so that no other choice looks like it, and a map of a
choice to its description is drawn as a menu above the question, which is not repeated.

The console marks the line the terminal moved the cursor to by echoing the answer, so a
status drawn afterwards does not erase a half-written line.

command-line by dg added objects of options: a parameter typed by a class whose constructor declares inputs is built from them

A handler with a dozen inputs has a dozen parameters, and the ones that belong
together travel together through every layer below it. A parameter typed by a class
whose constructor carries #[Flag], #[Option] or #[Argument] is an object of options
instead: its inputs are defined and bound exactly as the parameters of the handler are,
and the object is built for the call, so the handler takes one argument and passes it
on as a whole.

A class that cannot be created, or one that contains itself, is an error of the
definition, and so is an object of options on a handler given to addCommand(), which
reads no attributes at all.

A normalizer written in an attribute may also be a static method as [class, name],
which every PHP can write there, since a closure in an attribute needs PHP 8.5.

command-line by dg added attributes: a handler describes its command by #[Command] and its inputs by #[Flag], #[Option] and #[Argument]

A handler bound by name still needs its inputs defined somewhere above it, which for a
command of its own means writing the same names twice. With the attributes the handler
is the definition: addCommandFrom() reads #[Command] from the method and the inputs
from its parameters, in their order, and addCommandsFrom() does it for every public
method of an object that carries #[Command].

The name of the command is derived from the method, migrateSuppressions() is
migrate-suppressions, and name: of the attribute overrides it; a closure, which has no
name to derive, has to give one. The name of an input is derived the same way, $dryRun
is --dry-run, the type of the parameter gives the enum when it is a backed one, and its
default value becomes the default of the input, which an argument takes as being
optional.

The attributes are only read by addCommandFrom(), so a handler given to addCommand()
that carries one is refused rather than silently ignored. They are read through
reflection once, at definition time, and what they declare goes through the same
add*() methods as a definition written by hand, so nothing is checked twice or checked
differently.

command-line by dg added handlers: a command holds a closure that ParseResult::run() calls with its inputs bound by name

A command line that dispatches by hand is a match over the command and a read of every
value; a handler says it once. The command holds the closure, the parser never calls
it, and ParseResult::run() does, returning its exit code, 0 when it returns nothing.

Each parameter is given the input it is named after, $maxWarnings the option
--max-warnings or the argument max-warnings, or this result or the command when it is
typed by one of them. HandlerBinding checks the binding before anything runs: that
every parameter has exactly one input, and that it accepts every value that input can
give, the default and the sentinel of an optional value included. The call itself is
direct, never through reflection, which would coerce the values the type checking just
made sure about.

The binding is checked for the whole tree when parsing, since an input a handler binds
to may be added after the handler; the signature alone, which cannot depend on the
definitions, is refused as soon as the handler is given.

command-line by dg Normalizers: added int(), float() and realPath()

The normalizers an application writes over and over, ready made: a number refused
unless it is written as one and within the range, and a path resolved to an absolute
one or refused as not found. They report a bad value the way any normalizer does, by
throwing with a message that fits after the name of the parameter, so the parser turns
it into a ParseException of its own.

The range is checked when the normalizer is built, not when it runs, so a minimum
greater than its maximum is an error of the definition like any other.

command-line by dg Parser: expanded bundled short flags and read a negative number as a value

A token like -abc is replaced by -a -b -c and read again, but only when it is a clean
bundle: every character a defined single-letter flag, with a value allowed for the last
one alone. So a defined multi-letter short option such as -vf stays itself, and a token
that is no bundle is reported as the unknown option it looked like.

A negative number such as -5 or -.5 is a value, not an option, since that is what a
user writing `--offset -5` means. Unless an option is named like a number: then every
-5 is an option, as in argparse, and this is decided by the options valid at the
command the line has reached, not by the program as a whole. An option that demands a
value and finds a number is told that `--count=-5` gives it.

command-line by dg Parser: suggested a close match for an unknown option or command, and named the owners of an option

A name nobody knows is most often a typo, so the message offers the nearest candidate
when one is close enough, measured by the edit distance and only where the name is long
enough that the nearest is not just any other short flag.

An option that is defined, but belongs to another command, is a different mistake and
gets its own message: it names every command that owns an option of that name, so that
a user who wrote it beside the wrong command sees where it belongs. A hidden option is
never named and never suggested, since the help does not show it either.

command-line by dg Flag: added standalone, the flag answers on its own like --help

--help has to work before the configuration is read, the paths are resolved and the
locks are taken, so a standalone flag answers between the first and the second phase,
once the command is selected: the flag is true, the other flags are taken as given,
since they need no conversion, and everything else is its default, nothing validated
or converted. A required argument is not demanded either, and a command that requires
a subcommand does not complain.

The line still has to be a line: an unknown option or a value given to a flag is an
error whatever answers. A negated flag does not answer, since --no-help asks for
nothing.

command-line by dg Parser: followed the commands down the tree

The line is always read from the root: the first token that is no option picks a
command, and from there the options of that command and of the ones above it are valid
on both sides of its name. Given a subcommand, parse() still reads the whole line and
refuses one that runs another command, so a caller cannot be handed values meant for
somewhere else.

A command that requires one of its subcommands and is given none is a missing command,
and a token that names no command is an unknown one. The exception carries the command
the line had reached, so the help drawn beside it is the help of that command.

command-line by dg Parser: added, which reads the command line against the definition

parse() takes a Command and returns a ParseResult, and what the line said is read from
it like from an array. The parser keeps no state and checks no definition, since a
Command refuses what is wrong when it is built.

parse() works in three phases: collect the raw occurrences, convert what was supplied,
fill in what was not. Presence is recorded apart from the value, so a normalizer may
return null and an optional value is independent of the default one. A bare option
yields the OptionPresent sentinel, which neither the enum check nor the normalizer
sees, and only the last value of a parameter that is not repeatable is checked at all.
The value itself is checked and converted by its own parameter, which does it for any
caller, not only for the parser.

A line that does not match the definition raises a ParseException, which says what was
wrong as a ParseFailure and which parameter and token it concerns. Reading the result
is strict where an array is not: an unknown name is refused, so that a typo cannot
pass for a missing value, while isset() answers false for a known name with null.

command-line by dg HelpRenderer: added, which draws the help of a command from its definition

The help is drawn from the ordered items of a command, with a heading in front of
every run of one kind where the author wrote none, and the options it inherits at the
end. The usage is generated from the definitions unless the command gives its own
lines, descriptions are wrapped to the width of the terminal and a syntax too long for
the column keeps a line of its own rather than stretching the column for everybody.

The colored output is the plain one wrapped in escape sequences, so stripping them
gives the plain one back; the renderer colors through roles of its own, which a theme
maps to colors, and the console knows no roles. Code written in backticks as Markdown
does holds the role of code, which takes the place of the backticks where there are
colors and stays as it is where a pipe needs to tell the code from the words; a color
never spans two lines. formatCode() writes such a span, so that every message of the
library speaks the same way, and splitCodeSpans() reads it back.

renderError() writes a UsageException, the error of a command line used wrongly,
followed by the help of the command in which it went wrong.

command-line by dg Console: added setStatus(), which draws lines in place

The status owns the drawing in place, so no caller moves the cursor itself: a line too
long is cut, since a wrapped one cannot be redrawn, the cursor is hidden while the
status stands and the next output of that console erases it. A status lives as long as
its console, and a shutdown function holding a weak reference restores the cursor
after a fatal error without keeping the console alive.

command-line by dg Console: one console per stream, which writes in the colors the stream takes

A console is one stream, and its colors, width and terminal follow that stream, so an
application writing to stdout and stderr makes one for each. The depth of colors is
asked from the stream and honors NO_COLOR, FORCE_COLOR and COLORTERM; what the
application says wins over all.

hasColors() and isTerminal() are separate on purpose: color is gated on the first,
interactive-only features on the second, since a user may disable color yet still sit
at a real terminal. The console writes what it is given, so color() and link() are the
only places that add a sequence, and text from elsewhere is filtered by the caller.

A color is a name of the sixteen the terminal draws in the colors of its theme, a
number 0-255 of the extended palette or an RGB written as a hex. Color knows the RGB
of each, a name its aixterm SGR code and an index its number, and toSgr() gives the
parameters the depth allows: the code of a name, the index where the palette is taken,
an RGB exactly in true color and otherwise the nearest color there is. The nearest of
the sixteen is found by hue and lightness, since a plain distance of RGB gives most
pastel colors a gray.

command-line by dg docs: added internals, how the library works

The classes are small and mostly clear from their signatures, so the internals keep
only what an agent or a reader would get wrong or waste time rediscovering: the three
phases of parse() with the command selection and the sentinel of a bare option, what
isset() on ParseResult answers, how a handler binds and what is checked when, and the
two questions a console answers about its stream. The how-to of add*() and the format
of the help belongs to the manual, not here.

AGENTS.md is rewritten for the same classes and points at docs/ instead of describing
the parser of 1.x.

Last synchronization: 2026-10-14 07:02:41