Repository navigation
chore(phpstan): raise level to max - #76
Merged
Merged
Conversation
Narrow untyped values from the YAML config and the seo field JSON through a new ConfigValue helper instead of passing mixed into string-typed code. Behaviour changes, each covered by tests and documented in ConfigValue, config.yaml and the README: - override, seo field and default values: a number is used as text, '0' is a value, and true/false/lists/maps count as not set. Before, a number or list caused a TypeError in most getters, true/lists printed 1/Array in others, and empty() skipped '0' in overrides and defaults. - an override entry that is not a map, a Twig record global that is not a Content, a _route that is not text and a listing route without a content type slug are ignored instead of causing a TypeError. - templates.meta that is not text keeps the default template. - a title separator or content type name that is not text is left out (like a missing one) instead of printing 1/Array. - fields: a single name (title: headline) is accepted, entries that are not text are skipped, and a field definition that is missing moves on to the next configured name instead of ending the lookup.
Level max is level 10 on PHPStan 2: values from Bolt's untyped APIs are narrowed before use. getExtras() is now called once instead of twice. Override and seo field arrays are typed array<mixed>, since YAML and JSON keys are not guaranteed to be strings. Behaviour changes, each covered by tests and documented in code comments, config.yaml and the README: - Bolt's sitename and payoff follow the ConfigValue rules: a number is used as text (it was a TypeError), and other values that are not text count as empty. - a title_postfix that is not text (true, a list), or an empty postfix falling back to a sitename that is not text, means no postfix. true used to print 1. The same goes for the admin snippet preview. - the record extras image is skipped when it has no url that is text, or is not a map, instead of causing a TypeError. - the SEO injector widget injects nothing when its extension is not a Bolt BaseExtension, instead of causing an Error.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Raises PHPStan from level 8 to
max(level 10 on PHPStan 2), one commit per level. Most of the new errors come from values read from the YAML config, the JSON in a record'sseofield, and Bolt's untyped APIs. These were passed into string-typed code unchecked, so a value of the wrong type could take the page down with a TypeError. A newConfigValuehelper checks the type first. A wrong type now counts as not set, so the next fallback is used.A valid config renders exactly as before.
Rules for config and
seofield valuesDocumented in
ConfigValue,config/config.yamland the README:'0';title: 2024) is used as text;true,false, a list or a map counts as not set, like"".Behaviour changes
Each one is covered by a test that fails on
main.Level 9 (
chore(phpstan): raise level to 9)seofield anddefaultvalues follow the rules above. Before, a number or a list crashed most getters with a TypeError.trueor a list printed1/Arrayin others, andempty()skipped'0'in overrides and defaults.homepage: 'Home') is ignored instead of crashing.recordglobal: one that is not aContentis ignored instead of crashing._route: a non-text value is treated as no route instead of crashing.templates.meta: a non-text value keeps the default template. Before, it broke every request, including the admin login.1/Array.fields:title: headline);Level 10 (
chore(phpstan): raise level to max)sitenameandpayofffollow the same rules. A numeric site name used to crash.title_postfix: trueor a list means no postfix.trueused to append| 1. The admin snippet preview behaves the same.getExtras()) is skipped when it has nourlthat is text, instead of crashing.SeoInjectorWidgetinjects nothing when its extension is not a BoltBaseExtension, instead of an Error.Unchanged on purpose: without a matched route, an override keyed
''still applies, because PHP has always looked up a null key as''.Performance
The front-end path is unchanged. I benchmarked
mainagainst this branch with real Twig, a realRequestand real config collections:mainSeometatags()including the Twig rendergetExtras(), which builds every link and excerpt of the record, is now called once instead of twice when the image falls back to it.Verified
composer test: 336 tests, OKmake cscheck(ECS and PHPStan atmax): no errorsvendor/bin/rector process --dry-run: no changesmainand this branch. Pages: homepage,nlhomepage, pages with and without SEO data, an entry, listings, a taxonomy page, search and a 404.locales): the rendered<head>and all seven values are identical on both. So are the admin edit and new forms (snippet preview data, field mapping, injected assets).seofield holds wrongly typed values: 500 onmain, rendered with the fallbacks above on this branch.main, including the admin login. Every page renders as documented on this branch.title_postfix: true: the only difference frommainis the missing| 1.