From a4a40e7122470efa0b596b189bed0def3a6f7da7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:07:46 +0000 Subject: [PATCH] Version v0.18.0-dev --- VERSION | 2 +- asset/css/action-link-and-button-link.less | 26 ++ asset/css/balls.less | 18 +- asset/css/compat.less | 16 +- asset/css/controls.less | 6 +- asset/css/empty-state.less | 6 +- asset/css/icinga-icons.less | 36 +-- asset/css/item-layout.less | 115 ++++--- asset/css/mixin/mixins.less | 5 + asset/css/schedule-element.less | 45 ++- asset/css/search-bar.less | 40 +-- asset/css/search-base.less | 6 +- asset/css/search-editor.less | 12 +- asset/css/suggestion-element.less | 29 ++ asset/css/variables.less | 8 +- asset/js/widget/FilterInput.js | 18 +- composer.lock | 238 +++++++++----- vendor/autoload.php | 7 +- vendor/brick/math/composer.json | 6 +- vendor/brick/math/src/BigDecimal.php | 143 ++++++--- vendor/brick/math/src/BigInteger.php | 201 ++++++++---- vendor/brick/math/src/BigNumber.php | 127 +++++--- vendor/brick/math/src/BigRational.php | 71 +++-- .../src/Exception/DivisionByZeroException.php | 8 +- .../Exception/IntegerOverflowException.php | 4 +- .../src/Exception/NegativeNumberException.php | 2 +- .../src/Exception/NumberFormatException.php | 7 +- .../Exception/RoundingNecessaryException.php | 4 +- vendor/brick/math/src/Internal/Calculator.php | 123 ++++--- .../Internal/Calculator/BcMathCalculator.php | 4 +- .../src/Internal/Calculator/GmpCalculator.php | 9 +- .../Internal/Calculator/NativeCalculator.php | 51 +-- .../math/src/Internal/CalculatorRegistry.php | 73 +++++ vendor/composer/InstalledVersions.php | 45 ++- vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_psr4.php | 2 +- vendor/composer/autoload_real.php | 10 +- vendor/composer/autoload_static.php | 15 +- vendor/composer/installed.json | 190 +++++------ vendor/composer/installed.php | 116 ++++--- vendor/composer/platform_check.php | 5 +- vendor/guzzlehttp/psr7/composer.json | 2 +- vendor/guzzlehttp/psr7/src/MessageTrait.php | 4 - vendor/guzzlehttp/psr7/src/Utils.php | 4 +- vendor/ipl/html/composer.json | 2 +- vendor/ipl/html/src/Attribute.php | 25 +- vendor/ipl/html/src/Attributes.php | 24 +- vendor/ipl/html/src/BaseHtmlElement.php | 66 +--- vendor/ipl/html/src/Contract/Decorator.php | 55 ++++ .../html/src/Contract/DecoratorOptions.php | 40 +++ .../Contract/DecoratorOptionsInterface.php | 19 ++ .../Contract/DefaultFormElementDecoration.php | 39 +++ vendor/ipl/html/src/Contract/FormElement.php | 19 +- .../src/Contract/HtmlElementInterface.php | 90 ++++++ vendor/ipl/html/src/Contract/MutableHtml.php | 90 ++++++ vendor/ipl/html/src/Contract/Wrappable.php | 3 + vendor/ipl/html/src/Form.php | 12 +- .../html/src/FormDecorator/DdDtDecorator.php | 3 +- .../src/FormDecorator/DecorationResults.php | 161 ++++++++++ .../html/src/FormDecorator/DecoratorChain.php | 299 ++++++++++++++++++ .../FormDecorator/DescriptionDecorator.php | 80 +++++ .../src/FormDecorator/ErrorsDecorator.php | 69 ++++ .../src/FormDecorator/FieldsetDecorator.php | 46 +++ .../src/FormDecorator/HtmlTagDecorator.php | 189 +++++++++++ .../html/src/FormDecorator/LabelDecorator.php | 76 +++++ .../FormDecorator/RenderElementDecorator.php | 23 ++ .../html/src/FormDecorator/Transformation.php | 18 ++ .../html/src/FormElement/BaseFormElement.php | 42 ++- .../html/src/FormElement/FieldsetElement.php | 8 +- .../ipl/html/src/FormElement/FormElements.php | 108 ++++++- .../ipl/html/src/FormElement/TextElement.php | 36 +++ vendor/ipl/html/src/HtmlDocument.php | 132 +++----- vendor/ipl/sql/src/Connection.php | 20 ++ vendor/ipl/sql/src/Test/TestConnection.php | 49 +++ vendor/ipl/validator/composer.json | 6 +- vendor/ipl/validator/src/BetweenValidator.php | 29 +- .../ipl/validator/src/CallbackValidator.php | 2 +- vendor/ipl/validator/src/CidrValidator.php | 4 + .../ipl/validator/src/DateTimeValidator.php | 6 +- .../src/DeferredInArrayValidator.php | 6 +- .../validator/src/EmailAddressValidator.php | 5 +- vendor/ipl/validator/src/FileValidator.php | 37 ++- .../validator/src/GreaterThanValidator.php | 17 +- .../ipl/validator/src/HostnameValidator.php | 4 +- vendor/ipl/validator/src/InArrayValidator.php | 14 +- .../ipl/validator/src/LessThanValidator.php | 17 +- .../ipl/validator/src/PrivateKeyValidator.php | 7 +- .../validator/src/StringLengthValidator.php | 35 +- vendor/ipl/validator/src/ValidatorChain.php | 26 +- .../ipl/validator/src/X509CertValidator.php | 6 +- vendor/ipl/web/composer.json | 2 +- .../ipl/web/src/Common/CsrfCounterMeasure.php | 55 ++++ .../web/src/Control/SearchBar/Suggestions.php | 6 +- .../ipl/web/src/Control/SearchBar/Terms.php | 8 +- vendor/ipl/web/src/Control/SortControl.php | 8 +- .../web/src/FormElement/SearchSuggestions.php | 279 ++++++++++++++++ .../web/src/FormElement/SuggestionElement.php | 87 +++++ .../FormElement/TermInput/TermSuggestions.php | 279 +--------------- vendor/ipl/web/src/Layout/Controls.php | 2 +- vendor/ipl/web/src/Widget/Ball.php | 2 +- vendor/ipl/web/src/Widget/ButtonLink.php | 45 ++- vendor/ramsey/uuid/composer.json | 2 +- .../uuid/src/Generator/UnixTimeGenerator.php | 8 +- .../uuid/src/Math/BrickMathCalculator.php | 19 +- vendor/symfony/polyfill-php84/Php84.php | 40 +++ .../Resources/stubs/ReflectionConstant.php | 158 +++++++++ vendor/symfony/polyfill-php84/bootstrap.php | 14 + vendor/symfony/polyfill-php84/bootstrap82.php | 20 ++ 108 files changed, 3717 insertions(+), 1241 deletions(-) create mode 100644 asset/css/action-link-and-button-link.less create mode 100644 asset/css/suggestion-element.less create mode 100644 vendor/brick/math/src/Internal/CalculatorRegistry.php create mode 100644 vendor/ipl/html/src/Contract/Decorator.php create mode 100644 vendor/ipl/html/src/Contract/DecoratorOptions.php create mode 100644 vendor/ipl/html/src/Contract/DecoratorOptionsInterface.php create mode 100644 vendor/ipl/html/src/Contract/DefaultFormElementDecoration.php create mode 100644 vendor/ipl/html/src/Contract/HtmlElementInterface.php create mode 100644 vendor/ipl/html/src/Contract/MutableHtml.php create mode 100644 vendor/ipl/html/src/FormDecorator/DecorationResults.php create mode 100644 vendor/ipl/html/src/FormDecorator/DecoratorChain.php create mode 100644 vendor/ipl/html/src/FormDecorator/DescriptionDecorator.php create mode 100644 vendor/ipl/html/src/FormDecorator/ErrorsDecorator.php create mode 100644 vendor/ipl/html/src/FormDecorator/FieldsetDecorator.php create mode 100644 vendor/ipl/html/src/FormDecorator/HtmlTagDecorator.php create mode 100644 vendor/ipl/html/src/FormDecorator/LabelDecorator.php create mode 100644 vendor/ipl/html/src/FormDecorator/RenderElementDecorator.php create mode 100644 vendor/ipl/html/src/FormDecorator/Transformation.php create mode 100644 vendor/ipl/web/src/FormElement/SearchSuggestions.php create mode 100644 vendor/ipl/web/src/FormElement/SuggestionElement.php create mode 100644 vendor/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php create mode 100644 vendor/symfony/polyfill-php84/bootstrap82.php diff --git a/VERSION b/VERSION index 6b60281..1e3f60f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.17.0 +v0.18.0-dev diff --git a/asset/css/action-link-and-button-link.less b/asset/css/action-link-and-button-link.less new file mode 100644 index 0000000..d654f66 --- /dev/null +++ b/asset/css/action-link-and-button-link.less @@ -0,0 +1,26 @@ +.action-link { + color: var(--control-color, @control-color); +} + +.button-link { + .action-link(); + .rounded-corners(3px); + + background: var(--default-input-bg, @default-input-bg); + display: inline-block; + padding: 0.25em 0.5em; + + &:hover { + background: var(--default-input-hover-bg, @default-input-hover-bg); + text-decoration: none; + } + + &[aria-disabled="true"] { + background: none; + border: 1px solid var(--control-disabled-color, @control-disabled-color); + color: var(--control-disabled-color, @control-disabled-color); + cursor: not-allowed; + + .user-select(none); + } +} diff --git a/asset/css/balls.less b/asset/css/balls.less index c6d5445..70ef391 100644 --- a/asset/css/balls.less +++ b/asset/css/balls.less @@ -30,7 +30,7 @@ width: 0.75em; line-height: 0; - i.icon:before { + i.icon::before { font-size: .75 - @ball-pad * 2; line-height: 1em; } @@ -48,7 +48,7 @@ i.icon { line-height: 0.3; - &:before { + &::before { font-size: 0.8 - @ball-pad * 2; line-height: 1 - @ball-pad * 2; } @@ -64,7 +64,7 @@ width: 1.5em; line-height: 1em; - i.icon:before, span { + i.icon::before, span { font-size: 1 - @ball-pad * 2; line-height: 1.5 - @ball-pad * 2; } @@ -74,7 +74,7 @@ width: 2em; height: 2em; - i.icon:before, span { + i.icon::before, span { line-height: 2 - @ball-pad * 2; } } @@ -92,7 +92,7 @@ .state-ball { .ball(); - &.state-pending:not(.ball-size-l):not(.ball-size-xl) { + &.state-pending:not(.ball-size-l, .ball-size-xl) { .ball-solid(var(--state-pending, @state-pending)); } @@ -101,7 +101,7 @@ .ball-outline(var(--state-pending, @state-pending)); } - &.state-up:not(.ball-size-l):not(.ball-size-xl) { + &.state-up:not(.ball-size-l, .ball-size-xl) { .ball-solid(var(--state-up, @state-up)); } @@ -114,7 +114,7 @@ .ball-solid(var(--state-down, @state-down)); } - &.state-ok:not(.ball-size-l):not(.ball-size-xl) { + &.state-ok:not(.ball-size-l, .ball-size-xl) { .ball-solid(var(--state-ok, @state-ok)); } @@ -149,13 +149,13 @@ // Specific icon styles &.ball-size-l i { - &.fa-sitemap:before { + &.fa-sitemap::before { font-size: 8px; // px to ignore browser min font-size } } &.ball-size-xl i { - &.fa-sitemap:before { + &.fa-sitemap::before { font-size: .857em; line-height: (2 - @ball-pad * 2) / .857; } diff --git a/asset/css/compat.less b/asset/css/compat.less index 759b380..f29dc67 100644 --- a/asset/css/compat.less +++ b/asset/css/compat.less @@ -2,10 +2,15 @@ .icinga-controls { .uploaded-files { - background-color: @default-input-bg; + background-color: var(--default-input-bg, @default-input-bg); } } +// Icinga Web < 2.13 +.button-link[aria-disabled="true"]:hover { + background: none; +} + form.icinga-form { .uploaded-files { flex: 1 1 auto; @@ -87,6 +92,15 @@ form.icinga-form .control-group { } } +// suggestion-element style +form.icinga-form .suggestion-element-group { + flex: 1 1 auto; + + .suggestion-element { + border-radius: 0 0.25em 0.25em 0; + } +} + .module-icingadb { // Icinga DB Web (legacy) table header layout (e.g. in group details) > .controls { diff --git a/asset/css/controls.less b/asset/css/controls.less index a37083c..ed27794 100644 --- a/asset/css/controls.less +++ b/asset/css/controls.less @@ -16,7 +16,7 @@ display: block; } - i:before { + i::before { margin: 0; } } @@ -52,7 +52,7 @@ } } - i.icon:before { + i.icon::before { color: inherit; } } @@ -69,7 +69,7 @@ height: 100%; } - i.icon:before { + i.icon::before { margin-right: 0; } } diff --git a/asset/css/empty-state.less b/asset/css/empty-state.less index 8650ebc..75879d8 100644 --- a/asset/css/empty-state.less +++ b/asset/css/empty-state.less @@ -1,5 +1,5 @@ .empty-state { - color: @empty-state-color; + color: var(--empty-state-color, @empty-state-color); } .empty-state-bar { @@ -7,6 +7,6 @@ text-align: center; .rounded-corners(); - background-color: @empty-state-bar-bg; - color: @default-text-color; + background-color: var(--empty-state-bar-bg, @empty-state-bar-bg); + color: var(--default-text-color, @default-text-color); } diff --git a/asset/css/icinga-icons.less b/asset/css/icinga-icons.less index 5b631f9..5888217 100644 --- a/asset/css/icinga-icons.less +++ b/asset/css/icinga-icons.less @@ -1,5 +1,5 @@ @font-face { - font-family: 'Icinga-Icons'; + font-family: Icinga-Icons; src: url('@{iplWebAssets}/font/icinga-icons/fonts/Icinga-Icons.ttf') format('truetype'), url('@{iplWebAssets}/font/icinga-icons/fonts/Icinga-Icons.woff') format('woff'), url('@{iplWebAssets}/font/icinga-icons/fonts/Icinga-Icons.svg') format('svg'); @@ -8,9 +8,9 @@ font-display: block; } -[class^="iicon-"]:before, [class*=" iicon-"]:before { +[class^="iicon-"]::before, [class*=" iicon-"]::before { /* use !important to prevent issues with browser extensions that change fonts */ - font-family: 'Icinga-Icons'; + font-family: Icinga-Icons; speak: none; font-style: normal; font-weight: normal; @@ -23,42 +23,42 @@ -moz-osx-font-smoothing: grayscale; } -.iicon-certificate:before { +.iicon-certificate::before { content: "\e906"; } -.iicon-filter-check-circle:before { +.iicon-filter-check-circle::before { content: "\e90b"; } -.iicon-ca-check-circle:before { +.iicon-ca-check-circle::before { content: "\e908"; } -.iicon-refresh-cert:before { +.iicon-refresh-cert::before { content: "\e909"; } -.iicon-th-list:before { +.iicon-th-list::before { content: "\e90a"; } -.iicon-icinga:before { +.iicon-icinga::before { content: "\e907"; } -.iicon-minimal:before, -.iicon-list-view-minimal:before { +.iicon-minimal::before, +.iicon-list-view-minimal::before { content: "\e900"; } -.iicon-detailed:before, -.iicon-list-view-detailed:before { +.iicon-detailed::before, +.iicon-list-view-detailed::before { content: "\e901"; } -.iicon-default:before, -.iicon-list-view-default:before { +.iicon-default::before, +.iicon-list-view-default::before { content: "\e902"; } -.iicon-grid:before { +.iicon-grid::before { content: "\e903"; } -.iicon-bracket-open:before { +.iicon-bracket-open::before { content: "\e904"; } -.iicon-bracket-close:before { +.iicon-bracket-close::before { content: "\e905"; } diff --git a/asset/css/item-layout.less b/asset/css/item-layout.less index 9a96554..0d5ef68 100644 --- a/asset/css/item-layout.less +++ b/asset/css/item-layout.less @@ -50,11 +50,12 @@ header { display: flex; - align-items: flex-start; + align-items: baseline; justify-content: space-between; - } - &.minimal-item-layout > .main > header { - max-width: 100%; + + > .extended-info { + flex-shrink: 0; + } } .caption { @@ -66,29 +67,6 @@ max-height: 1em; } } - &.default-item-layout > .main > .caption { - height: 1.5em; - - .text-ellipsis(); - } - &.minimal-item-layout > .main > header > .caption { - flex: 1 1 auto; - height: 1.5em; - width: 0; - - &:not(:empty) { - margin-right: 1em; - } - - .text-ellipsis(); - } - &.detailed-item-layout > .main > .caption { - display: block; - overflow: hidden; - position: relative; - - .line-clamp(5); - } footer { display: flex; @@ -100,33 +78,72 @@ .title { margin-right: 1em; } - &.default-item-layout > .main > header > .title { - .flowing-content("default"); - } - &.detailed-item-layout > .main > header > .title { - .flowing-content("detailed"); - word-break: break-word; - -webkit-hyphens: auto; - -ms-hyphens: auto; - hyphens: auto; + &.minimal-item-layout > .main > header { + max-width: 100%; + height: 1.5em; + + > .caption { + flex: 1 1 auto; + height: 1.5em; + width: 0; + + &:not(:empty) { + margin-right: 1em; + } + + .text-ellipsis(); + } } - header > .extended-info { - flex-shrink: 0; + &.default-item-layout > .main { + > header { + height: 1.5em; + + > .title { + .flowing-content("default"); + } + + > .extended-info { + .flowing-content("default"); + } + } + + > .caption { + height: 1.5em; + + .text-ellipsis(); + } } - &.default-item-layout > .main > header > .extended-info { - .flowing-content("default"); - } - &.detailed-item-layout > .main > header > .extended-info { - .flowing-content("detailed"); + + &.detailed-item-layout > .main { + > header { + > .title { + .flowing-content("detailed"); + + word-break: break-word; + hyphens: auto; + } + + > .extended-info { + .flowing-content("detailed"); + } + } + + > .caption { + display: block; + overflow: hidden; + position: relative; + + .line-clamp(5); + } } } // Style .item-layout { - color: @default-text-color-light; + color: var(--default-text-color-light, @default-text-color-light); .caption { i { @@ -134,25 +151,25 @@ } a { - color: @default-text-color; + color: var(--default-text-color, @default-text-color); } } .title { .subject { - color: @default-text-color; + color: var(--default-text-color, @default-text-color); } a { - color: @default-text-color; + color: var(--default-text-color, @default-text-color); font-weight: bold; &:hover { - color: @link-hover-color; + color: var(--link-hover-color, @link-hover-color); text-decoration: none; .subject { - color: @link-hover-color; + color: var(--link-hover-color, @link-hover-color); } } } diff --git a/asset/css/mixin/mixins.less b/asset/css/mixin/mixins.less index 58b9567..e82fb37 100644 --- a/asset/css/mixin/mixins.less +++ b/asset/css/mixin/mixins.less @@ -39,3 +39,8 @@ .monospace-font() { font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; } + +.user-select(@user-select) { + -webkit-user-select: @user-select; + user-select: @user-select; +} diff --git a/asset/css/schedule-element.less b/asset/css/schedule-element.less index 1905a05..abaa352 100644 --- a/asset/css/schedule-element.less +++ b/asset/css/schedule-element.less @@ -19,7 +19,7 @@ } &:disabled { - color: @schedule-element-fields-disabled-color; + color: var(--schedule-element-fields-disabled-color, @schedule-element-fields-disabled-color); } } } @@ -39,7 +39,7 @@ .monthly, .ordinal:not(.annually) { padding: .5em; margin-left: -.5em; - border: 1px solid @schedule-element-fields-border-color; + border: 1px solid var(--schedule-element-fields-border-color, @schedule-element-fields-border-color); .rounded-corners(.75em); } @@ -56,13 +56,13 @@ pointer-events: none; label { - color: @schedule-element-fields-disabled-color; - background-color: @schedule-element-fields-disabled-bg; + color: var(--schedule-element-fields-disabled-color, @schedule-element-fields-disabled-color); + background-color: var(--schedule-element-fields-disabled-bg, @schedule-element-fields-disabled-bg); } input:checked + label { - background: @schedule-element-fields-disabled-selected-bg; - color: @schedule-element-fields-disabled-color; + background: var(--schedule-element-fields-disabled-selected-bg, @schedule-element-fields-disabled-selected-bg); + color: var(--schedule-element-fields-disabled-color, @schedule-element-fields-disabled-color); } } @@ -75,11 +75,11 @@ cursor: pointer; text-align: center; padding: .75em 0; - background: @schedule-element-fields-bg; - color: @schedule-element-fields-color; + background: var(--schedule-element-fields-bg, @schedule-element-fields-bg); + color: var(--schedule-element-fields-color, @schedule-element-fields-color); &:hover { - background-color: @schedule-element-fields-hover-bg; + background-color: var(--schedule-element-fields-hover-bg, @schedule-element-fields-hover-bg); } &:focus { @@ -88,27 +88,27 @@ } input:checked + label { - background-color: @schedule-element-fields-selected-bg; - color: @schedule-element-fields-selected-color; + background-color: var(--schedule-element-fields-selected-bg, @schedule-element-fields-selected-bg); + color: var(--schedule-element-fields-selected-color, @schedule-element-fields-selected-color); } input:checked + label:hover { - background-color: @schedule-element-fields-selected-hover-bg; - border-color: @schedule-element-fields-selected-hover-bg; + background-color: var(--schedule-element-fields-selected-hover-bg, @schedule-element-fields-selected-hover-bg); + border-color: var(--schedule-element-fields-selected-hover-bg, @schedule-element-fields-selected-hover-bg); } } &.multiple-fields { li:not(:last-child) label { - border-right: 1px solid @schedule-element-fields-border-color; + border-right: 1px solid var(--schedule-element-fields-border-color, @schedule-element-fields-border-color); } input:focus + label { - box-shadow: inset 0 0 0 3px @schedule-element-fields-outline-color; + box-shadow: inset 0 0 0 3px var(--schedule-element-fields-outline-color, @schedule-element-fields-outline-color); } input:checked:focus + label { - box-shadow: inset 0 0 0 3px @schedule-element-fields-selected-outline-color; + box-shadow: inset 0 0 0 3px var(--schedule-element-fields-selected-outline-color, @schedule-element-fields-selected-outline-color); } } @@ -128,7 +128,7 @@ } &:focus-within { - outline: 3px solid @schedule-element-fields-outline-color; + outline: 3px solid var(--schedule-element-fields-outline-color, @schedule-element-fields-outline-color); outline-offset: 2px; } @@ -137,7 +137,7 @@ } input:checked + label:hover { - background-color: @schedule-element-fields-selected-bg; + background-color: var(--schedule-element-fields-selected-bg, @schedule-element-fields-selected-bg); } } } @@ -145,20 +145,17 @@ .note { display: none; padding: .5em; - background: @schedule-element-keyboard-note-bg; + background: var(--schedule-element-keyboard-note-bg, @schedule-element-keyboard-note-bg); .rounded-corners(.25em); text-align: center; margin-top: 1em; line-height: 1.25; } - /* .weekly */ - .weekly { } - /* .monthly styles */ .monthly { li label { - border-top: 1px solid @schedule-element-fields-border-color; + border-top: 1px solid var(--schedule-element-fields-border-color, @schedule-element-fields-border-color); } li:first-child, @@ -205,6 +202,6 @@ padding-top: 0.5625em; p { - color: @schedule-element-fields-disabled-color; + color: var(--schedule-element-fields-disabled-color, @schedule-element-fields-disabled-color); } } diff --git a/asset/css/search-bar.less b/asset/css/search-bar.less index 6ad1aec..deb5f9e 100644 --- a/asset/css/search-bar.less +++ b/asset/css/search-bar.less @@ -11,8 +11,8 @@ } // Submit button styles - input[type=submit], - button[type=submit], + input[type="submit"], + button[type="submit"], button:not([type]) { background: var(--primary-button-bg, @primary-button-bg); color: var(--primary-button-color, @primary-button-color); @@ -26,20 +26,20 @@ } // Hide the submit button, it must exist, but shouldn't be shown to the user - input[type=submit][value="hidden"] { + input[type="submit"][value="hidden"] { display: none; } // Left-most search dropdown style button.search-options { - i.icon:before { + i.icon::before { font-size: 1.2em; margin-right: 0; color: var(--control-color, @control-color); } &:disabled { - i.icon:before { + i.icon::before { color: var(--control-disabled-color, @control-disabled-color); } } @@ -52,7 +52,7 @@ background-color: var(--search-condition-remove-bg, @search-condition-remove-bg); color: var(--search-condition-remove-color, @search-condition-remove-color); - &:after { + &::after { content: ""; position: absolute; width: .4em; @@ -77,7 +77,7 @@ .terms > .filter-condition:first-child button { border-radius: 0 .4em .4em 0; - &:before { + &::before { content: ""; position: absolute; width: .4em; @@ -92,14 +92,14 @@ border-bottom-right-radius: .4em; } - &:after { + &::after { content: none; } } - .logical_operator, - .grouping_operator_open, - .grouping_operator_close { + .logical-operator, + .grouping-operator-open, + .grouping-operator-close { input { .rounded-corners(); background-color: var(--search-logical-operator-bg, @search-logical-operator-bg); @@ -108,9 +108,9 @@ } .operator, - .logical_operator, - .grouping_operator_open, - .grouping_operator_close { + .logical-operator, + .grouping-operator-open, + .grouping-operator-close { input { text-align: center; } @@ -153,7 +153,7 @@ li { display: inline; - &:not(:first-of-type):before { + &:not(:first-of-type)::before { display: inline; content: ', '; } @@ -193,12 +193,12 @@ left: ~"calc(-2em - 2px)"; // That's min-width + margin-right of an operator line-height: 16/12; // 16 (px) desired / default font size (px) - i:before { + i::before { margin-right: 0; } } - &:not(._hover_delay):hover button { + &:not([data-hover-delay]):hover button { display: inline; } } @@ -210,9 +210,9 @@ } label { - &.logical_operator, - &.grouping_operator_open, - &.grouping_operator_close { + &.logical-operator, + &.grouping-operator-open, + &.grouping-operator-close { margin-left: 1px; // adds up to 2px with the previous term margin-right: 2px; } diff --git a/asset/css/search-base.less b/asset/css/search-base.less index 1e41ee2..50e7fc7 100644 --- a/asset/css/search-base.less +++ b/asset/css/search-base.less @@ -78,8 +78,8 @@ fieldset:disabled .term-input-area [data-drag-initiator] { } .remove-action { - background: @search-term-remove-action-bg; - color: @search-term-remove-action-color; + background: var(--search-term-remove-action-bg, @search-term-remove-action-bg); + color: var(--search-term-remove-action-color, @search-term-remove-action-color); .rounded-corners(0.25em); } } @@ -370,7 +370,7 @@ fieldset:disabled .term-input-area [data-drag-initiator] { padding: 0; li.suggestion-title { - padding: 1.25em .625em 0 .625em; + padding: 1.25em .625em 0; } li.failure-message { diff --git a/asset/css/search-editor.less b/asset/css/search-editor.less index d32d31c..81803da 100644 --- a/asset/css/search-editor.less +++ b/asset/css/search-editor.less @@ -28,7 +28,7 @@ .rounded-corners(0); } - i.icon:before { + i.icon::before { color: var(--search-editor-control-color, @search-editor-control-color); } @@ -110,7 +110,7 @@ border-top-right-radius: 0; } - &:before { + &::before { // The left pointing arrow border-bottom: 1px solid var(--search-editor-context-menu-border-color, @search-editor-context-menu-border-color); border-left: 1px solid var(--search-editor-context-menu-border-color, @search-editor-context-menu-border-color); @@ -118,7 +118,7 @@ } } - &:hover i.icon:before { + &:hover i.icon::before { .rounded-corners(); background: var(--primary-button-bg, @primary-button-bg); color: var(--primary-button-color, @primary-button-color); @@ -202,7 +202,7 @@ margin-left: .5em; } - i.icon:before { + i.icon::before { margin: 0; font-size: 1.5em; line-height: 1.5; @@ -227,7 +227,7 @@ white-space: nowrap; } - &:before { + &::before { // The left pointing arrow content: ""; display: block; @@ -246,7 +246,7 @@ display: block; } - i.icon:before { + i.icon::before { padding: ((28/18)-1)/2em; // (Container pixels / default font size) - line height / (padding-top,padding-bottom) line-height: 1; } diff --git a/asset/css/suggestion-element.less b/asset/css/suggestion-element.less new file mode 100644 index 0000000..db6b89f --- /dev/null +++ b/asset/css/suggestion-element.less @@ -0,0 +1,29 @@ +.suggestion-element-group { + display: inline-flex; + + .suggestion-element, + .suggestion-element-icon { + line-height: normal; + height: 2.25em; + padding: 0.5em; + background-color: var(--default-input-bg, @default-input-bg); + color: var(--default-text-color, @default-text-color); + } + + .suggestion-element { + border: none; + outline: none; + border-radius: 0 0.25em 0.25em 0; + } + + .suggestion-element-icon { + padding-right: 0; + border-radius: 0.25em 0 0 0.25em; + } + + &:focus-within { + border-radius: 0.25em; + outline: 3px solid var(--default-input-outline-color, @default-input-outline-color); + outline-offset: 1px; + } +} diff --git a/asset/css/variables.less b/asset/css/variables.less index f48d8cc..01c2e68 100644 --- a/asset/css/variables.less +++ b/asset/css/variables.less @@ -41,6 +41,8 @@ @default-text-color-light: fade(@default-text-color, 75%); @default-text-color-inverted: @default-bg; @default-input-bg: #404d72; +@default-input-hover-bg: #434374; +@default-input-outline-color: @base-primary-light; @default-remove-bg: @state-critical; @default-remove-color: @default-text-color-inverted; @default-delete-bg: @state-critical; @@ -122,7 +124,7 @@ @schedule-element-fields-selected-bg: @primary-button-bg; @schedule-element-fields-selected-color: @default-text-color-inverted; @schedule-element-fields-hover-bg: @base-primary-light; -@schedule-element-fields-outline-color: fade(@base-primary-bg, 50%); +@schedule-element-fields-outline-color: @default-input-outline-color; @schedule-element-fields-selected-outline-color: fade(#fff, 50%); @schedule-element-fields-selected-hover-bg: @primary-button-hover-bg; @schedule-element-fields-disabled-color: @base-gray; @@ -150,6 +152,8 @@ --default-text-color-light: fade(#535353, 75%); // --default-text-color --default-text-color-inverted: #F5F9FA; --default-input-bg: #DEECF1; + --default-input-hover-bg: #C0CCCD; + --default-input-outline-color: @base-primary-light; --default-remove-bg: var(--base-remove-bg); --default-remove-color: var(--default-text-color-inverted); --default-delete-bg: var(--base-remove-bg); @@ -217,7 +221,7 @@ --schedule-element-fields-selected-bg: var(--primary-button-bg); --schedule-element-fields-selected-color: var(--default-text-color-inverted); --schedule-element-fields-hover-bg: @base-primary-light; - --schedule-element-fields-outline-color: fade(@base-primary-bg, 50%); + --schedule-element-fields-outline-color: @default-input-outline-color; --schedule-element-fields-selected-outline-color: fade(#fff, 50%); --schedule-element-fields-selected-hover-bg: var(--primary-button-hover-bg); --schedule-element-fields-disabled-color: var(--base-gray); diff --git a/asset/js/widget/FilterInput.js b/asset/js/widget/FilterInput.js index fad3da0..3f1d947 100644 --- a/asset/js/widget/FilterInput.js +++ b/asset/js/widget/FilterInput.js @@ -13,7 +13,7 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) { * * @type {{}} */ - this.negationOperator = { label: '!', search: '!', class: 'logical_operator', type: 'negation_operator' }; + this.negationOperator = { label: '!', search: '!', class: 'logical-operator', type: 'negation_operator' }; /** * Supported grouping operators @@ -21,8 +21,8 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) { * @type {{close: {}, open: {}}} */ this.grouping_operators = { - open: { label: '(', search: '(', class: 'grouping_operator_open', type: 'grouping_operator' }, - close: { label: ')', search: ')', class: 'grouping_operator_close', type: 'grouping_operator' } + open: { label: '(', search: '(', class: 'grouping-operator-open', type: 'grouping_operator' }, + close: { label: ')', search: ')', class: 'grouping-operator-close', type: 'grouping_operator' } }; /** @@ -33,8 +33,8 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) { * @type {{}[]} */ this.logical_operators = [ - { label: '&', search: '&', class: 'logical_operator', type: 'logical_operator', default: true }, - { label: '|', search: '|', class: 'logical_operator', type: 'logical_operator' }, + { label: '&', search: '&', class: 'logical-operator', type: 'logical_operator', default: true }, + { label: '|', search: '|', class: 'logical-operator', type: 'logical_operator' }, ]; /** @@ -958,7 +958,7 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) { label.dataset.type = termData.type; if (! termData.class) { - label.classList.add(termData.type); + label.classList.add(termData.type.replace('_', '-')); } if (termData.counterpart >= 0) { @@ -1256,11 +1256,11 @@ define(["../notjQuery", "BaseInput"], function ($, BaseInput) { let label = event.currentTarget; if (['column', 'operator', 'value'].includes(label.dataset.type)) { - // This adds a class to delay the remove button. If it's shown instantly upon hover + // This adds an attr to delay the remove button. If it's shown instantly upon hover // it's too easy to accidentally click it instead of the desired grouping operator. - label.parentNode.classList.add('_hover_delay'); + label.parentNode.dataset.hoverDelay = ""; setTimeout(function () { - label.parentNode.classList.remove('_hover_delay'); + delete label.parentNode.dataset.hoverDelay; }, 500); } diff --git a/composer.lock b/composer.lock index ef2fd4c..ffd8f0a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,29 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a455b1a65c7b995734d820bdba5a58b2", + "content-hash": "7a1692c86b6fc70eaaf43c4bee3673aa", "packages": [ { "name": "brick/math", - "version": "0.13.1", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -64,7 +64,7 @@ "type": "github" } ], - "time": "2025-03-29T13:50:30+00:00" + "time": "2025-08-29T12:40:03+00:00" }, { "name": "doctrine/collections", @@ -359,16 +359,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -384,7 +384,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -455,7 +455,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -471,20 +471,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "ipl/html", - "version": "v0.8.2", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-html.git", - "reference": "e18bdf11abca5e477100e2c7d190ef5f424d0d98" + "reference": "ace1348d1c4fc6d916663a30cbbe9217128f2b02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-html/zipball/e18bdf11abca5e477100e2c7d190ef5f424d0d98", - "reference": "e18bdf11abca5e477100e2c7d190ef5f424d0d98", + "url": "https://api.github.com/repos/Icinga/ipl-html/zipball/ace1348d1c4fc6d916663a30cbbe9217128f2b02", + "reference": "ace1348d1c4fc6d916663a30cbbe9217128f2b02", "shasum": "" }, "require": { @@ -492,13 +492,14 @@ "guzzlehttp/psr7": "^2.5", "ipl/stdlib": ">=0.12.0", "ipl/validator": ">=0.5.0", - "php": ">=7.2", + "php": ">=8.2", "psr/http-message": "^1.1" }, "require-dev": { "ipl/stdlib": "dev-main", "ipl/validator": "dev-main" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -516,22 +517,22 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-html/issues", - "source": "https://github.com/Icinga/ipl-html/tree/v0.8.2" + "source": "https://github.com/Icinga/ipl-html/tree/main" }, - "time": "2025-05-21T09:00:03+00:00" + "time": "2025-09-12T14:54:01+00:00" }, { "name": "ipl/i18n", - "version": "v0.2.2", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-i18n.git", - "reference": "a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c" + "reference": "692c33cf46fb8a4511da613dbf97c6216c345cc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-i18n/zipball/a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c", - "reference": "a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c", + "url": "https://api.github.com/repos/Icinga/ipl-i18n/zipball/692c33cf46fb8a4511da613dbf97c6216c345cc5", + "reference": "692c33cf46fb8a4511da613dbf97c6216c345cc5", "shasum": "" }, "require": { @@ -543,6 +544,7 @@ "require-dev": { "ipl/stdlib": "dev-main" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -567,13 +569,13 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-i18n/issues", - "source": "https://github.com/Icinga/ipl-i18n/tree/v0.2.2" + "source": "https://github.com/Icinga/ipl-i18n/tree/main" }, - "time": "2024-04-08T12:28:47+00:00" + "time": "2025-06-12T11:57:41+00:00" }, { "name": "ipl/orm", - "version": "v0.7.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-orm.git", @@ -596,6 +598,7 @@ "ipl/sql": "dev-main", "ipl/stdlib": "dev-main" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -621,16 +624,16 @@ }, { "name": "ipl/scheduler", - "version": "v0.1.2", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-scheduler.git", - "reference": "6119afdea07b1390bd728e350e0d80b26ec8d6ba" + "reference": "3e4e8db870239d213b1dfd5d79d59fc7784b4c34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-scheduler/zipball/6119afdea07b1390bd728e350e0d80b26ec8d6ba", - "reference": "6119afdea07b1390bd728e350e0d80b26ec8d6ba", + "url": "https://api.github.com/repos/Icinga/ipl-scheduler/zipball/3e4e8db870239d213b1dfd5d79d59fc7784b4c34", + "reference": "3e4e8db870239d213b1dfd5d79d59fc7784b4c34", "shasum": "" }, "require": { @@ -650,6 +653,7 @@ "suggest": { "ext-ev": "Improves performance, efficiency and avoids system limitations. Highly recommended! (See https://www.php.net/manual/en/intro.ev.php for details)" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -673,22 +677,22 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-scheduler/issues", - "source": "https://github.com/Icinga/ipl-scheduler/tree/v0.1.2" + "source": "https://github.com/Icinga/ipl-scheduler/tree/main" }, - "time": "2023-08-30T14:14:23+00:00" + "time": "2025-06-12T11:58:09+00:00" }, { "name": "ipl/sql", - "version": "v0.7.1", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-sql.git", - "reference": "e80f1b712c4b96099b0bf9096e6efe317a165e3b" + "reference": "6a73754c38ab91a442699e0c89ba1e15694d3ae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-sql/zipball/e80f1b712c4b96099b0bf9096e6efe317a165e3b", - "reference": "e80f1b712c4b96099b0bf9096e6efe317a165e3b", + "url": "https://api.github.com/repos/Icinga/ipl-sql/zipball/6a73754c38ab91a442699e0c89ba1e15694d3ae7", + "reference": "6a73754c38ab91a442699e0c89ba1e15694d3ae7", "shasum": "" }, "require": { @@ -699,6 +703,7 @@ "require-dev": { "ipl/stdlib": "dev-main" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -717,22 +722,22 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-sql/issues", - "source": "https://github.com/Icinga/ipl-sql/tree/v0.7.1" + "source": "https://github.com/Icinga/ipl-sql/tree/main" }, - "time": "2024-06-25T09:55:43+00:00" + "time": "2025-08-12T10:09:11+00:00" }, { "name": "ipl/stdlib", - "version": "v0.14.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-stdlib.git", - "reference": "bf5fc8f40b86bd90337db6f3be389be2a93fa64a" + "reference": "9b7a903fbfc341da59f242149ac333594e4a6fa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-stdlib/zipball/bf5fc8f40b86bd90337db6f3be389be2a93fa64a", - "reference": "bf5fc8f40b86bd90337db6f3be389be2a93fa64a", + "url": "https://api.github.com/repos/Icinga/ipl-stdlib/zipball/9b7a903fbfc341da59f242149ac333594e4a6fa3", + "reference": "9b7a903fbfc341da59f242149ac333594e4a6fa3", "shasum": "" }, "require": { @@ -740,6 +745,7 @@ "ext-openssl": "*", "php": ">=7.2" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -756,22 +762,22 @@ "description": "ipl Standard Library", "support": { "issues": "https://github.com/Icinga/ipl-stdlib/issues", - "source": "https://github.com/Icinga/ipl-stdlib/tree/v0.14.0" + "source": "https://github.com/Icinga/ipl-stdlib/tree/main" }, - "time": "2024-04-22T08:47:08+00:00" + "time": "2025-09-05T12:07:21+00:00" }, { "name": "ipl/validator", - "version": "v0.5.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-validator.git", - "reference": "a601fae0ed330e63cea50e4a2a6659ca1ad97bde" + "reference": "eac5c6c114d8007db5c24ae159fe6f55e89a946b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-validator/zipball/a601fae0ed330e63cea50e4a2a6659ca1ad97bde", - "reference": "a601fae0ed330e63cea50e4a2a6659ca1ad97bde", + "url": "https://api.github.com/repos/Icinga/ipl-validator/zipball/eac5c6c114d8007db5c24ae159fe6f55e89a946b", + "reference": "eac5c6c114d8007db5c24ae159fe6f55e89a946b", "shasum": "" }, "require": { @@ -780,11 +786,14 @@ "ipl/i18n": ">=0.2.0", "ipl/stdlib": ">=0.12.0", "php": ">=7.2", - "psr/http-message": "~1.0" + "psr/http-message": "^1.1" }, "require-dev": { - "guzzlehttp/psr7": "^1" + "guzzlehttp/psr7": "^1", + "ipl/i18n": "dev-main", + "ipl/stdlib": "dev-main" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -799,28 +808,28 @@ "homepage": "https://github.com/Icinga/ipl-validator", "support": { "issues": "https://github.com/Icinga/ipl-validator/issues", - "source": "https://github.com/Icinga/ipl-validator/tree/v0.5.0" + "source": "https://github.com/Icinga/ipl-validator/tree/main" }, - "time": "2023-03-21T15:59:00+00:00" + "time": "2025-06-12T11:59:27+00:00" }, { "name": "ipl/web", - "version": "v0.11.1", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-web.git", - "reference": "af3a8a7be5e924f0ad841baf1471d296329aa1e7" + "reference": "2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-web/zipball/af3a8a7be5e924f0ad841baf1471d296329aa1e7", - "reference": "af3a8a7be5e924f0ad841baf1471d296329aa1e7", + "url": "https://api.github.com/repos/Icinga/ipl-web/zipball/2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f", + "reference": "2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f", "shasum": "" }, "require": { "ext-json": "*", "fortawesome/font-awesome": "^6", - "ipl/html": ">=0.8.0", + "ipl/html": ">=0.9.0", "ipl/i18n": ">=0.2.0", "ipl/orm": ">=0.5.2", "ipl/scheduler": ">=0.1.0", @@ -837,6 +846,7 @@ "ipl/stdlib": "dev-main", "shardj/zf1-future": "^1.22" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -854,9 +864,9 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-web/issues", - "source": "https://github.com/Icinga/ipl-web/tree/v0.11.1" + "source": "https://github.com/Icinga/ipl-web/tree/main" }, - "time": "2025-06-12T11:58:42+00:00" + "time": "2025-08-25T13:59:25+00:00" }, { "name": "psr/http-factory", @@ -1138,20 +1148,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -1210,9 +1220,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "react/event-loop", @@ -1419,16 +1429,16 @@ }, { "name": "symfony/polyfill-php84", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "000df7860439609837bbe28670b0be15783b7fbf" + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", - "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { @@ -1475,7 +1485,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -1486,12 +1496,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-20T12:04:08+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "webmozart/assert", @@ -1628,15 +1642,73 @@ } ], "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, + "aliases": [ + { + "package": "ipl/html", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/i18n", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/orm", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/scheduler", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/sql", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/stdlib", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/validator", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + }, + { + "package": "ipl/web", + "version": "dev-main", + "alias": "99.x-dev", + "alias_normalized": "99.9999999.9999999.9999999-dev" + } + ], + "minimum-stability": "dev", + "stability-flags": { + "ipl/html": 20, + "ipl/i18n": 20, + "ipl/orm": 20, + "ipl/scheduler": 20, + "ipl/sql": 20, + "ipl/stdlib": 20, + "ipl/validator": 20, + "ipl/web": 20 + }, + "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.2" + "php": ">=7.2" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.2" }, diff --git a/vendor/autoload.php b/vendor/autoload.php index 917cad0..5c3658e 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -14,12 +14,9 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInita455b1a65c7b995734d820bdba5a58b2::getLoader(); +return ComposerAutoloaderInit7a1692c86b6fc70eaaf43c4bee3673aa::getLoader(); diff --git a/vendor/brick/math/composer.json b/vendor/brick/math/composer.json index f400aa4..ad1dfe0 100644 --- a/vendor/brick/math/composer.json +++ b/vendor/brick/math/composer.json @@ -19,12 +19,12 @@ ], "license": "MIT", "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { - "phpunit/phpunit": "^10.1", + "phpunit/phpunit": "^11.5", "php-coveralls/php-coveralls": "^2.2", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22" }, "autoload": { "psr-4": { diff --git a/vendor/brick/math/src/BigDecimal.php b/vendor/brick/math/src/BigDecimal.php index 0932fd6..1a540c7 100644 --- a/vendor/brick/math/src/BigDecimal.php +++ b/vendor/brick/math/src/BigDecimal.php @@ -8,14 +8,13 @@ use Brick\Math\Exception\DivisionByZeroException; use Brick\Math\Exception\MathException; use Brick\Math\Exception\NegativeNumberException; use Brick\Math\Internal\Calculator; +use Brick\Math\Internal\CalculatorRegistry; use Override; /** * Immutable, arbitrary-precision signed decimal numbers. - * - * @psalm-immutable */ -final class BigDecimal extends BigNumber +final readonly class BigDecimal extends BigNumber { /** * The unscaled value of this decimal number. @@ -24,20 +23,22 @@ final class BigDecimal extends BigNumber * No leading zero must be present. * No leading minus sign must be present if the value is 0. */ - private readonly string $value; + private string $value; /** * The scale (number of digits after the decimal point) of this decimal number. * * This must be zero or more. */ - private readonly int $scale; + private int $scale; /** * Protected constructor. Use a factory method to obtain an instance. * * @param string $value The unscaled value, validated. * @param int $scale The scale, validated. + * + * @pure */ protected function __construct(string $value, int $scale = 0) { @@ -45,9 +46,6 @@ final class BigDecimal extends BigNumber $this->scale = $scale; } - /** - * @psalm-pure - */ #[Override] protected static function from(BigNumber $number): static { @@ -63,7 +61,7 @@ final class BigDecimal extends BigNumber * @param int $scale The scale of the number. If negative, the scale will be set to zero * and the unscaled value will be adjusted accordingly. * - * @psalm-pure + * @pure */ public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal { @@ -82,14 +80,11 @@ final class BigDecimal extends BigNumber /** * Returns a BigDecimal representing zero, with a scale of zero. * - * @psalm-pure + * @pure */ public static function zero() : BigDecimal { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigDecimal|null $zero - */ + /** @var BigDecimal|null $zero */ static $zero; if ($zero === null) { @@ -102,14 +97,11 @@ final class BigDecimal extends BigNumber /** * Returns a BigDecimal representing one, with a scale of zero. * - * @psalm-pure + * @pure */ public static function one() : BigDecimal { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigDecimal|null $one - */ + /** @var BigDecimal|null $one */ static $one; if ($one === null) { @@ -122,14 +114,11 @@ final class BigDecimal extends BigNumber /** * Returns a BigDecimal representing ten, with a scale of zero. * - * @psalm-pure + * @pure */ public static function ten() : BigDecimal { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigDecimal|null $ten - */ + /** @var BigDecimal|null $ten */ static $ten; if ($ten === null) { @@ -147,6 +136,8 @@ final class BigDecimal extends BigNumber * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal. * * @throws MathException If the number is not valid, or is not convertible to a BigDecimal. + * + * @pure */ public function plus(BigNumber|int|float|string $that) : BigDecimal { @@ -162,7 +153,7 @@ final class BigDecimal extends BigNumber [$a, $b] = $this->scaleValues($this, $that); - $value = Calculator::get()->add($a, $b); + $value = CalculatorRegistry::get()->add($a, $b); $scale = $this->scale > $that->scale ? $this->scale : $that->scale; return new BigDecimal($value, $scale); @@ -176,6 +167,8 @@ final class BigDecimal extends BigNumber * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal. * * @throws MathException If the number is not valid, or is not convertible to a BigDecimal. + * + * @pure */ public function minus(BigNumber|int|float|string $that) : BigDecimal { @@ -187,7 +180,7 @@ final class BigDecimal extends BigNumber [$a, $b] = $this->scaleValues($this, $that); - $value = Calculator::get()->sub($a, $b); + $value = CalculatorRegistry::get()->sub($a, $b); $scale = $this->scale > $that->scale ? $this->scale : $that->scale; return new BigDecimal($value, $scale); @@ -201,6 +194,8 @@ final class BigDecimal extends BigNumber * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal. * * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal. + * + * @pure */ public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal { @@ -214,7 +209,7 @@ final class BigDecimal extends BigNumber return $that; } - $value = Calculator::get()->mul($this->value, $that->value); + $value = CalculatorRegistry::get()->mul($this->value, $that->value); $scale = $this->scale + $that->scale; return new BigDecimal($value, $scale); @@ -229,6 +224,8 @@ final class BigDecimal extends BigNumber * * @throws \InvalidArgumentException If the scale or rounding mode is invalid. * @throws MathException If the number is invalid, is zero, or rounding was necessary. + * + * @pure */ public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal { @@ -251,7 +248,7 @@ final class BigDecimal extends BigNumber $p = $this->valueWithMinScale($that->scale + $scale); $q = $that->valueWithMinScale($this->scale - $scale); - $result = Calculator::get()->divRound($p, $q, $roundingMode); + $result = CalculatorRegistry::get()->divRound($p, $q, $roundingMode); return new BigDecimal($result, $scale); } @@ -265,6 +262,8 @@ final class BigDecimal extends BigNumber * * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero, * or the result yields an infinite number of digits. + * + * @pure */ public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal { @@ -279,7 +278,7 @@ final class BigDecimal extends BigNumber $d = \rtrim($b, '0'); $scale = \strlen($b) - \strlen($d); - $calculator = Calculator::get(); + $calculator = CalculatorRegistry::get(); foreach ([5, 2] as $prime) { for (;;) { @@ -297,12 +296,36 @@ final class BigDecimal extends BigNumber return $this->dividedBy($that, $scale)->stripTrailingZeros(); } + /** + * Limits (clamps) this number between the given minimum and maximum values. + * + * If the number is lower than $min, returns a copy of $min. + * If the number is greater than $max, returns a copy of $max. + * Otherwise, returns this number unchanged. + * + * @param BigNumber|int|float|string $min The minimum. Must be convertible to a BigDecimal. + * @param BigNumber|int|float|string $max The maximum. Must be convertible to a BigDecimal. + * + * @throws MathException If min/max are not convertible to a BigDecimal. + */ + public function clamp(BigNumber|int|float|string $min, BigNumber|int|float|string $max) : BigDecimal + { + if ($this->isLessThan($min)) { + return BigDecimal::of($min); + } elseif ($this->isGreaterThan($max)) { + return BigDecimal::of($max); + } + return $this; + } + /** * Returns this number exponentiated to the given value. * * The result has a scale of `$this->scale * $exponent`. * * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. + * + * @pure */ public function power(int $exponent) : BigDecimal { @@ -322,7 +345,7 @@ final class BigDecimal extends BigNumber )); } - return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent); + return new BigDecimal(CalculatorRegistry::get()->pow($this->value, $exponent), $this->scale * $exponent); } /** @@ -333,6 +356,8 @@ final class BigDecimal extends BigNumber * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. * * @throws MathException If the divisor is not a valid decimal number, or is zero. + * + * @pure */ public function quotient(BigNumber|int|float|string $that) : BigDecimal { @@ -345,7 +370,7 @@ final class BigDecimal extends BigNumber $p = $this->valueWithMinScale($that->scale); $q = $that->valueWithMinScale($this->scale); - $quotient = Calculator::get()->divQ($p, $q); + $quotient = CalculatorRegistry::get()->divQ($p, $q); return new BigDecimal($quotient, 0); } @@ -358,6 +383,8 @@ final class BigDecimal extends BigNumber * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. * * @throws MathException If the divisor is not a valid decimal number, or is zero. + * + * @pure */ public function remainder(BigNumber|int|float|string $that) : BigDecimal { @@ -370,7 +397,7 @@ final class BigDecimal extends BigNumber $p = $this->valueWithMinScale($that->scale); $q = $that->valueWithMinScale($this->scale); - $remainder = Calculator::get()->divR($p, $q); + $remainder = CalculatorRegistry::get()->divR($p, $q); $scale = $this->scale > $that->scale ? $this->scale : $that->scale; @@ -384,11 +411,11 @@ final class BigDecimal extends BigNumber * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. * - * @return BigDecimal[] An array containing the quotient and the remainder. - * - * @psalm-return array{BigDecimal, BigDecimal} + * @return array{BigDecimal, BigDecimal} An array containing the quotient and the remainder. * * @throws MathException If the divisor is not a valid decimal number, or is zero. + * + * @pure */ public function quotientAndRemainder(BigNumber|int|float|string $that) : array { @@ -401,7 +428,7 @@ final class BigDecimal extends BigNumber $p = $this->valueWithMinScale($that->scale); $q = $that->valueWithMinScale($this->scale); - [$quotient, $remainder] = Calculator::get()->divQR($p, $q); + [$quotient, $remainder] = CalculatorRegistry::get()->divQR($p, $q); $scale = $this->scale > $that->scale ? $this->scale : $that->scale; @@ -416,6 +443,8 @@ final class BigDecimal extends BigNumber * * @throws \InvalidArgumentException If the scale is negative. * @throws NegativeNumberException If this number is negative. + * + * @pure */ public function sqrt(int $scale) : BigDecimal { @@ -447,13 +476,15 @@ final class BigDecimal extends BigNumber $value = \substr($value, 0, $addDigits); } - $value = Calculator::get()->sqrt($value); + $value = CalculatorRegistry::get()->sqrt($value); return new BigDecimal($value, $scale); } /** * Returns a copy of this BigDecimal with the decimal point moved $n places to the left. + * + * @pure */ public function withPointMovedLeft(int $n) : BigDecimal { @@ -470,6 +501,8 @@ final class BigDecimal extends BigNumber /** * Returns a copy of this BigDecimal with the decimal point moved $n places to the right. + * + * @pure */ public function withPointMovedRight(int $n) : BigDecimal { @@ -496,6 +529,8 @@ final class BigDecimal extends BigNumber /** * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part. + * + * @pure */ public function stripTrailingZeros() : BigDecimal { @@ -527,6 +562,8 @@ final class BigDecimal extends BigNumber /** * Returns the absolute value of this number. + * + * @pure */ public function abs() : BigDecimal { @@ -535,10 +572,12 @@ final class BigDecimal extends BigNumber /** * Returns the negated value of this number. + * + * @pure */ public function negated() : BigDecimal { - return new BigDecimal(Calculator::get()->neg($this->value), $this->scale); + return new BigDecimal(CalculatorRegistry::get()->neg($this->value), $this->scale); } #[Override] @@ -553,7 +592,7 @@ final class BigDecimal extends BigNumber if ($that instanceof BigDecimal) { [$a, $b] = $this->scaleValues($this, $that); - return Calculator::get()->cmp($a, $b); + return CalculatorRegistry::get()->cmp($a, $b); } return - $that->compareTo($this); @@ -565,11 +604,17 @@ final class BigDecimal extends BigNumber return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1); } + /** + * @pure + */ public function getUnscaledValue() : BigInteger { return self::newBigInteger($this->value); } + /** + * @pure + */ public function getScale() : int { return $this->scale; @@ -588,6 +633,8 @@ final class BigDecimal extends BigNumber * 123.456 => 6 * 0.00123 => 3 * 0.0012300 => 5 + * + * @pure */ public function getPrecision(): int { @@ -606,6 +653,8 @@ final class BigDecimal extends BigNumber * Returns a string representing the integral part of this decimal number. * * Example: `-123.456` => `-123`. + * + * @pure */ public function getIntegralPart() : string { @@ -624,6 +673,8 @@ final class BigDecimal extends BigNumber * If the scale is zero, an empty string is returned. * * Examples: `-123.456` => '456', `123` => ''. + * + * @pure */ public function getFractionalPart() : string { @@ -638,6 +689,8 @@ final class BigDecimal extends BigNumber /** * Returns whether this decimal number has a non-zero fractional part. + * + * @pure */ public function hasNonZeroFractionalPart() : bool { @@ -702,7 +755,7 @@ final class BigDecimal extends BigNumber $value = $this->getUnscaledValueWithLeadingZeros(); - /** @var numeric-string */ + /** @phpstan-ignore return.type */ return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale); } @@ -722,7 +775,6 @@ final class BigDecimal extends BigNumber * This method is only here to allow unserializing the object and cannot be accessed directly. * * @internal - * @psalm-suppress RedundantPropertyInitializationCheck * * @param array{value: string, scale: int} $data * @@ -730,10 +782,12 @@ final class BigDecimal extends BigNumber */ public function __unserialize(array $data): void { + /** @phpstan-ignore isset.initializedProperty */ if (isset($this->value)) { throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); } + /** @phpstan-ignore deadCode.unreachable */ $this->value = $data['value']; $this->scale = $data['scale']; } @@ -742,6 +796,8 @@ final class BigDecimal extends BigNumber * Puts the internal values of the given decimal numbers on the same scale. * * @return array{string, string} The scaled integer values of $x and $y. + * + * @pure */ private function scaleValues(BigDecimal $x, BigDecimal $y) : array { @@ -757,6 +813,9 @@ final class BigDecimal extends BigNumber return [$a, $b]; } + /** + * @pure + */ private function valueWithMinScale(int $scale) : string { $value = $this->value; @@ -770,6 +829,8 @@ final class BigDecimal extends BigNumber /** * Adds leading zeros if necessary to the unscaled value to represent the full decimal number. + * + * @pure */ private function getUnscaledValueWithLeadingZeros() : string { diff --git a/vendor/brick/math/src/BigInteger.php b/vendor/brick/math/src/BigInteger.php index 6ede65e..48a7bb3 100644 --- a/vendor/brick/math/src/BigInteger.php +++ b/vendor/brick/math/src/BigInteger.php @@ -10,6 +10,7 @@ use Brick\Math\Exception\MathException; use Brick\Math\Exception\NegativeNumberException; use Brick\Math\Exception\NumberFormatException; use Brick\Math\Internal\Calculator; +use Brick\Math\Internal\CalculatorRegistry; use Override; /** @@ -17,10 +18,8 @@ use Override; * * All methods accepting a number as a parameter accept either a BigInteger instance, * an integer, or a string representing an arbitrary size integer. - * - * @psalm-immutable */ -final class BigInteger extends BigNumber +final readonly class BigInteger extends BigNumber { /** * The value, as a string of digits with optional leading minus sign. @@ -28,21 +27,20 @@ final class BigInteger extends BigNumber * No leading zeros must be present. * No leading minus sign must be present if the number is zero. */ - private readonly string $value; + private string $value; /** * Protected constructor. Use a factory method to obtain an instance. * * @param string $value A string of digits, with optional leading minus sign. + * + * @pure */ protected function __construct(string $value) { $this->value = $value; } - /** - * @psalm-pure - */ #[Override] protected static function from(BigNumber $number): static { @@ -66,7 +64,7 @@ final class BigInteger extends BigNumber * @throws NumberFormatException If the number is empty, or contains invalid chars for the given base. * @throws \InvalidArgumentException If the base is out of range. * - * @psalm-pure + * @pure */ public static function fromBase(string $number, int $base) : BigInteger { @@ -115,7 +113,7 @@ final class BigInteger extends BigNumber return new BigInteger($sign . $number); } - $result = Calculator::get()->fromBase($number, $base); + $result = CalculatorRegistry::get()->fromBase($number, $base); return new BigInteger($sign . $result); } @@ -131,7 +129,7 @@ final class BigInteger extends BigNumber * @throws NumberFormatException If the given number is empty or contains invalid chars for the given alphabet. * @throws \InvalidArgumentException If the alphabet does not contain at least 2 chars. * - * @psalm-pure + * @pure */ public static function fromArbitraryBase(string $number, string $alphabet) : BigInteger { @@ -151,7 +149,7 @@ final class BigInteger extends BigNumber throw NumberFormatException::charNotInAlphabet($matches[0]); } - $number = Calculator::get()->fromArbitraryBase($number, $alphabet, $base); + $number = CalculatorRegistry::get()->fromArbitraryBase($number, $alphabet, $base); return new BigInteger($number); } @@ -172,6 +170,8 @@ final class BigInteger extends BigNumber * sign bit. * * @throws NumberFormatException If the string is empty. + * + * @pure */ public static function fromBytes(string $value, bool $signed = true) : BigInteger { @@ -203,12 +203,10 @@ final class BigInteger extends BigNumber * * Using the default random bytes generator, this method is suitable for cryptographic use. * - * @psalm-param (callable(int): string)|null $randomBytesGenerator - * - * @param int $numBits The number of bits. - * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, and returns a - * string of random bytes of the given length. Defaults to the - * `random_bytes()` function. + * @param int $numBits The number of bits. + * @param (callable(int): string)|null $randomBytesGenerator A function that accepts a number of bytes, and returns + * a string of random bytes of the given length. Defaults + * to the `random_bytes()` function. * * @throws \InvalidArgumentException If $numBits is negative. */ @@ -243,13 +241,11 @@ final class BigInteger extends BigNumber * * Using the default random bytes generator, this method is suitable for cryptographic use. * - * @psalm-param (callable(int): string)|null $randomBytesGenerator - * - * @param BigNumber|int|float|string $min The lower bound. Must be convertible to a BigInteger. - * @param BigNumber|int|float|string $max The upper bound. Must be convertible to a BigInteger. - * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, - * and returns a string of random bytes of the given length. - * Defaults to the `random_bytes()` function. + * @param BigNumber|int|float|string $min The lower bound. Must be convertible to a BigInteger. + * @param BigNumber|int|float|string $max The upper bound. Must be convertible to a BigInteger. + * @param (callable(int): string)|null $randomBytesGenerator A function that accepts a number of bytes, and returns + * a string of random bytes of the given length. Defaults + * to the `random_bytes()` function. * * @throws MathException If one of the parameters cannot be converted to a BigInteger, * or `$min` is greater than `$max`. @@ -284,14 +280,11 @@ final class BigInteger extends BigNumber /** * Returns a BigInteger representing zero. * - * @psalm-pure + * @pure */ public static function zero() : BigInteger { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigInteger|null $zero - */ + /** @var BigInteger|null $zero */ static $zero; if ($zero === null) { @@ -304,14 +297,11 @@ final class BigInteger extends BigNumber /** * Returns a BigInteger representing one. * - * @psalm-pure + * @pure */ public static function one() : BigInteger { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigInteger|null $one - */ + /** @var BigInteger|null $one */ static $one; if ($one === null) { @@ -324,14 +314,11 @@ final class BigInteger extends BigNumber /** * Returns a BigInteger representing ten. * - * @psalm-pure + * @pure */ public static function ten() : BigInteger { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigInteger|null $ten - */ + /** @var BigInteger|null $ten */ static $ten; if ($ten === null) { @@ -341,6 +328,9 @@ final class BigInteger extends BigNumber return $ten; } + /** + * @pure + */ public static function gcdMultiple(BigInteger $a, BigInteger ...$n): BigInteger { $result = $a; @@ -362,6 +352,8 @@ final class BigInteger extends BigNumber * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigInteger. * * @throws MathException If the number is not valid, or is not convertible to a BigInteger. + * + * @pure */ public function plus(BigNumber|int|float|string $that) : BigInteger { @@ -375,7 +367,7 @@ final class BigInteger extends BigNumber return $that; } - $value = Calculator::get()->add($this->value, $that->value); + $value = CalculatorRegistry::get()->add($this->value, $that->value); return new BigInteger($value); } @@ -386,6 +378,8 @@ final class BigInteger extends BigNumber * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigInteger. * * @throws MathException If the number is not valid, or is not convertible to a BigInteger. + * + * @pure */ public function minus(BigNumber|int|float|string $that) : BigInteger { @@ -395,7 +389,7 @@ final class BigInteger extends BigNumber return $this; } - $value = Calculator::get()->sub($this->value, $that->value); + $value = CalculatorRegistry::get()->sub($this->value, $that->value); return new BigInteger($value); } @@ -406,6 +400,8 @@ final class BigInteger extends BigNumber * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigInteger. * * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigInteger. + * + * @pure */ public function multipliedBy(BigNumber|int|float|string $that) : BigInteger { @@ -419,7 +415,7 @@ final class BigInteger extends BigNumber return $that; } - $value = Calculator::get()->mul($this->value, $that->value); + $value = CalculatorRegistry::get()->mul($this->value, $that->value); return new BigInteger($value); } @@ -432,6 +428,8 @@ final class BigInteger extends BigNumber * * @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero, * or RoundingMode::UNNECESSARY is used and the remainder is not zero. + * + * @pure */ public function dividedBy(BigNumber|int|float|string $that, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigInteger { @@ -445,15 +443,40 @@ final class BigInteger extends BigNumber throw DivisionByZeroException::divisionByZero(); } - $result = Calculator::get()->divRound($this->value, $that->value, $roundingMode); + $result = CalculatorRegistry::get()->divRound($this->value, $that->value, $roundingMode); return new BigInteger($result); } + /** + * Limits (clamps) this number between the given minimum and maximum values. + * + * If the number is lower than $min, returns a copy of $min. + * If the number is greater than $max, returns a copy of $max. + * Otherwise, returns this number unchanged. + * + * @param BigNumber|int|float|string $min The minimum. Must be convertible to a BigInteger. + * @param BigNumber|int|float|string $max The maximum. Must be convertible to a BigInteger. + * + * @throws MathException If min/max are not convertible to a BigInteger. + */ + public function clamp(BigNumber|int|float|string $min, BigNumber|int|float|string $max) : BigInteger + { + if ($this->isLessThan($min)) { + return BigInteger::of($min); + } elseif ($this->isGreaterThan($max)) { + return BigInteger::of($max); + } + return $this; + } + + /** * Returns this number exponentiated to the given value. * * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. + * + * @pure */ public function power(int $exponent) : BigInteger { @@ -473,7 +496,7 @@ final class BigInteger extends BigNumber )); } - return new BigInteger(Calculator::get()->pow($this->value, $exponent)); + return new BigInteger(CalculatorRegistry::get()->pow($this->value, $exponent)); } /** @@ -482,6 +505,8 @@ final class BigInteger extends BigNumber * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @throws DivisionByZeroException If the divisor is zero. + * + * @pure */ public function quotient(BigNumber|int|float|string $that) : BigInteger { @@ -495,7 +520,7 @@ final class BigInteger extends BigNumber throw DivisionByZeroException::divisionByZero(); } - $quotient = Calculator::get()->divQ($this->value, $that->value); + $quotient = CalculatorRegistry::get()->divQ($this->value, $that->value); return new BigInteger($quotient); } @@ -508,6 +533,8 @@ final class BigInteger extends BigNumber * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @throws DivisionByZeroException If the divisor is zero. + * + * @pure */ public function remainder(BigNumber|int|float|string $that) : BigInteger { @@ -521,7 +548,7 @@ final class BigInteger extends BigNumber throw DivisionByZeroException::divisionByZero(); } - $remainder = Calculator::get()->divR($this->value, $that->value); + $remainder = CalculatorRegistry::get()->divR($this->value, $that->value); return new BigInteger($remainder); } @@ -531,11 +558,11 @@ final class BigInteger extends BigNumber * * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * - * @return BigInteger[] An array containing the quotient and the remainder. - * - * @psalm-return array{BigInteger, BigInteger} + * @return array{BigInteger, BigInteger} An array containing the quotient and the remainder. * * @throws DivisionByZeroException If the divisor is zero. + * + * @pure */ public function quotientAndRemainder(BigNumber|int|float|string $that) : array { @@ -545,7 +572,7 @@ final class BigInteger extends BigNumber throw DivisionByZeroException::divisionByZero(); } - [$quotient, $remainder] = Calculator::get()->divQR($this->value, $that->value); + [$quotient, $remainder] = CalculatorRegistry::get()->divQR($this->value, $that->value); return [ new BigInteger($quotient), @@ -564,6 +591,8 @@ final class BigInteger extends BigNumber * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. * * @throws DivisionByZeroException If the divisor is zero. + * + * @pure */ public function mod(BigNumber|int|float|string $that) : BigInteger { @@ -573,7 +602,7 @@ final class BigInteger extends BigNumber throw DivisionByZeroException::modulusMustNotBeZero(); } - $value = Calculator::get()->mod($this->value, $that->value); + $value = CalculatorRegistry::get()->mod($this->value, $that->value); return new BigInteger($value); } @@ -585,6 +614,8 @@ final class BigInteger extends BigNumber * @throws NegativeNumberException If $m is negative. * @throws MathException If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger * is not relatively prime to m). + * + * @pure */ public function modInverse(BigInteger $m) : BigInteger { @@ -600,7 +631,7 @@ final class BigInteger extends BigNumber return BigInteger::zero(); } - $value = Calculator::get()->modInverse($this->value, $m->value); + $value = CalculatorRegistry::get()->modInverse($this->value, $m->value); if ($value === null) { throw new MathException('Unable to compute the modInverse for the given modulus.'); @@ -619,6 +650,8 @@ final class BigInteger extends BigNumber * * @throws NegativeNumberException If any of the operands is negative. * @throws DivisionByZeroException If the modulus is zero. + * + * @pure */ public function modPow(BigNumber|int|float|string $exp, BigNumber|int|float|string $mod) : BigInteger { @@ -633,7 +666,7 @@ final class BigInteger extends BigNumber throw DivisionByZeroException::modulusMustNotBeZero(); } - $result = Calculator::get()->modPow($this->value, $exp->value, $mod->value); + $result = CalculatorRegistry::get()->modPow($this->value, $exp->value, $mod->value); return new BigInteger($result); } @@ -644,6 +677,8 @@ final class BigInteger extends BigNumber * The GCD is always positive, unless both operands are zero, in which case it is zero. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + * + * @pure */ public function gcd(BigNumber|int|float|string $that) : BigInteger { @@ -657,7 +692,7 @@ final class BigInteger extends BigNumber return $that; } - $value = Calculator::get()->gcd($this->value, $that->value); + $value = CalculatorRegistry::get()->gcd($this->value, $that->value); return new BigInteger($value); } @@ -668,6 +703,8 @@ final class BigInteger extends BigNumber * The result is the largest x such that x² ≤ n. * * @throws NegativeNumberException If this number is negative. + * + * @pure */ public function sqrt() : BigInteger { @@ -675,13 +712,15 @@ final class BigInteger extends BigNumber throw new NegativeNumberException('Cannot calculate the square root of a negative number.'); } - $value = Calculator::get()->sqrt($this->value); + $value = CalculatorRegistry::get()->sqrt($this->value); return new BigInteger($value); } /** * Returns the absolute value of this number. + * + * @pure */ public function abs() : BigInteger { @@ -690,10 +729,12 @@ final class BigInteger extends BigNumber /** * Returns the inverse of this number. + * + * @pure */ public function negated() : BigInteger { - return new BigInteger(Calculator::get()->neg($this->value)); + return new BigInteger(CalculatorRegistry::get()->neg($this->value)); } /** @@ -702,12 +743,14 @@ final class BigInteger extends BigNumber * This method returns a negative BigInteger if and only if both operands are negative. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + * + * @pure */ public function and(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); - return new BigInteger(Calculator::get()->and($this->value, $that->value)); + return new BigInteger(CalculatorRegistry::get()->and($this->value, $that->value)); } /** @@ -716,12 +759,14 @@ final class BigInteger extends BigNumber * This method returns a negative BigInteger if and only if either of the operands is negative. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + * + * @pure */ public function or(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); - return new BigInteger(Calculator::get()->or($this->value, $that->value)); + return new BigInteger(CalculatorRegistry::get()->or($this->value, $that->value)); } /** @@ -730,16 +775,20 @@ final class BigInteger extends BigNumber * This method returns a negative BigInteger if and only if exactly one of the operands is negative. * * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + * + * @pure */ public function xor(BigNumber|int|float|string $that) : BigInteger { $that = BigInteger::of($that); - return new BigInteger(Calculator::get()->xor($this->value, $that->value)); + return new BigInteger(CalculatorRegistry::get()->xor($this->value, $that->value)); } /** * Returns the bitwise-not of this BigInteger. + * + * @pure */ public function not() : BigInteger { @@ -748,6 +797,8 @@ final class BigInteger extends BigNumber /** * Returns the integer left shifted by a given number of bits. + * + * @pure */ public function shiftedLeft(int $distance) : BigInteger { @@ -764,6 +815,8 @@ final class BigInteger extends BigNumber /** * Returns the integer right shifted by a given number of bits. + * + * @pure */ public function shiftedRight(int $distance) : BigInteger { @@ -789,6 +842,8 @@ final class BigInteger extends BigNumber * * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation. * Computes (ceil(log2(this < 0 ? -this : this+1))). + * + * @pure */ public function getBitLength() : int { @@ -807,6 +862,8 @@ final class BigInteger extends BigNumber * Returns the index of the rightmost (lowest-order) one bit in this BigInteger. * * Returns -1 if this BigInteger contains no one bits. + * + * @pure */ public function getLowestSetBit() : int { @@ -826,6 +883,8 @@ final class BigInteger extends BigNumber /** * Returns whether this number is even. + * + * @pure */ public function isEven() : bool { @@ -834,6 +893,8 @@ final class BigInteger extends BigNumber /** * Returns whether this number is odd. + * + * @pure */ public function isOdd() : bool { @@ -848,6 +909,8 @@ final class BigInteger extends BigNumber * @param int $n The bit to test, 0-based. * * @throws \InvalidArgumentException If the bit to test is negative. + * + * @pure */ public function testBit(int $n) : bool { @@ -864,7 +927,7 @@ final class BigInteger extends BigNumber $that = BigNumber::of($that); if ($that instanceof BigInteger) { - return Calculator::get()->cmp($this->value, $that->value); + return CalculatorRegistry::get()->cmp($this->value, $that->value); } return - $that->compareTo($this); @@ -924,6 +987,8 @@ final class BigInteger extends BigNumber * The output will always be lowercase for bases greater than 10. * * @throws \InvalidArgumentException If the base is out of range. + * + * @pure */ public function toBase(int $base) : string { @@ -935,7 +1000,7 @@ final class BigInteger extends BigNumber throw new \InvalidArgumentException(\sprintf('Base %d is out of range [2, 36]', $base)); } - return Calculator::get()->toBase($this->value, $base); + return CalculatorRegistry::get()->toBase($this->value, $base); } /** @@ -948,6 +1013,8 @@ final class BigInteger extends BigNumber * * @throws NegativeNumberException If this number is negative. * @throws \InvalidArgumentException If the given alphabet does not contain at least 2 chars. + * + * @pure */ public function toArbitraryBase(string $alphabet) : string { @@ -961,7 +1028,7 @@ final class BigInteger extends BigNumber throw new NegativeNumberException(__FUNCTION__ . '() does not support negative numbers.'); } - return Calculator::get()->toArbitraryBase($this->value, $alphabet, $base); + return CalculatorRegistry::get()->toArbitraryBase($this->value, $alphabet, $base); } /** @@ -981,6 +1048,8 @@ final class BigInteger extends BigNumber * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit. * * @throws NegativeNumberException If $signed is false, and the number is negative. + * + * @pure */ public function toBytes(bool $signed = true) : string { @@ -1020,7 +1089,10 @@ final class BigInteger extends BigNumber } } - return \hex2bin($hex); + $result = \hex2bin($hex); + assert($result !== false); + + return $result; } /** @@ -1049,7 +1121,6 @@ final class BigInteger extends BigNumber * This method is only here to allow unserializing the object and cannot be accessed directly. * * @internal - * @psalm-suppress RedundantPropertyInitializationCheck * * @param array{value: string} $data * @@ -1057,10 +1128,12 @@ final class BigInteger extends BigNumber */ public function __unserialize(array $data): void { + /** @phpstan-ignore isset.initializedProperty */ if (isset($this->value)) { throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); } + /** @phpstan-ignore deadCode.unreachable */ $this->value = $data['value']; } } diff --git a/vendor/brick/math/src/BigNumber.php b/vendor/brick/math/src/BigNumber.php index 5dabd31..2fbdf69 100644 --- a/vendor/brick/math/src/BigNumber.php +++ b/vendor/brick/math/src/BigNumber.php @@ -11,11 +11,14 @@ use Brick\Math\Exception\RoundingNecessaryException; use Override; /** - * Common interface for arbitrary-precision rational numbers. + * Base class for arbitrary-precision numbers. * - * @psalm-immutable + * This class is sealed: it is part of the public API but should not be subclassed in userland. + * Protected methods may change in any version. + * + * @phpstan-sealed BigInteger|BigDecimal|BigRational */ -abstract class BigNumber implements \JsonSerializable +abstract readonly class BigNumber implements \JsonSerializable, \Stringable { /** * The regular expression used to parse integer or decimal numbers. @@ -43,7 +46,8 @@ abstract class BigNumber implements \JsonSerializable /** * Creates a BigNumber of the given value. * - * The concrete return type is dependent on the given value, with the following rules: + * When of() is called on BigNumber, the concrete return type is dependent on the given value, with the following + * rules: * * - BigNumber instances are returned as is * - integer numbers are returned as BigInteger @@ -52,18 +56,20 @@ abstract class BigNumber implements \JsonSerializable * - strings containing a `.` character or using an exponential notation are returned as BigDecimal * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger * + * When of() is called on BigInteger, BigDecimal, or BigRational, the resulting number is converted to an instance + * of the subclass when possible; otherwise a RoundingNecessaryException exception is thrown. + * * @throws NumberFormatException If the format of the number is not valid. * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero. * @throws RoundingNecessaryException If the value cannot be converted to an instance of the subclass without rounding. * - * @psalm-pure + * @pure */ final public static function of(BigNumber|int|float|string $value) : static { $value = self::_of($value); if (static::class === BigNumber::class) { - // https://github.com/vimeo/psalm/issues/10309 assert($value instanceof static); return $value; @@ -76,7 +82,7 @@ abstract class BigNumber implements \JsonSerializable * @throws NumberFormatException If the format of the number is not valid. * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero. * - * @psalm-pure + * @pure */ private static function _of(BigNumber|int|float|string $value) : BigNumber { @@ -102,9 +108,6 @@ abstract class BigNumber implements \JsonSerializable $numerator = $matches['numerator']; $denominator = $matches['denominator']; - assert($numerator !== null); - assert($denominator !== null); - $numerator = self::cleanUp($sign, $numerator); $denominator = self::cleanUp(null, $denominator); @@ -138,7 +141,7 @@ abstract class BigNumber implements \JsonSerializable } if ($point !== null || $exponent !== null) { - $fractional = ($fractional ?? ''); + $fractional ??= ''; $exponent = ($exponent !== null) ? (int)$exponent : 0; if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) { @@ -170,15 +173,15 @@ abstract class BigNumber implements \JsonSerializable * * @throws RoundingNecessaryException If the value cannot be converted. * - * @psalm-pure + * @pure */ abstract protected static function from(BigNumber $number): static; /** * Proxy method to access BigInteger's protected constructor from sibling classes. * + * @pure * @internal - * @psalm-pure */ final protected function newBigInteger(string $value) : BigInteger { @@ -188,8 +191,8 @@ abstract class BigNumber implements \JsonSerializable /** * Proxy method to access BigDecimal's protected constructor from sibling classes. * + * @pure * @internal - * @psalm-pure */ final protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal { @@ -199,8 +202,8 @@ abstract class BigNumber implements \JsonSerializable /** * Proxy method to access BigRational's protected constructor from sibling classes. * + * @pure * @internal - * @psalm-pure */ final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational { @@ -216,7 +219,7 @@ abstract class BigNumber implements \JsonSerializable * @throws \InvalidArgumentException If no values are given. * @throws MathException If an argument is not valid. * - * @psalm-pure + * @pure */ final public static function min(BigNumber|int|float|string ...$values) : static { @@ -246,7 +249,7 @@ abstract class BigNumber implements \JsonSerializable * @throws \InvalidArgumentException If no values are given. * @throws MathException If an argument is not valid. * - * @psalm-pure + * @pure */ final public static function max(BigNumber|int|float|string ...$values) : static { @@ -270,41 +273,43 @@ abstract class BigNumber implements \JsonSerializable /** * Returns the sum of the given values. * - * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible - * to an instance of the class this method is called on. + * When called on BigNumber, sum() accepts any supported type and returns a result whose type is the widest among + * the given values (BigInteger < BigDecimal < BigRational). + * + * When called on BigInteger, BigDecimal, or BigRational, sum() requires that all values can be converted to that + * specific subclass, and returns a result of the same type. + * + * @param BigNumber|int|float|string ...$values The values to add. All values must be convertible to the class on + * which this method is called. * * @throws \InvalidArgumentException If no values are given. * @throws MathException If an argument is not valid. * - * @psalm-pure + * @pure */ final public static function sum(BigNumber|int|float|string ...$values) : static { - /** @var static|null $sum */ - $sum = null; + $first = array_shift($values); - foreach ($values as $value) { - $value = static::of($value); - - $sum = $sum === null ? $value : self::add($sum, $value); - } - - if ($sum === null) { + if ($first === null) { throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); } + $sum = static::of($first); + + foreach ($values as $value) { + $sum = self::add($sum, static::of($value)); + } + + assert($sum instanceof static); + return $sum; } /** * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException. * - * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to - * concrete classes the responsibility to perform the addition themselves or delegate it to the given number, - * depending on their ability to perform the operation. This will also require a version bump because we're - * potentially breaking custom BigNumber implementations (if any...) - * - * @psalm-pure + * @pure */ private static function add(BigNumber $a, BigNumber $b) : BigNumber { @@ -324,8 +329,6 @@ abstract class BigNumber implements \JsonSerializable return $b->plus($a); } - /** @var BigInteger $a */ - return $a->plus($b); } @@ -333,9 +336,9 @@ abstract class BigNumber implements \JsonSerializable * Removes optional leading zeros and applies sign. * * @param string|null $sign The sign, '+' or '-', optional. Null is allowed for convenience and treated as '+'. - * @param string $number The number, validated as a non-empty string of digits. + * @param string $number The number, validated as a string of digits. * - * @psalm-pure + * @pure */ private static function cleanUp(string|null $sign, string $number) : string { @@ -350,6 +353,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is equal to the given one. + * + * @pure */ final public function isEqualTo(BigNumber|int|float|string $that) : bool { @@ -358,6 +363,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is strictly lower than the given one. + * + * @pure */ final public function isLessThan(BigNumber|int|float|string $that) : bool { @@ -366,6 +373,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is lower than or equal to the given one. + * + * @pure */ final public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool { @@ -374,6 +383,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is strictly greater than the given one. + * + * @pure */ final public function isGreaterThan(BigNumber|int|float|string $that) : bool { @@ -382,6 +393,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is greater than or equal to the given one. + * + * @pure */ final public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool { @@ -390,6 +403,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number equals zero. + * + * @pure */ final public function isZero() : bool { @@ -398,6 +413,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is strictly negative. + * + * @pure */ final public function isNegative() : bool { @@ -406,6 +423,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is negative or zero. + * + * @pure */ final public function isNegativeOrZero() : bool { @@ -414,6 +433,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is strictly positive. + * + * @pure */ final public function isPositive() : bool { @@ -422,6 +443,8 @@ abstract class BigNumber implements \JsonSerializable /** * Checks if this number is positive or zero. + * + * @pure */ final public function isPositiveOrZero() : bool { @@ -431,20 +454,24 @@ abstract class BigNumber implements \JsonSerializable /** * Returns the sign of this number. * - * @psalm-return -1|0|1 + * Returns -1 if the number is negative, 0 if zero, 1 if positive. * - * @return int -1 if the number is negative, 0 if zero, 1 if positive. + * @return -1|0|1 + * + * @pure */ abstract public function getSign() : int; /** * Compares this number to the given one. * - * @psalm-return -1|0|1 + * Returns -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`. * - * @return int -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`. + * @return -1|0|1 * * @throws MathException If the number is not valid. + * + * @pure */ abstract public function compareTo(BigNumber|int|float|string $that) : int; @@ -452,6 +479,8 @@ abstract class BigNumber implements \JsonSerializable * Converts this number to a BigInteger. * * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding. + * + * @pure */ abstract public function toBigInteger() : BigInteger; @@ -459,11 +488,15 @@ abstract class BigNumber implements \JsonSerializable * Converts this number to a BigDecimal. * * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding. + * + * @pure */ abstract public function toBigDecimal() : BigDecimal; /** * Converts this number to a BigRational. + * + * @pure */ abstract public function toBigRational() : BigRational; @@ -475,6 +508,8 @@ abstract class BigNumber implements \JsonSerializable * * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding. * This only applies when RoundingMode::UNNECESSARY is used. + * + * @pure */ abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal; @@ -485,6 +520,8 @@ abstract class BigNumber implements \JsonSerializable * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit. * * @throws MathException If this number cannot be exactly converted to a native integer. + * + * @pure */ abstract public function toInt() : int; @@ -496,6 +533,8 @@ abstract class BigNumber implements \JsonSerializable * * If the number is greater than the largest representable floating point number, positive infinity is returned. * If the number is less than the smallest representable floating point number, negative infinity is returned. + * + * @pure */ abstract public function toFloat() : float; @@ -504,6 +543,8 @@ abstract class BigNumber implements \JsonSerializable * * The output of this method can be parsed by the `of()` factory method; * this will yield an object equal to this one, without any information loss. + * + * @pure */ abstract public function __toString() : string; diff --git a/vendor/brick/math/src/BigRational.php b/vendor/brick/math/src/BigRational.php index fc36e55..56045e5 100644 --- a/vendor/brick/math/src/BigRational.php +++ b/vendor/brick/math/src/BigRational.php @@ -14,20 +14,18 @@ use Override; * An arbitrarily large rational number. * * This class is immutable. - * - * @psalm-immutable */ -final class BigRational extends BigNumber +final readonly class BigRational extends BigNumber { /** * The numerator. */ - private readonly BigInteger $numerator; + private BigInteger $numerator; /** * The denominator. Always strictly positive. */ - private readonly BigInteger $denominator; + private BigInteger $denominator; /** * Protected constructor. Use a factory method to obtain an instance. @@ -37,6 +35,8 @@ final class BigRational extends BigNumber * @param bool $checkDenominator Whether to check the denominator for negative and zero. * * @throws DivisionByZeroException If the denominator is zero. + * + * @pure */ protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) { @@ -55,9 +55,6 @@ final class BigRational extends BigNumber $this->denominator = $denominator; } - /** - * @psalm-pure - */ #[Override] protected static function from(BigNumber $number): static { @@ -77,7 +74,7 @@ final class BigRational extends BigNumber * @throws RoundingNecessaryException If an argument represents a non-integer number. * @throws DivisionByZeroException If the denominator is zero. * - * @psalm-pure + * @pure */ public static function nd( BigNumber|int|float|string $numerator, @@ -92,14 +89,11 @@ final class BigRational extends BigNumber /** * Returns a BigRational representing zero. * - * @psalm-pure + * @pure */ public static function zero() : BigRational { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigRational|null $zero - */ + /** @var BigRational|null $zero */ static $zero; if ($zero === null) { @@ -112,14 +106,11 @@ final class BigRational extends BigNumber /** * Returns a BigRational representing one. * - * @psalm-pure + * @pure */ public static function one() : BigRational { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigRational|null $one - */ + /** @var BigRational|null $one */ static $one; if ($one === null) { @@ -132,14 +123,11 @@ final class BigRational extends BigNumber /** * Returns a BigRational representing ten. * - * @psalm-pure + * @pure */ public static function ten() : BigRational { - /** - * @psalm-suppress ImpureStaticVariable - * @var BigRational|null $ten - */ + /** @var BigRational|null $ten */ static $ten; if ($ten === null) { @@ -149,11 +137,17 @@ final class BigRational extends BigNumber return $ten; } + /** + * @pure + */ public function getNumerator() : BigInteger { return $this->numerator; } + /** + * @pure + */ public function getDenominator() : BigInteger { return $this->denominator; @@ -161,6 +155,8 @@ final class BigRational extends BigNumber /** * Returns the quotient of the division of the numerator by the denominator. + * + * @pure */ public function quotient() : BigInteger { @@ -169,6 +165,8 @@ final class BigRational extends BigNumber /** * Returns the remainder of the division of the numerator by the denominator. + * + * @pure */ public function remainder() : BigInteger { @@ -178,9 +176,9 @@ final class BigRational extends BigNumber /** * Returns the quotient and remainder of the division of the numerator by the denominator. * - * @return BigInteger[] + * @return array{BigInteger, BigInteger} * - * @psalm-return array{BigInteger, BigInteger} + * @pure */ public function quotientAndRemainder() : array { @@ -193,6 +191,8 @@ final class BigRational extends BigNumber * @param BigNumber|int|float|string $that The number to add. * * @throws MathException If the number is not valid. + * + * @pure */ public function plus(BigNumber|int|float|string $that) : BigRational { @@ -211,6 +211,8 @@ final class BigRational extends BigNumber * @param BigNumber|int|float|string $that The number to subtract. * * @throws MathException If the number is not valid. + * + * @pure */ public function minus(BigNumber|int|float|string $that) : BigRational { @@ -229,6 +231,8 @@ final class BigRational extends BigNumber * @param BigNumber|int|float|string $that The multiplier. * * @throws MathException If the multiplier is not a valid number. + * + * @pure */ public function multipliedBy(BigNumber|int|float|string $that) : BigRational { @@ -246,6 +250,8 @@ final class BigRational extends BigNumber * @param BigNumber|int|float|string $that The divisor. * * @throws MathException If the divisor is not a valid number, or is zero. + * + * @pure */ public function dividedBy(BigNumber|int|float|string $that) : BigRational { @@ -261,6 +267,8 @@ final class BigRational extends BigNumber * Returns this number exponentiated to the given value. * * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. + * + * @pure */ public function power(int $exponent) : BigRational { @@ -287,6 +295,8 @@ final class BigRational extends BigNumber * The reciprocal has the numerator and denominator swapped. * * @throws DivisionByZeroException If the numerator is zero. + * + * @pure */ public function reciprocal() : BigRational { @@ -295,6 +305,8 @@ final class BigRational extends BigNumber /** * Returns the absolute value of this BigRational. + * + * @pure */ public function abs() : BigRational { @@ -303,6 +315,8 @@ final class BigRational extends BigNumber /** * Returns the negated value of this BigRational. + * + * @pure */ public function negated() : BigRational { @@ -311,6 +325,8 @@ final class BigRational extends BigNumber /** * Returns the simplified value of this BigRational. + * + * @pure */ public function simplified() : BigRational { @@ -406,7 +422,6 @@ final class BigRational extends BigNumber * This method is only here to allow unserializing the object and cannot be accessed directly. * * @internal - * @psalm-suppress RedundantPropertyInitializationCheck * * @param array{numerator: BigInteger, denominator: BigInteger} $data * @@ -414,10 +429,12 @@ final class BigRational extends BigNumber */ public function __unserialize(array $data): void { + /** @phpstan-ignore isset.initializedProperty */ if (isset($this->numerator)) { throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); } + /** @phpstan-ignore deadCode.unreachable */ $this->numerator = $data['numerator']; $this->denominator = $data['denominator']; } diff --git a/vendor/brick/math/src/Exception/DivisionByZeroException.php b/vendor/brick/math/src/Exception/DivisionByZeroException.php index ce7769a..2daaf5e 100644 --- a/vendor/brick/math/src/Exception/DivisionByZeroException.php +++ b/vendor/brick/math/src/Exception/DivisionByZeroException.php @@ -7,10 +7,10 @@ namespace Brick\Math\Exception; /** * Exception thrown when a division by zero occurs. */ -class DivisionByZeroException extends MathException +final class DivisionByZeroException extends MathException { /** - * @psalm-pure + * @pure */ public static function divisionByZero() : DivisionByZeroException { @@ -18,7 +18,7 @@ class DivisionByZeroException extends MathException } /** - * @psalm-pure + * @pure */ public static function modulusMustNotBeZero() : DivisionByZeroException { @@ -26,7 +26,7 @@ class DivisionByZeroException extends MathException } /** - * @psalm-pure + * @pure */ public static function denominatorMustNotBeZero() : DivisionByZeroException { diff --git a/vendor/brick/math/src/Exception/IntegerOverflowException.php b/vendor/brick/math/src/Exception/IntegerOverflowException.php index c73b490..56f3cf8 100644 --- a/vendor/brick/math/src/Exception/IntegerOverflowException.php +++ b/vendor/brick/math/src/Exception/IntegerOverflowException.php @@ -9,10 +9,10 @@ use Brick\Math\BigInteger; /** * Exception thrown when an integer overflow occurs. */ -class IntegerOverflowException extends MathException +final class IntegerOverflowException extends MathException { /** - * @psalm-pure + * @pure */ public static function toIntOverflow(BigInteger $value) : IntegerOverflowException { diff --git a/vendor/brick/math/src/Exception/NegativeNumberException.php b/vendor/brick/math/src/Exception/NegativeNumberException.php index 4739113..73ed3a4 100644 --- a/vendor/brick/math/src/Exception/NegativeNumberException.php +++ b/vendor/brick/math/src/Exception/NegativeNumberException.php @@ -7,6 +7,6 @@ namespace Brick\Math\Exception; /** * Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number. */ -class NegativeNumberException extends MathException +final class NegativeNumberException extends MathException { } diff --git a/vendor/brick/math/src/Exception/NumberFormatException.php b/vendor/brick/math/src/Exception/NumberFormatException.php index 119cadb..0dcda34 100644 --- a/vendor/brick/math/src/Exception/NumberFormatException.php +++ b/vendor/brick/math/src/Exception/NumberFormatException.php @@ -7,8 +7,11 @@ namespace Brick\Math\Exception; /** * Exception thrown when attempting to create a number from a string with an invalid format. */ -class NumberFormatException extends MathException +final class NumberFormatException extends MathException { + /** + * @pure + */ public static function invalidFormat(string $value) : self { return new self(\sprintf( @@ -20,7 +23,7 @@ class NumberFormatException extends MathException /** * @param string $char The failing character. * - * @psalm-pure + * @pure */ public static function charNotInAlphabet(string $char) : self { diff --git a/vendor/brick/math/src/Exception/RoundingNecessaryException.php b/vendor/brick/math/src/Exception/RoundingNecessaryException.php index 57bfcd8..034a040 100644 --- a/vendor/brick/math/src/Exception/RoundingNecessaryException.php +++ b/vendor/brick/math/src/Exception/RoundingNecessaryException.php @@ -7,10 +7,10 @@ namespace Brick\Math\Exception; /** * Exception thrown when a number cannot be represented at the requested scale without rounding. */ -class RoundingNecessaryException extends MathException +final class RoundingNecessaryException extends MathException { /** - * @psalm-pure + * @pure */ public static function roundingNecessary() : RoundingNecessaryException { diff --git a/vendor/brick/math/src/Internal/Calculator.php b/vendor/brick/math/src/Internal/Calculator.php index 44dd669..a5e000a 100644 --- a/vendor/brick/math/src/Internal/Calculator.php +++ b/vendor/brick/math/src/Internal/Calculator.php @@ -17,10 +17,8 @@ use Brick\Math\RoundingMode; * All methods must return strings respecting this format, unless specified otherwise. * * @internal - * - * @psalm-immutable */ -abstract class Calculator +abstract readonly class Calculator { /** * The maximum exponent value allowed for the pow() method. @@ -32,63 +30,12 @@ abstract class Calculator */ public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'; - /** - * The Calculator instance in use. - */ - private static ?Calculator $instance = null; - - /** - * Sets the Calculator instance to use. - * - * An instance is typically set only in unit tests: the autodetect is usually the best option. - * - * @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect. - */ - final public static function set(?Calculator $calculator) : void - { - self::$instance = $calculator; - } - - /** - * Returns the Calculator instance to use. - * - * If none has been explicitly set, the fastest available implementation will be returned. - * - * @psalm-pure - * @psalm-suppress ImpureStaticProperty - */ - final public static function get() : Calculator - { - if (self::$instance === null) { - /** @psalm-suppress ImpureMethodCall */ - self::$instance = self::detect(); - } - - return self::$instance; - } - - /** - * Returns the fastest available Calculator implementation. - * - * @codeCoverageIgnore - */ - private static function detect() : Calculator - { - if (\extension_loaded('gmp')) { - return new Calculator\GmpCalculator(); - } - - if (\extension_loaded('bcmath')) { - return new Calculator\BcMathCalculator(); - } - - return new Calculator\NativeCalculator(); - } - /** * Extracts the sign & digits of the operands. * * @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits. + * + * @pure */ final protected function init(string $a, string $b) : array { @@ -103,6 +50,8 @@ abstract class Calculator /** * Returns the absolute value of a number. + * + * @pure */ final public function abs(string $n) : string { @@ -111,6 +60,8 @@ abstract class Calculator /** * Negates a number. + * + * @pure */ final public function neg(string $n) : string { @@ -128,9 +79,11 @@ abstract class Calculator /** * Compares two numbers. * - * @psalm-return -1|0|1 + * Returns -1 if the first number is less than, 0 if equal to, 1 if greater than the second number. * - * @return int -1 if the first number is less than, 0 if equal to, 1 if greater than the second number. + * @return -1|0|1 + * + * @pure */ final public function cmp(string $a, string $b) : int { @@ -160,16 +113,22 @@ abstract class Calculator /** * Adds two numbers. + * + * @pure */ abstract public function add(string $a, string $b) : string; /** * Subtracts two numbers. + * + * @pure */ abstract public function sub(string $a, string $b) : string; /** * Multiplies two numbers. + * + * @pure */ abstract public function mul(string $a, string $b) : string; @@ -180,6 +139,8 @@ abstract class Calculator * @param string $b The divisor, must not be zero. * * @return string The quotient. + * + * @pure */ abstract public function divQ(string $a, string $b) : string; @@ -190,6 +151,8 @@ abstract class Calculator * @param string $b The divisor, must not be zero. * * @return string The remainder. + * + * @pure */ abstract public function divR(string $a, string $b) : string; @@ -200,6 +163,8 @@ abstract class Calculator * @param string $b The divisor, must not be zero. * * @return array{string, string} An array containing the quotient and remainder. + * + * @pure */ abstract public function divQR(string $a, string $b) : array; @@ -210,11 +175,15 @@ abstract class Calculator * @param int $e The exponent, validated as an integer between 0 and MAX_POWER. * * @return string The power. + * + * @pure */ abstract public function pow(string $a, int $e) : string; /** * @param string $b The modulus; must not be zero. + * + * @pure */ public function mod(string $a, string $b) : string { @@ -229,6 +198,8 @@ abstract class Calculator * This method can be overridden by the concrete implementation if the underlying library has built-in support. * * @param string $m The modulus; must not be negative or zero. + * + * @pure */ public function modInverse(string $x, string $m) : ?string { @@ -257,6 +228,8 @@ abstract class Calculator * @param string $base The base number; must be positive or zero. * @param string $exp The exponent; must be positive or zero. * @param string $mod The modulus; must be strictly positive. + * + * @pure */ abstract public function modPow(string $base, string $exp, string $mod) : string; @@ -267,6 +240,8 @@ abstract class Calculator * has built-in support for GCD calculations. * * @return string The GCD, always positive, or zero if both arguments are zero. + * + * @pure */ public function gcd(string $a, string $b) : string { @@ -283,6 +258,8 @@ abstract class Calculator /** * @return array{string, string, string} GCD, X, Y + * + * @pure */ private function gcdExtended(string $a, string $b) : array { @@ -303,6 +280,8 @@ abstract class Calculator * * The result is the largest x such that x² ≤ n. * The input MUST NOT be negative. + * + * @pure */ abstract public function sqrt(string $n) : string; @@ -316,6 +295,8 @@ abstract class Calculator * @param int $base The base of the number, validated from 2 to 36. * * @return string The converted number, following the Calculator conventions. + * + * @pure */ public function fromBase(string $number, int $base) : string { @@ -332,6 +313,8 @@ abstract class Calculator * @param int $base The base to convert to, validated from 2 to 36. * * @return string The converted number, lowercase. + * + * @pure */ public function toBase(string $number, int $base) : string { @@ -359,6 +342,8 @@ abstract class Calculator * @param int $base The base of the number, validated from 2 to alphabet length. * * @return string The number in base 10, following the Calculator conventions. + * + * @pure */ final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string { @@ -405,6 +390,8 @@ abstract class Calculator * @param int $base The base to convert to, validated from 2 to alphabet length. * * @return string The converted number in the given alphabet. + * + * @pure */ final public function toArbitraryBase(string $number, string $alphabet, int $base) : string { @@ -434,10 +421,9 @@ abstract class Calculator * @param string $b The divisor, must not be zero. * @param RoundingMode $roundingMode The rounding mode. * - * @throws \InvalidArgumentException If the rounding mode is invalid. * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary. * - * @psalm-suppress ImpureFunctionCall + * @pure */ final public function divRound(string $a, string $b, RoundingMode $roundingMode) : string { @@ -498,9 +484,6 @@ abstract class Calculator $lastDigitIsEven = ($lastDigit % 2 === 0); $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; break; - - default: - throw new \InvalidArgumentException('Invalid rounding mode.'); } if ($increment) { @@ -515,6 +498,8 @@ abstract class Calculator * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for bitwise operations. + * + * @pure */ public function and(string $a, string $b) : string { @@ -526,6 +511,8 @@ abstract class Calculator * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for bitwise operations. + * + * @pure */ public function or(string $a, string $b) : string { @@ -537,6 +524,8 @@ abstract class Calculator * * This method can be overridden by the concrete implementation if the underlying library * has built-in support for bitwise operations. + * + * @pure */ public function xor(string $a, string $b) : string { @@ -549,6 +538,8 @@ abstract class Calculator * @param 'and'|'or'|'xor' $operator The operator to use. * @param string $a The left operand. * @param string $b The right operand. + * + * @pure */ private function bitwise(string $operator, string $a, string $b) : string { @@ -596,6 +587,8 @@ abstract class Calculator /** * @param string $number A positive, binary number. + * + * @pure */ private function twosComplement(string $number) : string { @@ -625,6 +618,8 @@ abstract class Calculator * Converts a decimal number to a binary string. * * @param string $number The number to convert, positive or zero, only digits. + * + * @pure */ private function toBinary(string $number) : string { @@ -642,6 +637,8 @@ abstract class Calculator * Returns the positive decimal representation of a binary number. * * @param string $bytes The bytes representing the number. + * + * @pure */ private function toDecimal(string $bytes) : string { diff --git a/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php b/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php index 93a27ff..5ba961e 100644 --- a/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php +++ b/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php @@ -11,10 +11,8 @@ use Override; * Calculator implementation built around the bcmath library. * * @internal - * - * @psalm-immutable */ -class BcMathCalculator extends Calculator +final readonly class BcMathCalculator extends Calculator { #[Override] public function add(string $a, string $b) : string diff --git a/vendor/brick/math/src/Internal/Calculator/GmpCalculator.php b/vendor/brick/math/src/Internal/Calculator/GmpCalculator.php index 0e44dee..c0f9bd5 100644 --- a/vendor/brick/math/src/Internal/Calculator/GmpCalculator.php +++ b/vendor/brick/math/src/Internal/Calculator/GmpCalculator.php @@ -5,16 +5,15 @@ declare(strict_types=1); namespace Brick\Math\Internal\Calculator; use Brick\Math\Internal\Calculator; +use GMP; use Override; /** * Calculator implementation built around the GMP library. * * @internal - * - * @psalm-immutable */ -class GmpCalculator extends Calculator +final readonly class GmpCalculator extends Calculator { #[Override] public function add(string $a, string $b) : string @@ -51,6 +50,10 @@ class GmpCalculator extends Calculator { [$q, $r] = \gmp_div_qr($a, $b); + /** + * @var GMP $q + * @var GMP $r + */ return [ \gmp_strval($q), \gmp_strval($r) diff --git a/vendor/brick/math/src/Internal/Calculator/NativeCalculator.php b/vendor/brick/math/src/Internal/Calculator/NativeCalculator.php index f71c55b..ee476c9 100644 --- a/vendor/brick/math/src/Internal/Calculator/NativeCalculator.php +++ b/vendor/brick/math/src/Internal/Calculator/NativeCalculator.php @@ -11,10 +11,8 @@ use Override; * Calculator implementation using only native PHP code. * * @internal - * - * @psalm-immutable */ -class NativeCalculator extends Calculator +final readonly class NativeCalculator extends Calculator { /** * The max number of digits the platform can natively add, subtract, multiply or divide without overflow. @@ -24,9 +22,10 @@ class NativeCalculator extends Calculator * Example: 32-bit: max number 1,999,999,999 (9 digits + carry) * 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry) */ - private readonly int $maxDigits; + private int $maxDigits; /** + * @pure * @codeCoverageIgnore */ public function __construct() @@ -34,7 +33,6 @@ class NativeCalculator extends Calculator $this->maxDigits = match (PHP_INT_SIZE) { 4 => 9, 8 => 18, - default => throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.') }; } @@ -42,8 +40,8 @@ class NativeCalculator extends Calculator public function add(string $a, string $b) : string { /** - * @psalm-var numeric-string $a - * @psalm-var numeric-string $b + * @var numeric-string $a + * @var numeric-string $b */ $result = $a + $b; @@ -80,8 +78,8 @@ class NativeCalculator extends Calculator public function mul(string $a, string $b) : string { /** - * @psalm-var numeric-string $a - * @psalm-var numeric-string $b + * @var numeric-string $a + * @var numeric-string $b */ $result = $a * $b; @@ -151,11 +149,11 @@ class NativeCalculator extends Calculator return [$this->neg($a), '0']; } - /** @psalm-var numeric-string $a */ + /** @var numeric-string $a */ $na = $a * 1; // cast to number if (is_int($na)) { - /** @psalm-var numeric-string $b */ + /** @var numeric-string $b */ $nb = $b * 1; if (is_int($nb)) { @@ -202,7 +200,6 @@ class NativeCalculator extends Calculator $aa = $this->mul($a, $a); - /** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */ $result = $this->pow($aa, $e / 2); if ($odd === 1) { @@ -278,6 +275,8 @@ class NativeCalculator extends Calculator /** * Performs the addition of two non-signed large integers. + * + * @pure */ private function doAdd(string $a, string $b) : string { @@ -291,14 +290,13 @@ class NativeCalculator extends Calculator if ($i < 0) { $blockLength += $i; - /** @psalm-suppress LoopInvalidation */ $i = 0; } - /** @psalm-var numeric-string $blockA */ + /** @var numeric-string $blockA */ $blockA = \substr($a, $i, $blockLength); - /** @psalm-var numeric-string $blockB */ + /** @var numeric-string $blockB */ $blockB = \substr($b, $i, $blockLength); $sum = (string) ($blockA + $blockB + $carry); @@ -330,6 +328,8 @@ class NativeCalculator extends Calculator /** * Performs the subtraction of two non-signed large integers. + * + * @pure */ private function doSub(string $a, string $b) : string { @@ -360,14 +360,13 @@ class NativeCalculator extends Calculator if ($i < 0) { $blockLength += $i; - /** @psalm-suppress LoopInvalidation */ $i = 0; } - /** @psalm-var numeric-string $blockA */ + /** @var numeric-string $blockA */ $blockA = \substr($a, $i, $blockLength); - /** @psalm-var numeric-string $blockB */ + /** @var numeric-string $blockB */ $blockB = \substr($b, $i, $blockLength); $sum = $blockA - $blockB - $carry; @@ -407,6 +406,8 @@ class NativeCalculator extends Calculator /** * Performs the multiplication of two non-signed large integers. + * + * @pure */ private function doMul(string $a, string $b) : string { @@ -423,7 +424,6 @@ class NativeCalculator extends Calculator if ($i < 0) { $blockALength += $i; - /** @psalm-suppress LoopInvalidation */ $i = 0; } @@ -437,7 +437,6 @@ class NativeCalculator extends Calculator if ($j < 0) { $blockBLength += $j; - /** @psalm-suppress LoopInvalidation */ $j = 0; } @@ -480,6 +479,8 @@ class NativeCalculator extends Calculator * Performs the division of two non-signed large integers. * * @return string[] The quotient and remainder. + * + * @pure */ private function doDiv(string $a, string $b) : array { @@ -498,7 +499,7 @@ class NativeCalculator extends Calculator $r = $a; // remainder $z = $y; // focus length, always $y or $y+1 - /** @psalm-var numeric-string $b */ + /** @var numeric-string $b */ $nb = $b * 1; // cast to number // performance optimization in cases where the remainder will never cause int overflow if (is_int(($nb - 1) * 10 + 9)) { @@ -506,7 +507,7 @@ class NativeCalculator extends Calculator for ($i = $z - 1; $i < $x; $i++) { $n = $r * 10 + (int) $a[$i]; - /** @psalm-var int $nb */ + /** @var int $nb */ $q .= \intdiv($n, $nb); $r = $n % $nb; } @@ -553,7 +554,9 @@ class NativeCalculator extends Calculator /** * Compares two non-signed large numbers. * - * @psalm-return -1|0|1 + * @return -1|0|1 + * + * @pure */ private function doCmp(string $a, string $b) : int { @@ -575,6 +578,8 @@ class NativeCalculator extends Calculator * The numbers must only consist of digits, without leading minus sign. * * @return array{string, string, int} + * + * @pure */ private function pad(string $a, string $b) : array { diff --git a/vendor/brick/math/src/Internal/CalculatorRegistry.php b/vendor/brick/math/src/Internal/CalculatorRegistry.php new file mode 100644 index 0000000..394fae6 --- /dev/null +++ b/vendor/brick/math/src/Internal/CalculatorRegistry.php @@ -0,0 +1,73 @@ +}|array{}|null */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +320,24 @@ class InstalledVersions { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; } /** @@ -322,19 +351,27 @@ class InstalledVersions } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -350,7 +387,7 @@ class InstalledVersions } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 021bf60..005b68e 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -8,5 +8,6 @@ $baseDir = dirname($vendorDir); return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Deprecated' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php', + 'ReflectionConstant' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php', 'lessc' => $vendorDir . '/wikimedia/less.php/lessc.inc.php', ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 17b6290..b04662e 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -22,7 +22,7 @@ return array( 'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'), 'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), - 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'Evenement\\' => array($vendorDir . '/evenement/evenement/src'), 'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/src'), diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index cbf456d..02157ed 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInita455b1a65c7b995734d820bdba5a58b2 +class ComposerAutoloaderInit7a1692c86b6fc70eaaf43c4bee3673aa { private static $loader; @@ -24,16 +24,16 @@ class ComposerAutoloaderInita455b1a65c7b995734d820bdba5a58b2 require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInita455b1a65c7b995734d820bdba5a58b2', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit7a1692c86b6fc70eaaf43c4bee3673aa', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInita455b1a65c7b995734d820bdba5a58b2', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit7a1692c86b6fc70eaaf43c4bee3673aa', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInita455b1a65c7b995734d820bdba5a58b2::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa::getInitializer($loader)); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInita455b1a65c7b995734d820bdba5a58b2::$files; + $filesToLoad = \Composer\Autoload\ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 438297c..3b06e81 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInita455b1a65c7b995734d820bdba5a58b2 +class ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa { public static $files = array ( 'a2c78434f64e5f5ed402f42eee19c025' => __DIR__ . '/..' . '/ipl/stdlib/src/functions_include.php', @@ -139,8 +139,8 @@ class ComposerStaticInita455b1a65c7b995734d820bdba5a58b2 ), 'Psr\\Http\\Message\\' => array ( - 0 => __DIR__ . '/..' . '/psr/http-message/src', - 1 => __DIR__ . '/..' . '/psr/http-factory/src', + 0 => __DIR__ . '/..' . '/psr/http-factory/src', + 1 => __DIR__ . '/..' . '/psr/http-message/src', ), 'GuzzleHttp\\Psr7\\' => array ( @@ -188,16 +188,17 @@ class ComposerStaticInita455b1a65c7b995734d820bdba5a58b2 public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'Deprecated' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php', + 'ReflectionConstant' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php', 'lessc' => __DIR__ . '/..' . '/wikimedia/less.php/lessc.inc.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInita455b1a65c7b995734d820bdba5a58b2::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInita455b1a65c7b995734d820bdba5a58b2::$prefixDirsPsr4; - $loader->prefixesPsr0 = ComposerStaticInita455b1a65c7b995734d820bdba5a58b2::$prefixesPsr0; - $loader->classMap = ComposerStaticInita455b1a65c7b995734d820bdba5a58b2::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa::$prefixesPsr0; + $loader->classMap = ComposerStaticInit7a1692c86b6fc70eaaf43c4bee3673aa::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 0d9ead0..b2246aa 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2,28 +2,28 @@ "packages": [ { "name": "brick/math", - "version": "0.13.1", - "version_normalized": "0.13.1.0", + "version": "0.14.0", + "version_normalized": "0.14.0.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, - "time": "2025-03-29T13:50:30+00:00", + "time": "2025-08-29T12:40:03+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -53,7 +53,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -371,17 +371,17 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", - "version_normalized": "2.7.1.0", + "version": "2.8.0", + "version_normalized": "2.8.0.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -397,12 +397,12 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, - "time": "2025-03-27T12:30:47+00:00", + "time": "2025-08-23T21:21:41+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -470,7 +470,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -490,17 +490,17 @@ }, { "name": "ipl/html", - "version": "v0.8.2", - "version_normalized": "0.8.2.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-html.git", - "reference": "e18bdf11abca5e477100e2c7d190ef5f424d0d98" + "reference": "ace1348d1c4fc6d916663a30cbbe9217128f2b02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-html/zipball/e18bdf11abca5e477100e2c7d190ef5f424d0d98", - "reference": "e18bdf11abca5e477100e2c7d190ef5f424d0d98", + "url": "https://api.github.com/repos/Icinga/ipl-html/zipball/ace1348d1c4fc6d916663a30cbbe9217128f2b02", + "reference": "ace1348d1c4fc6d916663a30cbbe9217128f2b02", "shasum": "" }, "require": { @@ -508,14 +508,15 @@ "guzzlehttp/psr7": "^2.5", "ipl/stdlib": ">=0.12.0", "ipl/validator": ">=0.5.0", - "php": ">=7.2", + "php": ">=8.2", "psr/http-message": "^1.1" }, "require-dev": { "ipl/stdlib": "dev-main", "ipl/validator": "dev-main" }, - "time": "2025-05-21T09:00:03+00:00", + "time": "2025-09-12T14:54:01+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -534,23 +535,23 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-html/issues", - "source": "https://github.com/Icinga/ipl-html/tree/v0.8.2" + "source": "https://github.com/Icinga/ipl-html/tree/main" }, "install-path": "../ipl/html" }, { "name": "ipl/i18n", - "version": "v0.2.2", - "version_normalized": "0.2.2.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-i18n.git", - "reference": "a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c" + "reference": "692c33cf46fb8a4511da613dbf97c6216c345cc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-i18n/zipball/a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c", - "reference": "a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c", + "url": "https://api.github.com/repos/Icinga/ipl-i18n/zipball/692c33cf46fb8a4511da613dbf97c6216c345cc5", + "reference": "692c33cf46fb8a4511da613dbf97c6216c345cc5", "shasum": "" }, "require": { @@ -562,7 +563,8 @@ "require-dev": { "ipl/stdlib": "dev-main" }, - "time": "2024-04-08T12:28:47+00:00", + "time": "2025-06-12T11:57:41+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -588,14 +590,14 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-i18n/issues", - "source": "https://github.com/Icinga/ipl-i18n/tree/v0.2.2" + "source": "https://github.com/Icinga/ipl-i18n/tree/main" }, "install-path": "../ipl/i18n" }, { "name": "ipl/orm", - "version": "v0.7.0", - "version_normalized": "0.7.0.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-orm.git", @@ -619,6 +621,7 @@ "ipl/stdlib": "dev-main" }, "time": "2025-07-10T06:16:16+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -645,17 +648,17 @@ }, { "name": "ipl/scheduler", - "version": "v0.1.2", - "version_normalized": "0.1.2.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-scheduler.git", - "reference": "6119afdea07b1390bd728e350e0d80b26ec8d6ba" + "reference": "3e4e8db870239d213b1dfd5d79d59fc7784b4c34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-scheduler/zipball/6119afdea07b1390bd728e350e0d80b26ec8d6ba", - "reference": "6119afdea07b1390bd728e350e0d80b26ec8d6ba", + "url": "https://api.github.com/repos/Icinga/ipl-scheduler/zipball/3e4e8db870239d213b1dfd5d79d59fc7784b4c34", + "reference": "3e4e8db870239d213b1dfd5d79d59fc7784b4c34", "shasum": "" }, "require": { @@ -675,7 +678,8 @@ "suggest": { "ext-ev": "Improves performance, efficiency and avoids system limitations. Highly recommended! (See https://www.php.net/manual/en/intro.ev.php for details)" }, - "time": "2023-08-30T14:14:23+00:00", + "time": "2025-06-12T11:58:09+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -700,23 +704,23 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-scheduler/issues", - "source": "https://github.com/Icinga/ipl-scheduler/tree/v0.1.2" + "source": "https://github.com/Icinga/ipl-scheduler/tree/main" }, "install-path": "../ipl/scheduler" }, { "name": "ipl/sql", - "version": "v0.7.1", - "version_normalized": "0.7.1.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-sql.git", - "reference": "e80f1b712c4b96099b0bf9096e6efe317a165e3b" + "reference": "6a73754c38ab91a442699e0c89ba1e15694d3ae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-sql/zipball/e80f1b712c4b96099b0bf9096e6efe317a165e3b", - "reference": "e80f1b712c4b96099b0bf9096e6efe317a165e3b", + "url": "https://api.github.com/repos/Icinga/ipl-sql/zipball/6a73754c38ab91a442699e0c89ba1e15694d3ae7", + "reference": "6a73754c38ab91a442699e0c89ba1e15694d3ae7", "shasum": "" }, "require": { @@ -727,7 +731,8 @@ "require-dev": { "ipl/stdlib": "dev-main" }, - "time": "2024-06-25T09:55:43+00:00", + "time": "2025-08-12T10:09:11+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -747,23 +752,23 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-sql/issues", - "source": "https://github.com/Icinga/ipl-sql/tree/v0.7.1" + "source": "https://github.com/Icinga/ipl-sql/tree/main" }, "install-path": "../ipl/sql" }, { "name": "ipl/stdlib", - "version": "v0.14.0", - "version_normalized": "0.14.0.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-stdlib.git", - "reference": "bf5fc8f40b86bd90337db6f3be389be2a93fa64a" + "reference": "9b7a903fbfc341da59f242149ac333594e4a6fa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-stdlib/zipball/bf5fc8f40b86bd90337db6f3be389be2a93fa64a", - "reference": "bf5fc8f40b86bd90337db6f3be389be2a93fa64a", + "url": "https://api.github.com/repos/Icinga/ipl-stdlib/zipball/9b7a903fbfc341da59f242149ac333594e4a6fa3", + "reference": "9b7a903fbfc341da59f242149ac333594e4a6fa3", "shasum": "" }, "require": { @@ -771,7 +776,8 @@ "ext-openssl": "*", "php": ">=7.2" }, - "time": "2024-04-22T08:47:08+00:00", + "time": "2025-09-05T12:07:21+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -789,23 +795,23 @@ "description": "ipl Standard Library", "support": { "issues": "https://github.com/Icinga/ipl-stdlib/issues", - "source": "https://github.com/Icinga/ipl-stdlib/tree/v0.14.0" + "source": "https://github.com/Icinga/ipl-stdlib/tree/main" }, "install-path": "../ipl/stdlib" }, { "name": "ipl/validator", - "version": "v0.5.0", - "version_normalized": "0.5.0.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-validator.git", - "reference": "a601fae0ed330e63cea50e4a2a6659ca1ad97bde" + "reference": "eac5c6c114d8007db5c24ae159fe6f55e89a946b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-validator/zipball/a601fae0ed330e63cea50e4a2a6659ca1ad97bde", - "reference": "a601fae0ed330e63cea50e4a2a6659ca1ad97bde", + "url": "https://api.github.com/repos/Icinga/ipl-validator/zipball/eac5c6c114d8007db5c24ae159fe6f55e89a946b", + "reference": "eac5c6c114d8007db5c24ae159fe6f55e89a946b", "shasum": "" }, "require": { @@ -814,12 +820,15 @@ "ipl/i18n": ">=0.2.0", "ipl/stdlib": ">=0.12.0", "php": ">=7.2", - "psr/http-message": "~1.0" + "psr/http-message": "^1.1" }, "require-dev": { - "guzzlehttp/psr7": "^1" + "guzzlehttp/psr7": "^1", + "ipl/i18n": "dev-main", + "ipl/stdlib": "dev-main" }, - "time": "2023-03-21T15:59:00+00:00", + "time": "2025-06-12T11:59:27+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -835,29 +844,29 @@ "homepage": "https://github.com/Icinga/ipl-validator", "support": { "issues": "https://github.com/Icinga/ipl-validator/issues", - "source": "https://github.com/Icinga/ipl-validator/tree/v0.5.0" + "source": "https://github.com/Icinga/ipl-validator/tree/main" }, "install-path": "../ipl/validator" }, { "name": "ipl/web", - "version": "v0.11.1", - "version_normalized": "0.11.1.0", + "version": "dev-main", + "version_normalized": "dev-main", "source": { "type": "git", "url": "https://github.com/Icinga/ipl-web.git", - "reference": "af3a8a7be5e924f0ad841baf1471d296329aa1e7" + "reference": "2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Icinga/ipl-web/zipball/af3a8a7be5e924f0ad841baf1471d296329aa1e7", - "reference": "af3a8a7be5e924f0ad841baf1471d296329aa1e7", + "url": "https://api.github.com/repos/Icinga/ipl-web/zipball/2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f", + "reference": "2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f", "shasum": "" }, "require": { "ext-json": "*", "fortawesome/font-awesome": "^6", - "ipl/html": ">=0.8.0", + "ipl/html": ">=0.9.0", "ipl/i18n": ">=0.2.0", "ipl/orm": ">=0.5.2", "ipl/scheduler": ">=0.1.0", @@ -874,7 +883,8 @@ "ipl/stdlib": "dev-main", "shardj/zf1-future": "^1.22" }, - "time": "2025-06-12T11:58:42+00:00", + "time": "2025-08-25T13:59:25+00:00", + "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { @@ -893,7 +903,7 @@ ], "support": { "issues": "https://github.com/Icinga/ipl-web/issues", - "source": "https://github.com/Icinga/ipl-web/tree/v0.11.1" + "source": "https://github.com/Icinga/ipl-web/tree/main" }, "install-path": "../ipl/web" }, @@ -1192,21 +1202,21 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", - "version_normalized": "4.9.0.0", + "version": "4.9.1", + "version_normalized": "4.9.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -1239,7 +1249,7 @@ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, - "time": "2025-06-25T14:20:11+00:00", + "time": "2025-09-04T20:59:21+00:00", "type": "library", "extra": { "captainhook": { @@ -1267,7 +1277,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, "install-path": "../ramsey/uuid" }, @@ -1485,23 +1495,23 @@ }, { "name": "symfony/polyfill-php84", - "version": "v1.32.0", - "version_normalized": "1.32.0.0", + "version": "v1.33.0", + "version_normalized": "1.33.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "000df7860439609837bbe28670b0be15783b7fbf" + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", - "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { "php": ">=7.2" }, - "time": "2025-02-20T12:04:08+00:00", + "time": "2025-06-24T13:30:11+00:00", "type": "library", "extra": { "thanks": { @@ -1544,7 +1554,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -1555,6 +1565,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 20f37e4..dc32a03 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'icinga/icinga-php-library', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => 'ac844e6464137f2e3b53a663dfb3a6d00599f24b', + 'reference' => '56897332adde2358f30ce407e4bc93c330a16e28', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -11,9 +11,9 @@ ), 'versions' => array( 'brick/math' => array( - 'pretty_version' => '0.13.1', - 'version' => '0.13.1.0', - 'reference' => 'fc7ed316430118cc7836bf45faff18d5dfc8de04', + 'pretty_version' => '0.14.0', + 'version' => '0.14.0.0', + 'reference' => '113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2', 'type' => 'library', 'install_path' => __DIR__ . '/../brick/math', 'aliases' => array(), @@ -65,9 +65,9 @@ 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( - 'pretty_version' => '2.7.1', - 'version' => '2.7.1.0', - 'reference' => 'c2270caaabe631b3b44c85f99e5a04bbb8060d16', + 'pretty_version' => '2.8.0', + 'version' => '2.8.0.0', + 'reference' => '21dc724a0583619cd1652f673303492272778051', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), @@ -76,82 +76,106 @@ 'icinga/icinga-php-library' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => 'ac844e6464137f2e3b53a663dfb3a6d00599f24b', + 'reference' => '56897332adde2358f30ce407e4bc93c330a16e28', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), 'ipl/html' => array( - 'pretty_version' => 'v0.8.2', - 'version' => '0.8.2.0', - 'reference' => 'e18bdf11abca5e477100e2c7d190ef5f424d0d98', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'ace1348d1c4fc6d916663a30cbbe9217128f2b02', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/html', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/i18n' => array( - 'pretty_version' => 'v0.2.2', - 'version' => '0.2.2.0', - 'reference' => 'a2b6109c5a93f86ce46d5dc351dbe75e8502cf8c', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '692c33cf46fb8a4511da613dbf97c6216c345cc5', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/i18n', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/orm' => array( - 'pretty_version' => 'v0.7.0', - 'version' => '0.7.0.0', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', 'reference' => '4893731e82c9a3859e8ea05c4c21d84f067d7cfb', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/orm', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/scheduler' => array( - 'pretty_version' => 'v0.1.2', - 'version' => '0.1.2.0', - 'reference' => '6119afdea07b1390bd728e350e0d80b26ec8d6ba', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '3e4e8db870239d213b1dfd5d79d59fc7784b4c34', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/scheduler', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/sql' => array( - 'pretty_version' => 'v0.7.1', - 'version' => '0.7.1.0', - 'reference' => 'e80f1b712c4b96099b0bf9096e6efe317a165e3b', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '6a73754c38ab91a442699e0c89ba1e15694d3ae7', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/sql', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/stdlib' => array( - 'pretty_version' => 'v0.14.0', - 'version' => '0.14.0.0', - 'reference' => 'bf5fc8f40b86bd90337db6f3be389be2a93fa64a', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '9b7a903fbfc341da59f242149ac333594e4a6fa3', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/stdlib', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/validator' => array( - 'pretty_version' => 'v0.5.0', - 'version' => '0.5.0.0', - 'reference' => 'a601fae0ed330e63cea50e4a2a6659ca1ad97bde', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'eac5c6c114d8007db5c24ae159fe6f55e89a946b', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/validator', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'ipl/web' => array( - 'pretty_version' => 'v0.11.1', - 'version' => '0.11.1.0', - 'reference' => 'af3a8a7be5e924f0ad841baf1471d296329aa1e7', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '2e7aa6bce432dd12f1f3b3efb75211f1fdd3878f', 'type' => 'library', 'install_path' => __DIR__ . '/../ipl/web', - 'aliases' => array(), + 'aliases' => array( + 0 => '99.x-dev', + 1 => '9999999-dev', + ), 'dev_requirement' => false, ), 'mtdowling/cron-expression' => array( @@ -218,9 +242,9 @@ 'dev_requirement' => false, ), 'ramsey/uuid' => array( - 'pretty_version' => '4.9.0', - 'version' => '4.9.0.0', - 'reference' => '4e0e23cc785f0724a0e838279a9eb03f28b092a0', + 'pretty_version' => '4.9.1', + 'version' => '4.9.1.0', + 'reference' => '81f941f6f729b1e3ceea61d9d014f8b6c6800440', 'type' => 'library', 'install_path' => __DIR__ . '/../ramsey/uuid', 'aliases' => array(), @@ -247,7 +271,7 @@ 'rhumsaa/uuid' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.9.0', + 0 => '4.9.1', ), ), 'simshaun/recurr' => array( @@ -260,9 +284,9 @@ 'dev_requirement' => false, ), 'symfony/polyfill-php84' => array( - 'pretty_version' => 'v1.32.0', - 'version' => '1.32.0.0', - 'reference' => '000df7860439609837bbe28670b0be15783b7fbf', + 'pretty_version' => 'v1.33.0', + 'version' => '1.33.0.0', + 'reference' => 'd8ced4d875142b6a7426000426b8abc631d6b191', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php84', 'aliases' => array(), diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php index d32d90c..14bf88d 100644 --- a/vendor/composer/platform_check.php +++ b/vendor/composer/platform_check.php @@ -19,8 +19,7 @@ if ($issues) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } - trigger_error( - 'Composer detected issues in your platform: ' . implode(' ', $issues), - E_USER_ERROR + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) ); } diff --git a/vendor/guzzlehttp/psr7/composer.json b/vendor/guzzlehttp/psr7/composer.json index 28d15f5..96098f5 100644 --- a/vendor/guzzlehttp/psr7/composer.json +++ b/vendor/guzzlehttp/psr7/composer.json @@ -62,7 +62,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" diff --git a/vendor/guzzlehttp/psr7/src/MessageTrait.php b/vendor/guzzlehttp/psr7/src/MessageTrait.php index 65dbc4b..c15ee63 100644 --- a/vendor/guzzlehttp/psr7/src/MessageTrait.php +++ b/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -174,10 +174,6 @@ trait MessageTrait return $this->trimAndValidateHeaderValues([$value]); } - if (count($value) === 0) { - throw new \InvalidArgumentException('Header value can not be an empty array.'); - } - return $this->trimAndValidateHeaderValues($value); } diff --git a/vendor/guzzlehttp/psr7/src/Utils.php b/vendor/guzzlehttp/psr7/src/Utils.php index 7682d2c..5451e3d 100644 --- a/vendor/guzzlehttp/psr7/src/Utils.php +++ b/vendor/guzzlehttp/psr7/src/Utils.php @@ -397,7 +397,7 @@ final class Utils restore_error_handler(); if ($ex) { - /** @var $ex \RuntimeException */ + /** @var \RuntimeException $ex */ throw $ex; } @@ -444,7 +444,7 @@ final class Utils restore_error_handler(); if ($ex) { - /** @var $ex \RuntimeException */ + /** @var \RuntimeException $ex */ throw $ex; } diff --git a/vendor/ipl/html/composer.json b/vendor/ipl/html/composer.json index 99c5525..815d1a1 100644 --- a/vendor/ipl/html/composer.json +++ b/vendor/ipl/html/composer.json @@ -9,7 +9,7 @@ "sort-packages": true }, "require": { - "php": ">=7.2", + "php": ">=8.2", "ext-fileinfo": "*", "ipl/stdlib": ">=0.12.0", "ipl/validator": ">=0.5.0", diff --git a/vendor/ipl/html/src/Attribute.php b/vendor/ipl/html/src/Attribute.php index c42485a..ddf23bc 100644 --- a/vendor/ipl/html/src/Attribute.php +++ b/vendor/ipl/html/src/Attribute.php @@ -12,6 +12,9 @@ use InvalidArgumentException; * * Usually attributes are not instantiated directly, but created through an HTML * element's exposed methods. + * + * @phpstan-type _AttributeScalar string|bool|null + * @phpstan-type AttributeValue _AttributeScalar|array<_AttributeScalar> */ class Attribute { @@ -21,14 +24,14 @@ class Attribute /** @var string The separator used if value is an array */ protected $separator = ' '; - /** @var string|array|bool|null */ + /** @var AttributeValue */ protected $value; /** * Create a new HTML attribute from the given name and value * - * @param string $name The name of the attribute - * @param string|bool|array|null $value The value of the attribute + * @param string $name The name of the attribute + * @param AttributeValue $value The value of the attribute * * @throws InvalidArgumentException If the name of the attribute contains special characters */ @@ -40,8 +43,8 @@ class Attribute /** * Create a new HTML attribute from the given name and value * - * @param string $name The name of the attribute - * @param string|bool|array|null $value The value of the attribute + * @param string $name The name of the attribute + * @param AttributeValue $value The value of the attribute * * @return static * @@ -91,8 +94,8 @@ class Attribute * Values are escaped according to the HTML5 double-quoted attribute value syntax: * {@link https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 }. * - * @param string|array $value - * @param string $glue Glue string to join elements if value is an array + * @param string|string[] $value + * @param string $glue Glue string to join elements if value is an array * * @return string */ @@ -184,7 +187,7 @@ class Attribute /** * Get the value of the attribute * - * @return string|bool|array|null + * @return AttributeValue */ public function getValue() { @@ -194,7 +197,7 @@ class Attribute /** * Set the value of the attribute * - * @param string|bool|array|null $value + * @param AttributeValue $value * * @return $this */ @@ -208,7 +211,7 @@ class Attribute /** * Add the given value(s) to the attribute * - * @param string|array $value The value(s) to add + * @param AttributeValue $value The value(s) to add * * @return $this */ @@ -230,7 +233,7 @@ class Attribute * * Does nothing if there is no such value to remove. * - * @param string|array $value The value(s) to remove + * @param AttributeValue $value The value(s) to remove * * @return $this */ diff --git a/vendor/ipl/html/src/Attributes.php b/vendor/ipl/html/src/Attributes.php index 8df9bbd..ed7e668 100644 --- a/vendor/ipl/html/src/Attributes.php +++ b/vendor/ipl/html/src/Attributes.php @@ -17,6 +17,9 @@ use function ipl\Stdlib\get_php_type; * behavior in various ways. * * Attributes usually come in name-value pairs and are rendered as name="value". + * + * @phpstan-import-type AttributeValue from Attribute + * @phpstan-type AttributesType array */ class Attributes implements ArrayAccess, IteratorAggregate { @@ -35,7 +38,7 @@ class Attributes implements ArrayAccess, IteratorAggregate /** * Create new HTML attributes * - * @param array $attributes + * @param AttributesType $attributes */ public function __construct(array $attributes = null) { @@ -57,7 +60,7 @@ class Attributes implements ArrayAccess, IteratorAggregate /** * Create new HTML attributes * - * @param array $attributes + * @param AttributesType $attributes * * @return static */ @@ -76,7 +79,7 @@ class Attributes implements ArrayAccess, IteratorAggregate * construct and return a new Attributes instance. * If the attributes are null, an empty new instance of Attributes is returned. * - * @param array|static|null $attributes + * @param AttributesType|static|null $attributes * * @return static * @@ -174,8 +177,8 @@ class Attributes implements ArrayAccess, IteratorAggregate * * If the attribute with the given name already exists, it gets overridden. * - * @param string|array|Attribute|self $attribute The attribute(s) to add - * @param string|bool|array $value The value of the attribute + * @param string|AttributesType|Attribute|self $attribute The attribute(s) to add + * @param AttributeValue $value The value of the attribute * * @return $this * @@ -224,8 +227,8 @@ class Attributes implements ArrayAccess, IteratorAggregate * If an attribute with the same name already exists, the attribute's value will be added to the current value of * the attribute. * - * @param string|array|Attribute|self $attribute The attribute(s) to add - * @param string|bool|array $value The value of the attribute + * @param string|AttributesType|Attribute|self $attribute The attribute(s) to add + * @param AttributeValue $value The value of the attribute * * @return $this * @@ -246,6 +249,7 @@ class Attributes implements ArrayAccess, IteratorAggregate } if (is_array($attribute)) { + // TODO: Handle if $attribute = [new Attribute('class', 'bar')] foreach ($attribute as $name => $value) { $this->add($name, $value); } @@ -280,7 +284,7 @@ class Attributes implements ArrayAccess, IteratorAggregate * Remove the attribute with the given name or remove the given value from the attribute * * @param string $name The name of the attribute - * @param null|string|array $value The value to remove if specified + * @param AttributeValue $value The value to remove if specified * * @return ?Attribute The removed or changed attribute, if any, otherwise null */ @@ -479,8 +483,8 @@ class Attributes implements ArrayAccess, IteratorAggregate * * If the attribute with the given name already exists, it gets overridden. * - * @param string $name Name of the attribute - * @param mixed $value Value of the attribute + * @param string $name Name of the attribute + * @param AttributeValue $value Value of the attribute * * @throws InvalidArgumentException If the attribute name contains special characters */ diff --git a/vendor/ipl/html/src/BaseHtmlElement.php b/vendor/ipl/html/src/BaseHtmlElement.php index 5dc01ce..0325186 100644 --- a/vendor/ipl/html/src/BaseHtmlElement.php +++ b/vendor/ipl/html/src/BaseHtmlElement.php @@ -2,7 +2,7 @@ namespace ipl\Html; -use InvalidArgumentException; +use ipl\Html\Contract\HtmlElementInterface; use RuntimeException; /** @@ -32,7 +32,7 @@ use RuntimeException; * } * ``` */ -abstract class BaseHtmlElement extends HtmlDocument +abstract class BaseHtmlElement extends HtmlDocument implements HtmlElementInterface { /** * List of void elements which must not contain end tags or content @@ -76,11 +76,6 @@ abstract class BaseHtmlElement extends HtmlDocument /** @var string Tag of element. Set this property in order to provide the element's tag when extending this class */ protected $tag; - /** - * Get the attributes of the element - * - * @return Attributes - */ public function getAttributes() { if ($this->attributes === null) { @@ -97,6 +92,13 @@ abstract class BaseHtmlElement extends HtmlDocument return $this->attributes; } + public function addAttributes($attributes) + { + $this->getAttributes()->add($attributes); + + return $this; + } + /** * Set the attributes of the element * @@ -114,44 +116,16 @@ abstract class BaseHtmlElement extends HtmlDocument return $this; } - /** - * Return true if the attribute with the given name exists, false otherwise - * - * @param string $name - * - * @return bool - */ public function hasAttribute(string $name): bool { return $this->getAttributes()->has($name); } - /** - * Get the attribute with the given name - * - * If the attribute does not already exist, an empty one is automatically created and added to the attributes. - * - * @param string $name - * - * @return Attribute - * - * @throws InvalidArgumentException If the attribute does not yet exist and its name contains special characters - */ public function getAttribute(string $name): Attribute { return $this->getAttributes()->get($name); } - /** - * Set the attribute with the given name and value - * - * If the attribute with the given name already exists, it gets overridden. - * - * @param string $name The name of the attribute - * @param string|bool|array $value The value of the attribute - * - * @return $this - */ public function setAttribute($name, $value) { $this->getAttributes()->set($name, $value); @@ -159,33 +133,11 @@ abstract class BaseHtmlElement extends HtmlDocument return $this; } - /** - * Remove the attribute with the given name or remove the given value from the attribute - * - * @param string $name The name of the attribute - * @param null|string|array $value The value to remove if specified - * - * @return ?Attribute The removed or changed attribute, if any, otherwise null - */ public function removeAttribute(string $name, $value = null): ?Attribute { return $this->getAttributes()->remove($name, $value); } - /** - * Add the given attributes - * - * @param Attributes|array $attributes - * - * @return $this - */ - public function addAttributes($attributes) - { - $this->getAttributes()->add($attributes); - - return $this; - } - /** * Get the default attributes of the element * diff --git a/vendor/ipl/html/src/Contract/Decorator.php b/vendor/ipl/html/src/Contract/Decorator.php new file mode 100644 index 0000000..cfc1b7b --- /dev/null +++ b/vendor/ipl/html/src/Contract/Decorator.php @@ -0,0 +1,55 @@ +getDescription(); + * + * if ($description === null) { + * return; + * } + * + * $results->append(new HtmlElement('p', null, new Text($description))); + * } + * ``` + * + * @param DecorationResults $results + * @param FormElement & HtmlElementInterface $formElement + * + * @return void + */ + public function decorate(DecorationResults $results, FormElement & HtmlElementInterface $formElement): void; +} diff --git a/vendor/ipl/html/src/Contract/DecoratorOptions.php b/vendor/ipl/html/src/Contract/DecoratorOptions.php new file mode 100644 index 0000000..29dbf0b --- /dev/null +++ b/vendor/ipl/html/src/Contract/DecoratorOptions.php @@ -0,0 +1,40 @@ +attributes === null) { + $this->attributes = new Attributes(); + $this->registerAttributeCallbacks($this->attributes); + } + + return $this->attributes; + } + + /** + * Register attribute callbacks + * + * Override this method in order to register attribute callbacks in concrete classes. + * + * @param Attributes $attributes + */ + abstract protected function registerAttributeCallbacks(Attributes $attributes): void; +} diff --git a/vendor/ipl/html/src/Contract/DecoratorOptionsInterface.php b/vendor/ipl/html/src/Contract/DecoratorOptionsInterface.php new file mode 100644 index 0000000..5dad683 --- /dev/null +++ b/vendor/ipl/html/src/Contract/DecoratorOptionsInterface.php @@ -0,0 +1,19 @@ + + */ +interface DefaultFormElementDecoration +{ + /** + * Set the default element decorators. + * + * The default decorators will be applied to all elements that do not have explicit decorators. + * The order of the decorators is important, as it determines the rendering order. + * + * Please see {@see DecoratorChain::addDecorators()} for the supported array formats. + * + * @param decoratorsFormat $decorators + * + * @return $this + */ + public function setDefaultElementDecorators(array $decorators): static; + + /** + * Add custom element decorator loader paths for the elements + * + * Each entry must be an array with index 0: class namespace, index 1: class name suffix (optional). + * + * @param loaderPaths $loaderPaths + * + * @return $this + */ + public function addElementDecoratorLoaderPaths(array $loaderPaths): static; +} diff --git a/vendor/ipl/html/src/Contract/FormElement.php b/vendor/ipl/html/src/Contract/FormElement.php index 1467c50..6d868db 100644 --- a/vendor/ipl/html/src/Contract/FormElement.php +++ b/vendor/ipl/html/src/Contract/FormElement.php @@ -4,9 +4,12 @@ namespace ipl\Html\Contract; use ipl\Html\Attributes; use ipl\Html\Form; +use ipl\Html\FormDecorator\DecoratorChain; /** * Representation of form elements + * + * @phpstan-import-type AttributesType from Attributes */ interface FormElement extends Wrappable { @@ -20,7 +23,7 @@ interface FormElement extends Wrappable /** * Add attributes or options to the form element * - * @param iterable $attributes + * @param Attributes|AttributesType $attributes * * @return $this */ @@ -121,6 +124,20 @@ interface FormElement extends Wrappable */ public function validate(); + /** + * Get all decorators + * + * @return DecoratorChain + */ + public function getDecorators(): DecoratorChain; + + /** + * Get whether the element has any decorators + * + * @return bool + */ + public function hasDecorators(): bool; + /** * Handler which is called after this element has been registered * diff --git a/vendor/ipl/html/src/Contract/HtmlElementInterface.php b/vendor/ipl/html/src/Contract/HtmlElementInterface.php new file mode 100644 index 0000000..b3e4d00 --- /dev/null +++ b/vendor/ipl/html/src/Contract/HtmlElementInterface.php @@ -0,0 +1,90 @@ +request = $request; - $this->emit(Form::ON_REQUEST, [$request]); return $this; } @@ -206,6 +206,8 @@ class Form extends BaseHtmlElement $this->setRequest($request); if (! $this->hasBeenSent()) { + $this->emit(Form::ON_REQUEST, [$request, $this]); + // Always assemble $this->ensureAssembled(); @@ -348,13 +350,13 @@ class Form extends BaseHtmlElement return $this; } - public function remove(ValidHtml $elementOrHtml) + public function remove(ValidHtml $content) { - if ($this->submitButton === $elementOrHtml) { + if ($this->submitButton === $content) { $this->submitButton = null; } - $this->removeElement($elementOrHtml); + $this->removeElement($content); return $this; } diff --git a/vendor/ipl/html/src/FormDecorator/DdDtDecorator.php b/vendor/ipl/html/src/FormDecorator/DdDtDecorator.php index 9cfec20..95808cc 100644 --- a/vendor/ipl/html/src/FormDecorator/DdDtDecorator.php +++ b/vendor/ipl/html/src/FormDecorator/DdDtDecorator.php @@ -2,6 +2,7 @@ namespace ipl\Html\FormDecorator; +use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\FormElement\BaseFormElement; use ipl\Html\Html; @@ -92,7 +93,7 @@ class DdDtDecorator extends BaseHtmlElement implements DecoratorInterface return null; } - public function addHtml(ValidHtml ...$content) + public function addHtml(ValidHtml ...$content): static { // TODO: is this required? if (! in_array($this->wrappedElement, $content, true)) { diff --git a/vendor/ipl/html/src/FormDecorator/DecorationResults.php b/vendor/ipl/html/src/FormDecorator/DecorationResults.php new file mode 100644 index 0000000..014d6f6 --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/DecorationResults.php @@ -0,0 +1,161 @@ +> + */ +class DecorationResults implements ValidHtml +{ + /** @var content The HTML content */ + protected array $content = []; + + /** @var string[] List of decorator names to skip */ + protected array $skipDecorators = []; + + /** + * Add decorator names to be skipped + * + * @param string ...$decoratorNames Decorator names to skip + * + * @return $this + */ + public function skipDecorators(string ...$decoratorNames): static + { + $this->skipDecorators = array_merge($this->skipDecorators, $decoratorNames); + + return $this; + } + + /** + * Get the list of decorator names to skip + * + * @return string[] + */ + public function getSkipDecorators(): array + { + return $this->skipDecorators; + } + + /** + * Transform the results according to the given case with the given HTML element + * + * @param Transformation $case + * @param ValidHtml|MutableHtml $item + * + * @return $this + */ + public function transform(Transformation $case, ValidHtml|MutableHtml $item): static + { + match ($case) { + Transformation::Append => $this->append($item), + Transformation::Prepend => $this->prepend($item), + Transformation::Wrap => $this->wrap($item) + }; + + return $this; + } + + /** + * Add the given HTML element to the results + * + * @param ValidHtml $item The HTML content to be added + * + * @return $this + */ + public function append(ValidHtml $item): static + { + $this->content[] = $item; + + return $this; + } + + /** + * Add the given HTML element to the beginning of the results + * + * @param ValidHtml $item The HTML element to be added + * + * @return $this + */ + public function prepend(ValidHtml $item): static + { + array_unshift($this->content, $item); + + return $this; + } + + /** + * Add the given HTML element as the wrapper of the results + * + * @param MutableHtml $item The HTML element to wrap around the content + * + * @return $this + */ + public function wrap(MutableHtml $item): static + { + $this->content = [[$item, $this->content]]; + + return $this; + } + + /** + * Render the results + * + * @return string The rendered HTML content + */ + public function render(): string + { + if (empty($this->content)) { + return ''; + } + + $content = new HtmlDocument(); + $this->resolveContent($content, $this->content); + + return $content->render(); + } + + /** + * Resolve content + * + * @param MutableHtml $parent The parent element + * @param content $content The content to be added + * + * @return void + */ + protected function resolveContent(MutableHtml $parent, array $content): void + { + foreach ($content as $item) { + if (is_array($item)) { + $item = $this->resolveWrappedContent($item[0], $item[1]); + } + + $parent->addHtml($item); + } + } + + /** + * Resolve wrapped content + * + * @param MutableHtml $parent The parent element + * @param ValidHtml|content $item The content to be added + * + * @return ValidHtml The resolved parent element with content added + */ + protected function resolveWrappedContent(MutableHtml $parent, ValidHtml|array $item): ValidHtml + { + if ($item instanceof ValidHtml) { + $parent->addHtml($item); + } else { + $this->resolveContent($parent, $item); + } + + return $parent; + } +} diff --git a/vendor/ipl/html/src/FormDecorator/DecoratorChain.php b/vendor/ipl/html/src/FormDecorator/DecoratorChain.php new file mode 100644 index 0000000..79fb759 --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/DecoratorChain.php @@ -0,0 +1,299 @@ + + * @phpstan-type _decoratorsFormat1 array + * @phpstan-type _decoratorsFormat2 array + * @phpstan-type decoratorsFormat _decoratorsFormat1 | _decoratorsFormat2 + */ +class DecoratorChain +{ + use Plugins; + + /** @var Decorator[] All registered decorators */ + protected array $decorators = []; + + /** + * Create a new decorator chain + */ + public function __construct() + { + $this->addDefaultPluginLoader('decorator', __NAMESPACE__, 'Decorator'); + } + + /** + * Add a decorator loader + * + * @param string $namespace Namespace of the decorator(s) + * @param string $suffix Decorator class name suffix, if any + * + * @return $this + */ + public function addDecoratorLoader(string $namespace, string $suffix = ''): static + { + $this->addPluginLoader('decorator', $namespace, $suffix); + + return $this; + } + + /** + * Add a decorator to the chain. + * + * @param Decorator|string $decorator + * @param decoratorOptionsFormat $options Only allowed if parameter 1 is a string + * + * @return $this + * + * @throws InvalidArgumentException If the decorator specification is invalid + */ + public function addDecorator(Decorator|string $decorator, array $options = []): static + { + if (! empty($options) && ! is_string($decorator)) { + throw new InvalidArgumentException('No options are allowed with parameter 1 of type Decorator'); + } + + if (! $decorator instanceof Decorator) { + $decorator = $this->createDecorator($decorator, $options); + } + + $this->decorators[] = $decorator; + + return $this; + } + + /** + * Add the decorators from the given decorator specification to the chain + * + * The order of the decorators is important, as it determines the rendering order. + * + * *The following array formats are supported:* + * + * ``` + * // When no options are required or defaults are sufficient + * $decorators = [ + * 'HtmlTag', + * 'Label' + * ]; + * + * // Override default options by defining the option key and value + * + * // key: decorator name, value: options + * $decorators = [ + * 'HtmlTag' => ['tag' => 'span', 'placement' => 'append'], + * 'Label' => ['class' => 'element-label'] + * ]; + * + * // or define the `name` and `options` key + * $decorators = [ + * ['name' => 'HtmlTag', 'options' => ['tag' => 'span', 'placement' => 'append']], + * ['name' => 'Label', 'options' => ['class' => 'element-label']] + * ]; + * + * // or add Decorator instances + * $decorators = [ + * (new HtmlTagDecorator())->getAttributes()->add(['tag' => 'span', 'placement' => 'append']), + * (new LabelDecorator())->getAttributes()->add(['class' => 'element-label']) + * ]; + * ``` + * + * @param static|decoratorsFormat $decorators + * + * @return $this + * + * @throws InvalidArgumentException If the decorator specification is invalid + */ + public function addDecorators(DecoratorChain|array $decorators): static + { + if ($decorators instanceof static) { + foreach ($decorators->getDecorators() as $decorator) { + $this->addDecorator($decorator); + } + + return $this; + } + + foreach ($decorators as $decoratorName => $decoratorOptions) { + $position = $decoratorName; + if (is_int($decoratorName)) { + if (is_array($decoratorOptions)) { + if (! isset($decoratorOptions['name'])) { + throw new InvalidArgumentException("Key 'name' is missing"); + } + + $decoratorName = $decoratorOptions['name']; + unset($decoratorOptions['name']); + + $options = []; + if (isset($decoratorOptions['options'])) { + $options = $decoratorOptions['options']; + + unset($decoratorOptions['options']); + } + + if (! empty($decoratorOptions)) { + throw new InvalidArgumentException( + sprintf( + "No other keys except 'name' and 'options' are allowed, got '%s'", + implode("', '", array_keys($decoratorOptions)) + ) + ); + } + + $decoratorOptions = $options; + } else { + $decoratorName = $decoratorOptions; + $decoratorOptions = []; + } + } + + if (! is_array($decoratorOptions)) { + throw new InvalidArgumentException(sprintf( + "The key must be a decorator name and value must be an array of options, got value of type" + . " '%s' for key '%s'", + get_php_type($decoratorOptions), + $decoratorName, + )); + } + + if (! is_string($decoratorName) && ! $decoratorName instanceof Decorator) { + throw new InvalidArgumentException(sprintf( + 'Expects array value at position %d to be a string or an instance of %s, got %s instead', + $position, + Decorator::class, + get_php_type($decoratorName) + )); + } + + $this->addDecorator($decoratorName, $decoratorOptions); + } + + return $this; + } + + /** + * Get all decorators + * + * @return Decorator[] + */ + public function getDecorators(): array + { + return $this->decorators; + } + + /** + * Clear all decorators from the chain + * + * @return $this + */ + public function clearDecorators(): static + { + $this->decorators = []; + + return $this; + } + + /** + * Create a decorator from the given name and options + * + * @param string $name + * @param decoratorOptionsFormat $options + * + * @return Decorator + * + * @throws InvalidArgumentException If the given decorator is unknown + */ + protected function createDecorator(string $name, array $options = []): Decorator + { + $class = $this->loadPlugin('decorator', $name); + + if (! $class) { + throw new InvalidArgumentException(sprintf( + "Can't load decorator '%s'. decorator unknown", + $name + )); + } + + $decorator = new $class(); + + if (! $decorator instanceof Decorator) { + throw new UnexpectedValueException(sprintf( + "%s expects loader to return an instance of %s for decorator '%s', got %s instead", + __METHOD__, + Decorator::class, + $name, + get_php_type($decorator) + )); + } + + if (! empty($options)) { + if (! $decorator instanceof DecoratorOptionsInterface) { + throw new InvalidArgumentException(sprintf("Decorator '%s' does not support options", $name)); + } + + $decorator->getAttributes()->add($options); + } + + return $decorator; + } + + /** + * Get whether the chain has decorators + * + * @return bool + */ + public function hasDecorators(): bool + { + return ! empty($this->decorators); + } + + /** + * Apply the registered decorators to the given form element + * + * @param FormElement $formElement The form element to decorate + * + * @return ValidHtml + * + * @throws LogicException If a decorator wants to skip a decorator that has already been applied + */ + public function apply(FormElement $formElement): ValidHtml + { + $results = new DecorationResults(); + $appliedDecorators = []; + $toSkip = []; + foreach ($this->decorators as $decorator) { + $decoratorName = $decorator->getName(); + if (in_array($decoratorName, $toSkip, true)) { + continue; + } + + $decorator->decorate($results, $formElement); + + $appliedDecorators[] = $decoratorName; + $toSkip = $results->getSkipDecorators(); + $alreadyApplied = array_intersect($toSkip, $appliedDecorators); + if (! empty($alreadyApplied)) { + throw new LogicException(sprintf( + "Cannot skip Decorator(s) '%s', Decoration already applied", + implode("', '", $alreadyApplied) + )); + } + } + + return $results; + } +} diff --git a/vendor/ipl/html/src/FormDecorator/DescriptionDecorator.php b/vendor/ipl/html/src/FormDecorator/DescriptionDecorator.php new file mode 100644 index 0000000..63ebbaa --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/DescriptionDecorator.php @@ -0,0 +1,80 @@ +class; + } + + /** + * Set the css class(es) + * + * @param string|string[] $class + * + * @return $this + */ + public function setClass(string|array $class): static + { + $this->class = $class; + + return $this; + } + + public function getName(): string + { + return 'Description'; + } + + public function decorate(DecorationResults $results, FormElement & HtmlElementInterface $formElement): void + { + $description = $formElement->getDescription(); + + if ($description === null || $formElement->getTag() === 'fieldset') { + return; + } + + $descriptionId = null; + if ($formElement->getAttributes()->has('id')) { + $descriptionId = 'desc_' . $formElement->getAttributes()->get('id')->getValue(); + $formElement->getAttributes()->set('aria-describedby', $descriptionId); + } + + $results->append( + new HtmlElement( + 'p', + new Attributes(['class' => $this->getClass(), 'id' => $descriptionId]), + new Text($description) + ) + ); + } + + protected function registerAttributeCallbacks(Attributes $attributes): void + { + $attributes->registerAttributeCallback('class', null, $this->setClass(...)); + } +} diff --git a/vendor/ipl/html/src/FormDecorator/ErrorsDecorator.php b/vendor/ipl/html/src/FormDecorator/ErrorsDecorator.php new file mode 100644 index 0000000..aa0214d --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/ErrorsDecorator.php @@ -0,0 +1,69 @@ +class; + } + + /** + * Set the css class(es) + * + * @param string|string[] $class + * + * @return $this + */ + public function setClass(string|array $class): static + { + $this->class = $class; + + return $this; + } + + public function getName(): string + { + return 'Errors'; + } + + public function decorate(DecorationResults $results, FormElement & HtmlElementInterface $formElement): void + { + $errors = new HtmlElement('ul', new Attributes(['class' => $this->getClass()])); + foreach ($formElement->getMessages() as $message) { + $errors->addHtml(new HtmlElement('li', null, Text::create($message))); + } + + if (! $errors->isEmpty()) { + $results->append($errors); + } + } + + protected function registerAttributeCallbacks(Attributes $attributes): void + { + $attributes->registerAttributeCallback('class', null, $this->setClass(...)); + } +} diff --git a/vendor/ipl/html/src/FormDecorator/FieldsetDecorator.php b/vendor/ipl/html/src/FormDecorator/FieldsetDecorator.php new file mode 100644 index 0000000..af93b88 --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/FieldsetDecorator.php @@ -0,0 +1,46 @@ +getTag() !== 'fieldset') { + return; + } + + $description = $formElement->getDescription(); + if ($description !== null) { + $attributes = null; + if ($formElement->getAttributes()->has('id')) { + $descriptionId = 'desc_' . $formElement->getAttributes()->get('id')->getValue(); + $formElement->getAttributes()->set('aria-describedby', $descriptionId); + $attributes = new Attributes(['id' => $descriptionId]); + } + + $formElement->prependHtml(new HtmlElement('p', $attributes, new Text($description))); + } + + $label = $formElement->getLabel(); + if ($label !== null) { + $formElement->prependHtml(new HtmlElement('legend', null, Text::create($label))); + } + } +} diff --git a/vendor/ipl/html/src/FormDecorator/HtmlTagDecorator.php b/vendor/ipl/html/src/FormDecorator/HtmlTagDecorator.php new file mode 100644 index 0000000..385b4a5 --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/HtmlTagDecorator.php @@ -0,0 +1,189 @@ +tag)) { + throw new RuntimeException('Option "tag" must be set'); + } + + return $this->tag; + } + + /** + * Set the HTML tag to use for the decoration + * + * @param string $tag + * + * @return $this + */ + public function setTag(string $tag): static + { + $this->tag = $tag; + + return $this; + } + + /** + * Set the transformation type of the HTML tag + * + * @param Transformation $transformation + * + * @return $this + */ + public function setTransformation(Transformation $transformation): static + { + $this->transformation = $transformation; + + return $this; + } + + /** + * Get the transformation type of the HTML tag + * + * @return Transformation + */ + public function getTransformation(): Transformation + { + return $this->transformation; + } + + /** + * Get the condition callable to decide whether to decorate the element + * + * @return ?callable(FormElement & HtmlElementInterface): bool + */ + public function getCondition(): ?callable + { + return $this->condition; + } + + /** + * Set the condition callable to decide whether to decorate the element + * + * @param callable(FormElement & HtmlElementInterface): bool $condition + * + * @return $this + */ + public function setCondition(callable $condition): static + { + $this->condition = $condition; + + return $this; + } + + /** + * Get the css class(es) + * + * @return ?(string|string[]) + */ + public function getClass(): string|array|null + { + return $this->class; + } + + /** + * Set the css class(es) + * + * @param string|string[] $class + * + * @return $this + */ + public function setClass(string|array $class): static + { + $this->class = $class; + + return $this; + } + + public function getName(): string + { + return 'HtmlTag'; + } + + /** + * @throws InvalidArgumentException if the condition callback does not return a boolean + * @throws RuntimeException if the condition callback throws an exception + */ + public function decorate(DecorationResults $results, FormElement & HtmlElementInterface $formElement): void + { + $condition = $this->getCondition(); + if ($condition !== null) { + try { + $shouldDecorate = $condition($formElement); + } catch (Throwable $e) { + throw new RuntimeException('Condition callback failed', previous: $e); + } + + if (! is_bool($shouldDecorate)) { + throw new InvalidArgumentException(sprintf( + 'Condition callback must return a boolean, got %s', + get_php_type($shouldDecorate) + )); + } + + if (! $shouldDecorate) { + return; + } + } + + $class = $this->getClass(); + $results->transform( + $this->getTransformation(), + new HtmlElement($this->getTag(), $class === null ? null : new Attributes(['class' => $class])) + ); + } + + protected function registerAttributeCallbacks(Attributes $attributes): void + { + $attributes + ->registerAttributeCallback('tag', null, $this->setTag(...)) + ->registerAttributeCallback('transformation', null, $this->setTransformation(...)) + ->registerAttributeCallback('condition', null, $this->setCondition(...)) + ->registerAttributeCallback('class', null, $this->setClass(...)); + } +} diff --git a/vendor/ipl/html/src/FormDecorator/LabelDecorator.php b/vendor/ipl/html/src/FormDecorator/LabelDecorator.php new file mode 100644 index 0000000..cac35d9 --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/LabelDecorator.php @@ -0,0 +1,76 @@ +class; + } + + /** + * Set the css class(es) + * + * @param string|string[] $class + * + * @return $this + */ + public function setClass(string|array $class): static + { + $this->class = $class; + + return $this; + } + + public function getName(): string + { + return 'Label'; + } + + public function decorate(DecorationResults $results, FormElement & HtmlElementInterface $formElement): void + { + if ( + $formElement instanceof FormSubmitElement + || $formElement->getTag() === 'fieldset' + || $formElement->getLabel() === null + ) { + return; + } + + $labelAttr = new Attributes(['class' => $this->getClass()]); + if ($formElement->getAttributes()->has('id')) { + $labelAttr->add(['for' => $formElement->getAttributes()->get('id')->getValue()]); + } + + $results->append(new HtmlElement('label', $labelAttr, new Text($formElement->getLabel()))); + } + + protected function registerAttributeCallbacks(Attributes $attributes): void + { + $attributes->registerAttributeCallback('class', null, $this->setClass(...)); + } +} diff --git a/vendor/ipl/html/src/FormDecorator/RenderElementDecorator.php b/vendor/ipl/html/src/FormDecorator/RenderElementDecorator.php new file mode 100644 index 0000000..b9b883a --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/RenderElementDecorator.php @@ -0,0 +1,23 @@ +append($formElement); + } +} diff --git a/vendor/ipl/html/src/FormDecorator/Transformation.php b/vendor/ipl/html/src/FormDecorator/Transformation.php new file mode 100644 index 0000000..8e99679 --- /dev/null +++ b/vendor/ipl/html/src/FormDecorator/Transformation.php @@ -0,0 +1,18 @@ + Value candidates of the element */ protected $valueCandidates = []; + /** All registered decorators */ + protected ?DecoratorChain $decorators = null; + /** * Create a new form element * @@ -351,7 +360,8 @@ abstract class BaseFormElement extends BaseHtmlElement implements FormElement, V ->registerAttributeCallback('description', null, [$this, 'setDescription']) ->registerAttributeCallback('validators', null, [$this, 'setValidators']) ->registerAttributeCallback('ignore', null, [$this, 'setIgnored']) - ->registerAttributeCallback('required', [$this, 'getRequiredAttribute'], [$this, 'setRequired']); + ->registerAttributeCallback('required', [$this, 'getRequiredAttribute'], [$this, 'setRequired']) + ->registerAttributeCallback('decorators', null, [$this, 'setDecorators']); $this->registerCallbacks(); } @@ -387,4 +397,34 @@ abstract class BaseFormElement extends BaseHtmlElement implements FormElement, V return $this->getName(); } + + public function getDecorators(): DecoratorChain + { + if ($this->decorators === null) { + $this->decorators = new DecoratorChain(); + } + + return $this->decorators; + } + + /** + * Set the decorators + * + * @param decoratorsFormat $decorators + * + * @return $this + */ + public function setDecorators(array $decorators): static + { + $this->getDecorators() + ->clearDecorators() + ->addDecorators($decorators); + + return $this; + } + + public function hasDecorators(): bool + { + return $this->getDecorators()->hasDecorators(); + } } diff --git a/vendor/ipl/html/src/FormElement/FieldsetElement.php b/vendor/ipl/html/src/FormElement/FieldsetElement.php index c18d472..6afc9d9 100644 --- a/vendor/ipl/html/src/FormElement/FieldsetElement.php +++ b/vendor/ipl/html/src/FormElement/FieldsetElement.php @@ -4,14 +4,16 @@ namespace ipl\Html\FormElement; use InvalidArgumentException; use ipl\Html\Common\MultipleAttribute; +use ipl\Html\Contract\DefaultFormElementDecoration; use ipl\Html\Contract\FormElement; use ipl\Html\Contract\FormElementDecorator; use ipl\Html\Contract\Wrappable; +use ipl\Html\HtmlDocument; use LogicException; use function ipl\Stdlib\get_php_type; -class FieldsetElement extends BaseFormElement +class FieldsetElement extends BaseFormElement implements DefaultFormElementDecoration { use FormElements { FormElements::getValue as private getElementValue; @@ -26,6 +28,8 @@ class FieldsetElement extends BaseFormElement */ public function hasValue() { + $this->ensureAssembled(); + foreach ($this->getElements() as $element) { if ($element->hasValue()) { return true; @@ -92,7 +96,7 @@ class FieldsetElement extends BaseFormElement return null; } - public function setWrapper(Wrappable $wrapper) + public function setWrapper(HtmlDocument|Wrappable $wrapper) { // TODO(lippserd): Revise decorator implementation to properly implement decorator propagation if ( diff --git a/vendor/ipl/html/src/FormElement/FormElements.php b/vendor/ipl/html/src/FormElement/FormElements.php index 4a2c598..75da096 100644 --- a/vendor/ipl/html/src/FormElement/FormElements.php +++ b/vendor/ipl/html/src/FormElement/FormElements.php @@ -3,10 +3,12 @@ namespace ipl\Html\FormElement; use InvalidArgumentException; +use ipl\Html\Contract\DefaultFormElementDecoration; use ipl\Html\Contract\FormElement; use ipl\Html\Contract\FormElementDecorator; use ipl\Html\Contract\ValueCandidates; use ipl\Html\Form; +use ipl\Html\FormDecorator\DecoratorChain; use ipl\Html\FormDecorator\DecoratorInterface; use ipl\Html\ValidHtml; use ipl\Stdlib\Events; @@ -15,6 +17,10 @@ use UnexpectedValueException; use function ipl\Stdlib\get_php_type; +/** + * @phpstan-import-type decoratorsFormat from DecoratorChain + * @phpstan-import-type loaderPaths from DefaultFormElementDecoration + */ trait FormElements { use Events; @@ -29,12 +35,56 @@ trait FormElements /** @var bool Whether the default element loader has been registered */ protected $defaultElementLoaderRegistered = false; + /** + * Custom Element decorator loader paths + * + * Override this property to add custom decorator loader paths. + * + * @var loaderPaths + */ + protected array $elementDecoratorLoaderPaths = []; + + /** + * Default element decorators + * + * Override this property to change the default decorators of the elements. + * + * Please see {@see DecoratorChain::addDecorators()} for the supported array formats. + * + * @var decoratorsFormat + */ + protected array $defaultElementDecorators = []; + /** @var FormElement[] */ private $elements = []; /** @var array> */ private $populatedValues = []; + /** + * Get the default element decorators. + * + * @return decoratorsFormat + */ + public function getDefaultElementDecorators(): array + { + return $this->defaultElementDecorators; + } + + public function setDefaultElementDecorators(array $decorators): static + { + $this->defaultElementDecorators = $decorators; + + return $this; + } + + public function addElementDecoratorLoaderPaths(array $loaderPaths): static + { + $this->elementDecoratorLoaderPaths = $loaderPaths; + + return $this; + } + /** * Get all elements * @@ -157,6 +207,28 @@ trait FormElements /** @var FormElement $element */ $element = new $class($name); + $customDecoratorPaths = $this->elementDecoratorLoaderPaths; + $elementDecoratorChain = $element->getDecorators(); + if (! empty($customDecoratorPaths)) { + if ($element instanceof DefaultFormElementDecoration) { + $element->addElementDecoratorLoaderPaths($customDecoratorPaths); + } + + foreach ($customDecoratorPaths as $path) { + $elementDecoratorChain->addDecoratorLoader($path[0], $path[1] ?? ''); + } + } + + $isHidden = $element instanceof HiddenElement || ! $element->getAttributes()->get('hidden')->isEmpty(); + $defaultDecorators = $this->getDefaultElementDecorators(); + if (! $isHidden && ! empty($defaultDecorators) && ! $this->hasDefaultElementDecorator()) { + if ($element instanceof DefaultFormElementDecoration) { + $element->setDefaultElementDecorators($defaultDecorators); + } + + $elementDecoratorChain->addDecorators($defaultDecorators); + } + if ($options !== null) { $element->addAttributes($options); } @@ -454,6 +526,11 @@ trait FormElements */ protected function decorate(FormElement $element) { + if ($element->hasDecorators()) { + // new decorator implementation in use + return $this; + } + if ($this->hasDefaultElementDecorator()) { $decorator = $this->getDefaultElementDecorator(); @@ -484,18 +561,18 @@ trait FormElements ]); } - public function remove(ValidHtml $elementOrHtml) + public function remove(ValidHtml $content) { - if ($elementOrHtml instanceof FormElement) { - if ($this->hasElement($elementOrHtml)) { - $name = array_search($elementOrHtml, $this->elements, true); + if ($content instanceof FormElement) { + if ($this->hasElement($content)) { + $name = array_search($content, $this->elements, true); if ($name !== false) { unset($this->elements[$name]); } } } - return parent::remove($elementOrHtml); + return parent::remove($content); } /** @@ -506,4 +583,25 @@ trait FormElements protected function onElementRegistered(FormElement $element) { } + + /** + * Render the element with decorators + * + * @param FormElement $element + * + * @return ValidHtml + */ + protected function applyDecoration(FormElement $element): ValidHtml + { + return $element->getDecorators()->apply($element); + } + + public function renderElement(ValidHtml $element): string + { + if ($element instanceof FormElement && $element->hasDecorators()) { + $element = $this->applyDecoration($element); + } + + return parent::renderElement($element); + } } diff --git a/vendor/ipl/html/src/FormElement/TextElement.php b/vendor/ipl/html/src/FormElement/TextElement.php index 0e3423d..b650209 100644 --- a/vendor/ipl/html/src/FormElement/TextElement.php +++ b/vendor/ipl/html/src/FormElement/TextElement.php @@ -2,7 +2,43 @@ namespace ipl\Html\FormElement; +use ipl\Html\Attributes; + class TextElement extends InputElement { protected $type = 'text'; + + /** @var ?string Placeholder text for the input */ + protected ?string $placeholder = null; + + /** + * Get the placeholder + * + * @return ?string + */ + public function getPlaceholder(): ?string + { + return $this->placeholder; + } + + /** + * Set the placeholder + * + * @param ?string $placeholder + * + * @return $this + */ + public function setPlaceholder(?string $placeholder): self + { + $this->placeholder = $placeholder; + + return $this; + } + + protected function registerAttributeCallbacks(Attributes $attributes): void + { + parent::registerAttributeCallbacks($attributes); + + $attributes->registerAttributeCallback('placeholder', [$this, 'getPlaceholder'], [$this, 'setPlaceholder']); + } } diff --git a/vendor/ipl/html/src/HtmlDocument.php b/vendor/ipl/html/src/HtmlDocument.php index d9a153a..c046509 100644 --- a/vendor/ipl/html/src/HtmlDocument.php +++ b/vendor/ipl/html/src/HtmlDocument.php @@ -4,6 +4,7 @@ namespace ipl\Html; use Countable; use InvalidArgumentException; +use ipl\Html\Contract\MutableHtml; use ipl\Html\Contract\Wrappable; use ipl\Stdlib\Events; use RuntimeException; @@ -14,7 +15,7 @@ use Throwable; * * An HTML document is composed of a tree of HTML nodes, i.e. text nodes and HTML elements. */ -class HtmlDocument implements Countable, Wrappable +class HtmlDocument implements Countable, Wrappable, MutableHtml { use Events; @@ -27,14 +28,14 @@ class HtmlDocument implements Countable, Wrappable /** @var bool Whether the document has been assembled */ protected $hasBeenAssembled = false; - /** @var Wrappable Wrapper */ + /** @var ?(HtmlDocument|Wrappable) Wrapper */ protected $wrapper; - /** @var Wrappable Wrapped element */ - private $wrapped; + /** @var ?(HtmlDocument|Wrappable) Wrapped element */ + private null|HtmlDocument|Wrappable $wrapped = null; - /** @var HtmlDocument The currently responsible wrapper */ - private $renderedBy; + /** @var ?(HtmlDocument|Wrappable) The currently responsible wrapper */ + private null|HtmlDocument|Wrappable $renderedBy = null; /** @var ValidHtml[] Content */ private $content = []; @@ -45,11 +46,11 @@ class HtmlDocument implements Countable, Wrappable /** * Set the element to wrap * - * @param Wrappable $element + * @param HtmlDocument|Wrappable $element * * @return $this */ - private function setWrapped(Wrappable $element) + private function setWrapped(HtmlDocument|Wrappable $element) { $this->wrapped = $element; @@ -59,7 +60,7 @@ class HtmlDocument implements Countable, Wrappable /** * Consume the wrapped element * - * @return Wrappable + * @return ?(HtmlDocument|Wrappable) */ private function consumeWrapped() { @@ -69,11 +70,6 @@ class HtmlDocument implements Countable, Wrappable return $wrapped; } - /** - * Get the content - * - * return ValidHtml[] - */ public function getContent() { return $this->content; @@ -94,13 +90,6 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Set content - * - * @param ValidHtml ...$content - * - * @return $this - */ public function setHtmlContent(ValidHtml ...$content) { $this->content = []; @@ -158,14 +147,6 @@ class HtmlDocument implements Countable, Wrappable )); } - /** - * Insert Html after an existing Html node - * - * @param ValidHtml $newNode - * @param ValidHtml $existingNode - * - * @return $this - */ public function insertAfter(ValidHtml $newNode, ValidHtml $existingNode): self { $index = array_search($existingNode, $this->content, true); @@ -180,14 +161,6 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Insert Html after an existing Html node - * - * @param ValidHtml $newNode - * @param ValidHtml $existingNode - * - * @return $this - */ public function insertBefore(ValidHtml $newNode, ValidHtml $existingNode): self { $index = array_search($existingNode, $this->content); @@ -216,13 +189,6 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Add content - * - * @param ValidHtml ...$content - * - * @return $this - */ public function addHtml(ValidHtml ...$content) { foreach ($content as $element) { @@ -252,25 +218,15 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Check whether the given element is a direct or indirect child of this document - * - * A direct child is one that is part of this document's content. An indirect child - * is one that is part of a direct child's content (recursively). - * - * @param ValidHtml $element - * - * @return bool - */ - public function contains(ValidHtml $element) + public function contains(ValidHtml $content) { - $key = spl_object_hash($element); + $key = spl_object_hash($content); if (array_key_exists($key, $this->contentIndex)) { return true; } foreach ($this->content as $child) { - if ($child instanceof self && $child->contains($element)) { + if ($child instanceof self && $child->contains($content)) { return true; } } @@ -292,13 +248,6 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Prepend content - * - * @param ValidHtml ...$content - * - * @return $this - */ public function prependHtml(ValidHtml ...$content) { foreach (array_reverse($content) as $html) { @@ -310,16 +259,9 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Remove content - * - * @param ValidHtml $html - * - * @return $this - */ - public function remove(ValidHtml $html) + public function remove(ValidHtml $content) { - $key = spl_object_hash($html); + $key = spl_object_hash($content); if (array_key_exists($key, $this->contentIndex)) { foreach ($this->contentIndex[$key] as $pos) { unset($this->content[$pos]); @@ -349,11 +291,6 @@ class HtmlDocument implements Countable, Wrappable return $this; } - /** - * Get whether the document is empty - * - * @return bool - */ public function isEmpty() { $this->ensureAssembled(); @@ -387,7 +324,7 @@ class HtmlDocument implements Countable, Wrappable $element->renderedBy = $this; } - $html[] = $element->render(); + $html[] = $this->renderElement($element); if ($element instanceof self) { $element->renderedBy = null; @@ -397,6 +334,18 @@ class HtmlDocument implements Countable, Wrappable return implode($this->contentSeparator, $html); } + /** + * Render the given element to HTML + * + * @param ValidHtml $element + * + * @return string The rendered HTML + */ + public function renderElement(ValidHtml $element): string + { + return $element->render(); + } + public function __clone() { foreach ($this->content as $key => $element) { @@ -442,7 +391,11 @@ class HtmlDocument implements Countable, Wrappable $wrapper = $this->wrapper; if (isset($this->renderedBy)) { - if ($wrapper === $this->renderedBy || $wrapper->contains($this->renderedBy)) { + // TODO: The instanceof check can be removed as once the Wrappable interface is removed + if ( + $wrapper === $this->renderedBy + || ($wrapper instanceof self && $wrapper->contains($this->renderedBy)) + ) { // $this might be an intermediate wrapper that's already about to be rendered. // In case of an element (referencing $this as a wrapper) that is a child of an // outer wrapper, it is required to ignore $wrapper as otherwise it's a loop. @@ -493,14 +446,27 @@ class HtmlDocument implements Countable, Wrappable return $this->wrapper; } - public function setWrapper(Wrappable $wrapper) + public function setWrapper(HtmlDocument|Wrappable $wrapper) { + if (! $wrapper instanceof self) { + trigger_error( + sprintf( + '%s::%s: Passing an instance of %s that does not extend class %s is deprecated.', + static::class, + __FUNCTION__, + Wrappable::class, + self::class + ), + E_USER_DEPRECATED + ); + } + $this->wrapper = $wrapper; return $this; } - public function addWrapper(Wrappable $wrapper) + public function addWrapper(HtmlDocument|Wrappable $wrapper) { if ($this->wrapper === null) { $this->setWrapper($wrapper); @@ -511,7 +477,7 @@ class HtmlDocument implements Countable, Wrappable return $this; } - public function prependWrapper(Wrappable $wrapper) + public function prependWrapper(HtmlDocument|Wrappable $wrapper) { if ($this->wrapper === null) { $this->setWrapper($wrapper); diff --git a/vendor/ipl/sql/src/Connection.php b/vendor/ipl/sql/src/Connection.php index de84c72..9480fe7 100644 --- a/vendor/ipl/sql/src/Connection.php +++ b/vendor/ipl/sql/src/Connection.php @@ -8,6 +8,7 @@ use InvalidArgumentException; use ipl\Sql\Contract\Adapter; use ipl\Sql\Contract\Quoter; use ipl\Stdlib\Plugins; +use LogicException; use PDO; use PDOStatement; @@ -438,6 +439,25 @@ class Connection implements Quoter return $this->prepexec($insert); } + /** + * Get the ID of the last inserted row + * + * @param ?string $name The name of the sequence object from which the ID should be returned. + * + * @throws LogicException If no connection to the database is established + * @return false|string + */ + public function lastInsertId(?string $name = null): false|string + { + if ($this->pdo === null) { + throw new LogicException( + 'Cannot get last insert ID because no connection to the database is established.' + ); + } + + return $this->pdo->lastInsertId($name); + } + /** * Update table rows with the specified data, optionally based on a given condition * diff --git a/vendor/ipl/sql/src/Test/TestConnection.php b/vendor/ipl/sql/src/Test/TestConnection.php index 981696b..913070f 100644 --- a/vendor/ipl/sql/src/Test/TestConnection.php +++ b/vendor/ipl/sql/src/Test/TestConnection.php @@ -13,4 +13,53 @@ class TestConnection extends Connection { $this->adapter = new TestAdapter(); } + + public function connect() + { + return $this; + } + + public function beginTransaction() + { + throw new \LogicException('Transactions are not supported by the test connection'); + } + + public function commitTransaction() + { + throw new \LogicException('Transactions are not supported by the test connection'); + } + + public function rollbackTransaction() + { + throw new \LogicException('Transactions are not supported by the test connection'); + } + + public function prepexec($stmt, $values = null) + { + if (PHP_MAJOR_VERSION >= 8) { + return new class extends \PDOStatement { + public function getIterator(): \Iterator + { + return new \ArrayIterator([]); + } + + public function setFetchMode($mode, ...$args): bool + { + return true; + } + }; + } else { + return new class extends \PDOStatement { + public function getIterator(): \Iterator + { + return new \ArrayIterator([]); + } + + public function setFetchMode($mode, $params = null): bool + { + return true; + } + }; + } + } } diff --git a/vendor/ipl/validator/composer.json b/vendor/ipl/validator/composer.json index 51ba68d..260b7d9 100644 --- a/vendor/ipl/validator/composer.json +++ b/vendor/ipl/validator/composer.json @@ -10,7 +10,7 @@ "ext-openssl": "*", "ipl/stdlib": ">=0.12.0", "ipl/i18n": ">=0.2.0", - "psr/http-message": "~1.0" + "psr/http-message": "^1.1" }, "autoload": { "psr-4": { @@ -23,6 +23,8 @@ } }, "require-dev": { - "guzzlehttp/psr7": "^1" + "guzzlehttp/psr7": "^1", + "ipl/stdlib": "dev-main", + "ipl/i18n": "dev-main" } } diff --git a/vendor/ipl/validator/src/BetweenValidator.php b/vendor/ipl/validator/src/BetweenValidator.php index 3d7faaf..c8dc026 100644 --- a/vendor/ipl/validator/src/BetweenValidator.php +++ b/vendor/ipl/validator/src/BetweenValidator.php @@ -12,10 +12,10 @@ class BetweenValidator extends BaseValidator { use Translation; - /** @var mixed Min value */ + /** @var int|float Min value */ protected $min; - /** @var mixed Max value */ + /** @var int|float Max value */ protected $max; /** @@ -33,14 +33,14 @@ class BetweenValidator extends BaseValidator * * Required options: * - * - min: (scalar) Minimum border - * - max: (scalar) Maximum border + * - min: (int|float) Minimum border + * - max: (int|float) Maximum border * * Optional options: * * - inclusive: (bool) Whether inclusive border values, default true * - * @param array $options + * @param array{min: int|float, max: int|float, inclusive?: bool} $options * * @throws Exception When required option is missing */ @@ -58,7 +58,7 @@ class BetweenValidator extends BaseValidator /** * Return the min option * - * @return mixed + * @return int|float */ public function getMin() { @@ -68,7 +68,7 @@ class BetweenValidator extends BaseValidator /** * Set the min option * - * @param mixed $min + * @param int|float $min * * @return $this */ @@ -82,7 +82,7 @@ class BetweenValidator extends BaseValidator /** * Return the max option * - * @return mixed + * @return int|float */ public function getMax() { @@ -92,7 +92,7 @@ class BetweenValidator extends BaseValidator /** * Set the max option * - * @param mixed $max + * @param int|float $max * * @return $this */ @@ -120,14 +120,19 @@ class BetweenValidator extends BaseValidator * * @return $this */ - public function setInclusive($inclusive = true): self + public function setInclusive(bool $inclusive = true): self { - $this->inclusive = (bool) $inclusive; + $this->inclusive = $inclusive; return $this; } - public function isValid($value) + /** + * @param int|float $value + * + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/CallbackValidator.php b/vendor/ipl/validator/src/CallbackValidator.php index 611a45e..29b8e7f 100644 --- a/vendor/ipl/validator/src/CallbackValidator.php +++ b/vendor/ipl/validator/src/CallbackValidator.php @@ -35,7 +35,7 @@ class CallbackValidator extends BaseValidator $this->callback = $callback; } - public function isValid($value) + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/CidrValidator.php b/vendor/ipl/validator/src/CidrValidator.php index 32c1162..248c589 100644 --- a/vendor/ipl/validator/src/CidrValidator.php +++ b/vendor/ipl/validator/src/CidrValidator.php @@ -12,6 +12,10 @@ class CidrValidator extends BaseValidator { use Translation; + /** + * @param string $value + * @return bool + */ public function isValid($value): bool { $this->clearMessages(); diff --git a/vendor/ipl/validator/src/DateTimeValidator.php b/vendor/ipl/validator/src/DateTimeValidator.php index 1e35d61..602724b 100644 --- a/vendor/ipl/validator/src/DateTimeValidator.php +++ b/vendor/ipl/validator/src/DateTimeValidator.php @@ -23,9 +23,9 @@ class DateTimeValidator extends BaseValidator * * @param bool $local */ - public function __construct($local = true) + public function __construct(bool $local = true) { - $this->local = (bool) $local; + $this->local = $local; } /** @@ -35,7 +35,7 @@ class DateTimeValidator extends BaseValidator * * @return bool */ - public function isValid($value) + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/DeferredInArrayValidator.php b/vendor/ipl/validator/src/DeferredInArrayValidator.php index 55b9b83..fa0ba5d 100644 --- a/vendor/ipl/validator/src/DeferredInArrayValidator.php +++ b/vendor/ipl/validator/src/DeferredInArrayValidator.php @@ -15,7 +15,7 @@ class DeferredInArrayValidator extends InArrayValidator * * **Required parameter:** * - * - `callback`: (`callable`) The callback to create haystack + * - `callback`: (`callable`) The callback to create the haystack * * **Optional parameter:** * @@ -23,8 +23,8 @@ class DeferredInArrayValidator extends InArrayValidator * * * `strict`: (`bool`) Whether the types of the needle in the haystack should also match, default `false` * - * @param callable $callback Validation callback - * @param array $options + * @param callable $callback The callback to create the haystack + * @param array{haystack?: mixed[], strict?: bool} $options */ public function __construct(callable $callback, array $options = []) { diff --git a/vendor/ipl/validator/src/EmailAddressValidator.php b/vendor/ipl/validator/src/EmailAddressValidator.php index 52c3697..e9dc2a7 100644 --- a/vendor/ipl/validator/src/EmailAddressValidator.php +++ b/vendor/ipl/validator/src/EmailAddressValidator.php @@ -40,7 +40,7 @@ class EmailAddressValidator extends BaseValidator * 'mx' => If an MX check should be enabled, boolean * 'deep' => If a deep MX check should be enabled, boolean * - * @param array $options + * @param array{max?: bool, deep?: bool} $options * * @throws Exception */ @@ -266,7 +266,8 @@ class EmailAddressValidator extends BaseValidator //decode IDN domain name $decodedHostname = idn_to_ascii($hostname, 0, INTL_IDNA_VARIANT_UTS46); - $result = getmxrr($decodedHostname, $mxHosts); + $result = $decodedHostname && getmxrr($decodedHostname, $mxHosts); + if (! $result) { $this->addMessage(sprintf( $this->translate("'%s' does not appear to have a valid MX record for the email address '%s'"), diff --git a/vendor/ipl/validator/src/FileValidator.php b/vendor/ipl/validator/src/FileValidator.php index 8c5b90e..bc435cc 100644 --- a/vendor/ipl/validator/src/FileValidator.php +++ b/vendor/ipl/validator/src/FileValidator.php @@ -34,6 +34,8 @@ class FileValidator extends BaseValidator * - maxSize: (int) Maximum allowed file size, by default no limit * - maxFileNameLength: (int) Maximum allowed file name length, by default no limit * - mimeType: (array) Allowed mime types, by default no restriction + * + * @param array{minSize?: int, maxSize?: int, maxFileNameLength?: int, mimeType?: string[]} $options */ public function __construct(array $options = []) { @@ -160,7 +162,11 @@ class FileValidator extends BaseValidator return $this; } - public function isValid($value) + /** + * @param UploadedFileInterface|UploadedFileInterface[] $value + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); @@ -185,7 +191,7 @@ class FileValidator extends BaseValidator if ($this->getMaxSize() && $file->getSize() > $this->getMaxSize()) { $this->addMessage(sprintf( $this->translate('File %s is bigger than the allowed maximum size of %d'), - $file->getClientFileName(), + $file->getClientFilename(), $this->getMaxSize() )); @@ -195,7 +201,7 @@ class FileValidator extends BaseValidator if ($this->getMinSize() && $file->getSize() < $this->getMinSize()) { $this->addMessage(sprintf( $this->translate('File %s is smaller than the minimum required size of %d'), - $file->getClientFileName(), + $file->getClientFilename(), $this->getMinSize() )); @@ -205,7 +211,7 @@ class FileValidator extends BaseValidator if ($this->getMaxFileNameLength()) { $strValidator = new StringLengthValidator(['max' => $this->getMaxFileNameLength()]); - if (! $strValidator->isValid($file->getClientFilename())) { + if (! $strValidator->isValid($file->getClientFilename() ?? '')) { $this->addMessage(sprintf( $this->translate('File name is longer than the allowed length of %d characters.'), $this->maxFileNameLength @@ -217,26 +223,29 @@ class FileValidator extends BaseValidator if (! empty($this->getAllowedMimeTypes())) { $hasAllowedMimeType = false; - foreach ($this->getAllowedMimeTypes() as $type) { - $fileMimetype = $file->getClientMediaType(); - if (($pos = strpos($type, '/*')) !== false) { // image/* - $typePrefix = substr($type, 0, $pos); - if (Str::startsWith($fileMimetype, $typePrefix)) { + $fileMimetype = $file->getClientMediaType(); + + if ($fileMimetype) { + foreach ($this->getAllowedMimeTypes() as $type) { + if (($pos = strpos($type, '/*')) !== false) { // image/* + $typePrefix = substr($type, 0, $pos); + if (Str::startsWith($fileMimetype, $typePrefix)) { + $hasAllowedMimeType = true; + break; + } + } elseif ($fileMimetype === $type) { // image/png $hasAllowedMimeType = true; break; } - } elseif ($fileMimetype === $type) { // image/png - $hasAllowedMimeType = true; - break; } } if (! $hasAllowedMimeType) { $this->addMessage(sprintf( $this->translate('File %s is of type %s. Only %s allowed.'), - $file->getClientFileName(), + $file->getClientFilename(), $file->getClientMediaType(), - implode(', ', $this->allowedMimeTypes) + implode(', ', $this->allowedMimeTypes ?? []) )); $isValid = false; diff --git a/vendor/ipl/validator/src/GreaterThanValidator.php b/vendor/ipl/validator/src/GreaterThanValidator.php index e5de3d0..177fa42 100644 --- a/vendor/ipl/validator/src/GreaterThanValidator.php +++ b/vendor/ipl/validator/src/GreaterThanValidator.php @@ -11,14 +11,16 @@ class GreaterThanValidator extends BaseValidator { use Translation; - /** @var mixed Comparison value for greater than */ + /** @var int|float Comparison value for greater than */ protected $min; /** * Create a new GreaterThanValidator * * Optional options: - * - min: (scalar) Comparison value for greater than, default 0 + * - min: (int|float) Comparison value for greater than, default 0 + * + * @param array{min?: int|float} $options */ public function __construct(array $options = []) { @@ -28,7 +30,7 @@ class GreaterThanValidator extends BaseValidator /** * Get the min option * - * @return mixed + * @return int|float */ public function getMin() { @@ -38,7 +40,7 @@ class GreaterThanValidator extends BaseValidator /** * Set the min option * - * @param mixed $min + * @param int|float $min * * @return $this */ @@ -49,7 +51,12 @@ class GreaterThanValidator extends BaseValidator return $this; } - public function isValid($value) + /** + * @param int|float $value + * + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/HostnameValidator.php b/vendor/ipl/validator/src/HostnameValidator.php index 3bb9b66..8c658d0 100644 --- a/vendor/ipl/validator/src/HostnameValidator.php +++ b/vendor/ipl/validator/src/HostnameValidator.php @@ -18,7 +18,7 @@ class HostnameValidator extends BaseValidator * * @return boolean */ - public function isValid($value) + public function isValid($value): bool { $this->clearMessages(); @@ -26,7 +26,7 @@ class HostnameValidator extends BaseValidator if (filter_var($asciiHostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) { $this->addMessage(sprintf( $this->translate("%s is not a valid host name."), - $value ?? '' + $value )); return false; diff --git a/vendor/ipl/validator/src/InArrayValidator.php b/vendor/ipl/validator/src/InArrayValidator.php index f8c18ef..f3e67e7 100644 --- a/vendor/ipl/validator/src/InArrayValidator.php +++ b/vendor/ipl/validator/src/InArrayValidator.php @@ -11,7 +11,7 @@ class InArrayValidator extends BaseValidator { use Translation; - /** @var array The array */ + /** @var ?mixed[] The array */ protected $haystack; /** @var bool Whether the types of the needle in the haystack should also match */ @@ -25,7 +25,7 @@ class InArrayValidator extends BaseValidator * * `haystack`: (`array`) The array * * `strict`: (`bool`) Whether the types of the needle in the haystack should also match, default `false` * - * @param array $options + * @param array{haystack?: mixed[], strict?: bool} $options */ public function __construct(array $options = []) { @@ -39,7 +39,7 @@ class InArrayValidator extends BaseValidator /** * Get the haystack * - * @return array + * @return mixed[] */ public function getHaystack(): array { @@ -49,7 +49,7 @@ class InArrayValidator extends BaseValidator /** * Set the haystack * - * @param array $haystack + * @param mixed[] $haystack * * @return $this */ @@ -84,7 +84,7 @@ class InArrayValidator extends BaseValidator return $this; } - public function isValid($value) + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); @@ -110,9 +110,9 @@ class InArrayValidator extends BaseValidator /** * Get the values from the specified array that are not present in the haystack * - * @param array $values + * @param mixed[] $values * - * @return array Values not found in the haystack + * @return mixed[] Values not found in the haystack */ protected function findInvalid(array $values = []): array { diff --git a/vendor/ipl/validator/src/LessThanValidator.php b/vendor/ipl/validator/src/LessThanValidator.php index 68e3daf..db80e1a 100644 --- a/vendor/ipl/validator/src/LessThanValidator.php +++ b/vendor/ipl/validator/src/LessThanValidator.php @@ -11,14 +11,16 @@ class LessThanValidator extends BaseValidator { use Translation; - /** @var mixed Comparison value for less than */ + /** @var int|float Comparison value for less than */ protected $max; /** * Create a new LessThanValidator * * Optional options: - * - max: (int) Comparison value for less than, default 0 + * - max: (int|float) Comparison value for less than, default 0 + * + * @param array{max?: int|float} $options */ public function __construct(array $options = []) { @@ -28,7 +30,7 @@ class LessThanValidator extends BaseValidator /** * Get the max option * - * @return mixed + * @return int|float */ public function getMax() { @@ -38,7 +40,7 @@ class LessThanValidator extends BaseValidator /** * Set the max option * - * @param mixed $max + * @param int|float $max * * @return $this */ @@ -49,7 +51,12 @@ class LessThanValidator extends BaseValidator return $this; } - public function isValid($value) + /** + * @param int|float $value + * + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/PrivateKeyValidator.php b/vendor/ipl/validator/src/PrivateKeyValidator.php index b629398..a207170 100644 --- a/vendor/ipl/validator/src/PrivateKeyValidator.php +++ b/vendor/ipl/validator/src/PrivateKeyValidator.php @@ -11,7 +11,12 @@ class PrivateKeyValidator extends BaseValidator { use Translation; - public function isValid($value) + /** + * @param string $value + * + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/StringLengthValidator.php b/vendor/ipl/validator/src/StringLengthValidator.php index 57df1eb..d4dd44b 100644 --- a/vendor/ipl/validator/src/StringLengthValidator.php +++ b/vendor/ipl/validator/src/StringLengthValidator.php @@ -13,10 +13,10 @@ class StringLengthValidator extends BaseValidator { use Translation; - /** @var mixed Minimum required length */ + /** @var int Minimum required length */ protected $min; - /** @var mixed Maximum required length */ + /** @var ?int Maximum required length */ protected $max; /** @var ?string Encoding to use */ @@ -26,9 +26,11 @@ class StringLengthValidator extends BaseValidator * Create a new StringLengthValidator * * Optional options: - * - min: (scalar) Minimum required string length, default 0 - * - max: (scalar) Maximum required string length, default null - * - encoding: (string) Encoding type, default null + * - min: (int) Minimum required string length, default 0 + * - max: (int) Maximum required string length, default none + * - encoding: (string) Encoding type, default none + * + * @param array{min?: int, max?: int, encoding?: string} $options */ public function __construct(array $options = []) { @@ -41,9 +43,9 @@ class StringLengthValidator extends BaseValidator /** * Get the minimum required string length * - * @return mixed + * @return int */ - public function getMin() + public function getMin(): int { return $this->min; } @@ -51,13 +53,13 @@ class StringLengthValidator extends BaseValidator /** * Set the minimum required string length * - * @param mixed $min + * @param int $min * * @return $this * * @throws LogicException When the $min is greater than the $max value */ - public function setMin($min): self + public function setMin(int $min): self { if ($this->getMax() !== null && $min > $this->getMax()) { throw new LogicException( @@ -77,9 +79,9 @@ class StringLengthValidator extends BaseValidator /** * Get the maximum required string length * - * @return mixed + * @return ?int */ - public function getMax() + public function getMax(): ?int { return $this->max; } @@ -87,13 +89,13 @@ class StringLengthValidator extends BaseValidator /** * Set the minimum required string length * - * @param mixed $max + * @param ?int $max * * @return $this * * @throws LogicException When the $min is greater than the $max value */ - public function setMax($max): self + public function setMax(?int $max): self { if ($max !== null && $this->getMin() > $max) { throw new LogicException( @@ -143,7 +145,12 @@ class StringLengthValidator extends BaseValidator return $this; } - public function isValid($value) + /** + * @param string $value + * + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/validator/src/ValidatorChain.php b/vendor/ipl/validator/src/ValidatorChain.php index 2860a12..8f0b9f3 100644 --- a/vendor/ipl/validator/src/ValidatorChain.php +++ b/vendor/ipl/validator/src/ValidatorChain.php @@ -15,6 +15,7 @@ use UnexpectedValueException; use function ipl\Stdlib\get_php_type; +/** @implements IteratorAggregate */ class ValidatorChain implements Countable, IteratorAggregate, Validator { use Messages; @@ -23,10 +24,10 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator /** Default priority at which validators are added */ const DEFAULT_PRIORITY = 1; - /** @var PriorityQueue Validator chain */ + /** @var PriorityQueue Validator chain */ protected $validators; - /** @var SplObjectStorage Validators that break the chain on failure */ + /** @var SplObjectStorage Validators that break the chain on failure */ protected $validatorsThatBreakTheChain; /** @@ -43,7 +44,7 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator /** * Get the validators that break the chain * - * @return SplObjectStorage + * @return SplObjectStorage */ public function getValidatorsThatBreakTheChain() { @@ -76,7 +77,7 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator /** * Add the validators from the given validator specification to the chain * - * @param iterable $validators + * @param static|Traversable $validators * * @return $this * @@ -149,7 +150,7 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator * * @return $this */ - public function addValidatorLoader($namespace, $postfix = null) + public function addValidatorLoader($namespace, $postfix = '') { $this->addPluginLoader('validator', $namespace, $postfix); @@ -221,6 +222,10 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator { $validatorsThatBreakTheChain = $validatorChain->getValidatorsThatBreakTheChain(); + /** + * @var int $priority + * @var Validator $validator + */ foreach ($validatorChain->validators->yieldAll() as $priority => $validator) { $this->add($validator, $validatorsThatBreakTheChain->contains($validator), $priority); } @@ -236,11 +241,14 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator /** * Export the chain as array * - * @return array + * @return Validator[] */ public function toArray() { - return array_values(iterator_to_array($this)); + /** @var Validator[] $validators */ + $validators = iterator_to_array($this); + + return array_values($validators); } public function count(): int @@ -251,7 +259,7 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator /** * Get an iterator for traversing the validators * - * @return Validator[]|PriorityQueue + * @return PriorityQueue */ public function getIterator(): Traversable { @@ -259,7 +267,7 @@ class ValidatorChain implements Countable, IteratorAggregate, Validator return clone $this->validators; } - public function isValid($value) + public function isValid($value): bool { $this->clearMessages(); diff --git a/vendor/ipl/validator/src/X509CertValidator.php b/vendor/ipl/validator/src/X509CertValidator.php index 7dfc4f7..0fc9677 100644 --- a/vendor/ipl/validator/src/X509CertValidator.php +++ b/vendor/ipl/validator/src/X509CertValidator.php @@ -11,7 +11,11 @@ class X509CertValidator extends BaseValidator { use Translation; - public function isValid($value) + /** + * @param String $value + * @return bool + */ + public function isValid($value): bool { // Multiple isValid() calls must not stack validation messages $this->clearMessages(); diff --git a/vendor/ipl/web/composer.json b/vendor/ipl/web/composer.json index 60d9456..1534baa 100644 --- a/vendor/ipl/web/composer.json +++ b/vendor/ipl/web/composer.json @@ -20,7 +20,7 @@ "php": ">=8.2", "ext-json": "*", "psr/http-message": "^1.1", - "ipl/html": ">=0.8.0", + "ipl/html": ">=0.9.0", "ipl/i18n": ">=0.2.0", "ipl/orm": ">=0.5.2", "ipl/scheduler": ">=0.1.0", diff --git a/vendor/ipl/web/src/Common/CsrfCounterMeasure.php b/vendor/ipl/web/src/Common/CsrfCounterMeasure.php index ae2dace..5ad58df 100644 --- a/vendor/ipl/web/src/Common/CsrfCounterMeasure.php +++ b/vendor/ipl/web/src/Common/CsrfCounterMeasure.php @@ -8,12 +8,44 @@ use ipl\Html\FormElement\HiddenElement; trait CsrfCounterMeasure { + /** @var ?string The ID of the CSRF form element */ + private ?string $csrfCounterMeasureId = null; + + /** @var bool Whether to actually add the CSRF element to the form */ + private bool $csrfCounterMeasureEnabled = true; + + /** + * Set the ID for the CSRF form element + * + * @param string $id A unique ID that persists through different requests + * + * @return $this + */ + public function setCsrfCounterMeasureId(string $id): static + { + $this->csrfCounterMeasureId = $id; + + return $this; + } + + /** + * Disable the CSRF form element + * + * @return void + */ + public function disableCsrfCounterMeasure(): void + { + $this->csrfCounterMeasureEnabled = false; + } + /** * Create a form element to countermeasure CSRF attacks * * @param string $uniqueId A unique ID that persists through different requests * * @return FormElement + * + * @deprecated Use {@see addCsrfCounterMeasure()} instead */ protected function createCsrfCounterMeasure($uniqueId) { @@ -53,4 +85,27 @@ trait CsrfCounterMeasure return $element; } + + /** + * Add the CSRF form element to this form + * + * Does nothing if disabled via {@see disableCsrfCounterMeasure()}. + * Unless passed as argument, requires a unique ID to be set via {@see setCsrfCounterMeasureId()}. + * + * @param ?string $uniqueId A unique ID that persists through different requests + * + * @return void + */ + protected function addCsrfCounterMeasure(?string $uniqueId = null): void + { + if (! $this->csrfCounterMeasureEnabled) { + return; + } + + if ($uniqueId === null && $this->csrfCounterMeasureId === null) { + throw new Error('No CSRF counter measure ID set'); + } + + $this->addElement($this->createCsrfCounterMeasure($uniqueId ?? $this->csrfCounterMeasureId)); + } } diff --git a/vendor/ipl/web/src/Control/SearchBar/Suggestions.php b/vendor/ipl/web/src/Control/SearchBar/Suggestions.php index 82420b1..cf5ff7b 100644 --- a/vendor/ipl/web/src/Control/SearchBar/Suggestions.php +++ b/vendor/ipl/web/src/Control/SearchBar/Suggestions.php @@ -125,7 +125,7 @@ abstract class Suggestions extends BaseHtmlElement $logicalSep = [ 'label' => QueryString::getRuleSymbol($filter), 'search' => QueryString::getRuleSymbol($filter), - 'class' => 'logical_operator', + 'class' => 'logical-operator', 'type' => 'logical_operator' ]; @@ -136,14 +136,14 @@ abstract class Suggestions extends BaseHtmlElement 'search' => '(', 'label' => '(', 'type' => 'grouping_operator', - 'class' => 'grouping_operator_open' + 'class' => 'grouping-operator-open' ]; $terms = array_merge($terms, $this->filterToTerms($child)); $terms[] = [ 'search' => ')', 'label' => ')', 'type' => 'grouping_operator', - 'class' => 'grouping_operator_close' + 'class' => 'grouping-operator-close' ]; } else { /** @var Filter\Condition $child */ diff --git a/vendor/ipl/web/src/Control/SearchBar/Terms.php b/vendor/ipl/web/src/Control/SearchBar/Terms.php index c81e336..c7a9ce8 100644 --- a/vendor/ipl/web/src/Control/SearchBar/Terms.php +++ b/vendor/ipl/web/src/Control/SearchBar/Terms.php @@ -81,7 +81,7 @@ class Terms extends BaseHtmlElement if ($i > 0) { $logicalOperator = QueryString::getRuleSymbol($filters); $this->assembleTerm([ - 'class' => 'logical_operator', + 'class' => 'logical-operator', 'type' => 'logical_operator', 'search' => $logicalOperator, 'label' => $logicalOperator @@ -110,7 +110,7 @@ class Terms extends BaseHtmlElement if ($chain instanceof Filter\None) { $this->assembleTerm([ - 'class' => 'logical_operator', + 'class' => 'logical-operator', 'type' => 'negation_operator', 'search' => '!', 'label' => '!' @@ -119,7 +119,7 @@ class Terms extends BaseHtmlElement if ($wrap) { $opening = $this->assembleTerm([ - 'class' => 'grouping_operator_open', + 'class' => 'grouping-operator-open', 'type' => 'grouping_operator', 'search' => '(', 'label' => '(' @@ -130,7 +130,7 @@ class Terms extends BaseHtmlElement if ($wrap) { $closing = $this->assembleTerm([ - 'class' => 'grouping_operator_close', + 'class' => 'grouping-operator-close', 'type' => 'grouping_operator', 'search' => ')', 'label' => ')' diff --git a/vendor/ipl/web/src/Control/SortControl.php b/vendor/ipl/web/src/Control/SortControl.php index 361899a..b5cabd8 100644 --- a/vendor/ipl/web/src/Control/SortControl.php +++ b/vendor/ipl/web/src/Control/SortControl.php @@ -68,11 +68,9 @@ class SortControl extends Form $self = new static($normalized); $self->on(self::ON_REQUEST, function (ServerRequestInterface $request) use ($self) { - if (! $self->hasBeenSent()) { - // If the form is submitted by POST, handleRequest() won't access the URL, so we have to - if (($sort = $request->getQueryParams()[$self->getSortParam()] ?? null)) { - $self->populate([$self->getSortParam() => $sort]); - } + // If the form is submitted by POST, handleRequest() won't access the URL, so we have to + if (($sort = $request->getQueryParams()[$self->getSortParam()] ?? null)) { + $self->populate([$self->getSortParam() => $sort]); } }); diff --git a/vendor/ipl/web/src/FormElement/SearchSuggestions.php b/vendor/ipl/web/src/FormElement/SearchSuggestions.php new file mode 100644 index 0000000..0218d43 --- /dev/null +++ b/vendor/ipl/web/src/FormElement/SearchSuggestions.php @@ -0,0 +1,279 @@ +provider = $provider; + } + + /** + * Set a callback to identify groups for terms delivered by the provider + * + * The callback must return a string which is used as label for the group. + * Its interface is: `function (array $data): string` + * + * @param callable $callback + * + * @return $this + */ + public function setGroupingCallback(callable $callback): self + { + $this->groupingCallback = $callback; + + return $this; + } + + /** + * Get the callback used to identify groups for terms delivered by the provider + * + * @return ?callable + */ + public function getGroupingCallback(): ?callable + { + return $this->groupingCallback; + } + + /** + * Set the search term (can contain `*` wildcards) + * + * @param string $term + * + * @return $this + */ + public function setSearchTerm(string $term): self + { + $this->searchTerm = $term; + $this->setSearchPattern( + '/' . str_replace( + '\\000', + '.*', + preg_quote( + str_replace( + '*', + "\0", + $term + ), + '/' + ) + ) . '/i' + ); + + return $this; + } + + /** + * Get the search term + * + * @return ?string + */ + public function getSearchTerm(): ?string + { + return $this->searchTerm; + } + + /** + * Set the search pattern used by {@see matchSearch} + * + * @param string $pattern + * + * @return $this + */ + protected function setSearchPattern(string $pattern): self + { + $this->searchPattern = $pattern; + + return $this; + } + + /** + * Set the original search value + * + * The one without automatically added wildcards. + * + * @param string $term + * + * @return $this + */ + public function setOriginalSearchValue(string $term): self + { + $this->originalValue = $term; + + return $this; + } + + /** + * Get the original search value + * + * @return ?string + */ + public function getOriginalSearchValue(): ?string + { + return $this->originalValue; + } + + /** + * Set the terms to exclude in the suggestion list + * + * @param string[] $terms + * + * @return $this + */ + public function setExcludeTerms(array $terms): self + { + $this->excludeTerms = $terms; + + return $this; + } + + /** + * Get the terms to exclude in the suggestion list + * + * @return string[] + */ + public function getExcludeTerms(): array + { + return $this->excludeTerms; + } + + /** + * Match the given search term against the users search + * + * @param string $term + * + * @return bool Whether the search matches or not + */ + public function matchSearch(string $term): bool + { + if (! $this->searchPattern || $this->searchPattern === '.*') { + return true; + } + + return (bool) preg_match($this->searchPattern, $term); + } + + /** + * Load suggestions as requested by the client + * + * @param ServerRequestInterface $request + * + * @return $this + */ + public function forRequest(ServerRequestInterface $request): self + { + if ($request->getMethod() !== 'POST') { + return $this; + } + + /** @var array> $requestData */ + $requestData = json_decode($request->getBody()->read(8192), true); + if (empty($requestData)) { + return $this; + } + + $this->setSearchTerm($requestData['term']['label']); + $this->setOriginalSearchValue($requestData['term']['search']); + $this->setExcludeTerms($requestData['exclude'] ?? []); + + return $this; + } + + protected function assemble(): void + { + $groupingCallback = $this->getGroupingCallback(); + if ($groupingCallback) { + $provider = yield_groups($this->provider, $groupingCallback); + } else { + $provider = [null => $this->provider]; + } + + /** @var iterable>> $provider */ + foreach ($provider as $group => $suggestions) { + if ($group) { + $this->addHtml( + new HtmlElement( + 'li', + Attributes::create(['class' => 'suggestion-title']), + Text::create($group) + ) + ); + } + + foreach ($suggestions as $data) { + $attributes = [ + 'type' => 'button', + 'value' => $data['label'] ?? $data['search'] + ]; + foreach ($data as $name => $value) { + $attributes["data-$name"] = $value; + } + + $this->addHtml( + new HtmlElement( + 'li', + null, + new HtmlElement( + 'input', + Attributes::create($attributes) + ) + ) + ); + } + } + + if ($this->isEmpty()) { + $this->addHtml(new HtmlElement( + 'li', + Attributes::create(['class' => 'nothing-to-suggest']), + new HtmlElement('em', null, Text::create($this->translate('Nothing to suggest'))) + )); + } + } +} diff --git a/vendor/ipl/web/src/FormElement/SuggestionElement.php b/vendor/ipl/web/src/FormElement/SuggestionElement.php new file mode 100644 index 0000000..83f3d45 --- /dev/null +++ b/vendor/ipl/web/src/FormElement/SuggestionElement.php @@ -0,0 +1,87 @@ + 'off', + 'class' => 'suggestion-element', + 'data-enrichment-type' => 'completion' + ]; + + /** @var Url URL to fetch suggestions from */ + protected Url $suggestionsUrl; + + /** + * Create a new SuggestionElement + * + * @param string $name Name of the form element + * @param Url $suggestionsUrl URL to fetch suggestions from + * @param ?(array|Attributes) $attributes Attributes of the form element + */ + public function __construct(string $name, Url $suggestionsUrl, array|Attributes $attributes = null) + { + parent::__construct($name, $attributes); + + $this->setSuggestionsUrl($suggestionsUrl); + } + + /** + * Get the URL to fetch suggestions from + * + * @return Url + */ + public function getSuggestionsUrl(): Url + { + return $this->suggestionsUrl; + } + + /** + * Set the URL to fetch suggestions from + * + * @param Url $suggestionsUrl + * + * @return $this + */ + public function setSuggestionsUrl(Url $suggestionsUrl): static + { + $this->suggestionsUrl = $suggestionsUrl; + + return $this; + } + + /** + * @return string If not set, returns a default placeholder + */ + public function getPlaceholder(): string + { + return $this->placeholder ?? $this->translate('Start typing to see suggestions…'); + } + + protected function assemble(): void + { + $suggestionsId = uniqid('search-suggestions-'); + + $this->prependWrapper( + new HtmlElement( + 'div', + new Attributes(['class' => 'suggestion-element-group']), + new HtmlElement('div', new Attributes(['id' => $suggestionsId, 'class' => 'search-suggestions'])), + new HtmlElement('span', new Attributes(['class' => 'suggestion-element-icon']), new Icon('search')) + ) + ); + + $this->getAttributes()->add([ + 'data-term-suggestions' => '#' . $suggestionsId, + 'data-suggest-url' => $this->getSuggestionsUrl() + ]); + } +} diff --git a/vendor/ipl/web/src/FormElement/TermInput/TermSuggestions.php b/vendor/ipl/web/src/FormElement/TermInput/TermSuggestions.php index 26b00ea..9a94b1c 100644 --- a/vendor/ipl/web/src/FormElement/TermInput/TermSuggestions.php +++ b/vendor/ipl/web/src/FormElement/TermInput/TermSuggestions.php @@ -2,280 +2,11 @@ namespace ipl\Web\FormElement\TermInput; -use ipl\Html\Attributes; -use ipl\Html\BaseHtmlElement; -use ipl\Html\HtmlElement; -use ipl\Html\Text; -use ipl\I18n\Translation; -use Psr\Http\Message\ServerRequestInterface; -use Traversable; +use ipl\Web\FormElement\SearchSuggestions; -use function ipl\Stdlib\yield_groups; - -class TermSuggestions extends BaseHtmlElement +/** + * @deprecated Use {@see SearchSuggestions} instead + */ +class TermSuggestions extends SearchSuggestions { - use Translation; - - protected $tag = 'ul'; - - /** @var Traversable */ - protected $provider; - - /** @var ?callable */ - protected $groupingCallback; - - /** @var ?string */ - protected $searchTerm; - - /** @var ?string */ - protected $searchPattern; - - /** @var ?string */ - protected $originalValue; - - /** @var string[] */ - protected $excludeTerms = []; - - /** - * Create new TermSuggestions - * - * The provider must deliver terms in form of arrays with the following keys: - * * (required) search: The search value - * * label: A human-readable label - * * class: A CSS class - * * title: A message shown upon hover on the term - * - * Any excess key is also transferred to the client, but currently unused. - * - * @param Traversable $provider - */ - public function __construct(Traversable $provider) - { - $this->provider = $provider; - } - - /** - * Set a callback to identify groups for terms delivered by the provider - * - * The callback must return a string which is used as label for the group. - * Its interface is: `function (array $data): string` - * - * @param callable $callback - * - * @return $this - */ - public function setGroupingCallback(callable $callback): self - { - $this->groupingCallback = $callback; - - return $this; - } - - /** - * Get the callback used to identify groups for terms delivered by the provider - * - * @return ?callable - */ - public function getGroupingCallback(): ?callable - { - return $this->groupingCallback; - } - - /** - * Set the search term (can contain `*` wildcards) - * - * @param string $term - * - * @return $this - */ - public function setSearchTerm(string $term): self - { - $this->searchTerm = $term; - $this->setSearchPattern( - '/' . str_replace( - '\\000', - '.*', - preg_quote( - str_replace( - '*', - "\0", - $term - ), - '/' - ) - ) . '/i' - ); - - return $this; - } - - /** - * Get the search term - * - * @return ?string - */ - public function getSearchTerm(): ?string - { - return $this->searchTerm; - } - - /** - * Set the search pattern used by {@see matchSearch} - * - * @param string $pattern - * - * @return $this - */ - protected function setSearchPattern(string $pattern): self - { - $this->searchPattern = $pattern; - - return $this; - } - - /** - * Set the original search value - * - * The one without automatically added wildcards. - * - * @param string $term - * - * @return $this - */ - public function setOriginalSearchValue(string $term): self - { - $this->originalValue = $term; - - return $this; - } - - /** - * Get the original search value - * - * @return ?string - */ - public function getOriginalSearchValue(): ?string - { - return $this->originalValue; - } - - /** - * Set the terms to exclude in the suggestion list - * - * @param string[] $terms - * - * @return $this - */ - public function setExcludeTerms(array $terms): self - { - $this->excludeTerms = $terms; - - return $this; - } - - /** - * Get the terms to exclude in the suggestion list - * - * @return string[] - */ - public function getExcludeTerms(): array - { - return $this->excludeTerms; - } - - /** - * Match the given search term against the users search - * - * @param string $term - * - * @return bool Whether the search matches or not - */ - public function matchSearch(string $term): bool - { - if (! $this->searchPattern || $this->searchPattern === '.*') { - return true; - } - - return (bool) preg_match($this->searchPattern, $term); - } - - /** - * Load suggestions as requested by the client - * - * @param ServerRequestInterface $request - * - * @return $this - */ - public function forRequest(ServerRequestInterface $request): self - { - if ($request->getMethod() !== 'POST') { - return $this; - } - - /** @var array> $requestData */ - $requestData = json_decode($request->getBody()->read(8192), true); - if (empty($requestData)) { - return $this; - } - - $this->setSearchTerm($requestData['term']['label']); - $this->setOriginalSearchValue($requestData['term']['search']); - $this->setExcludeTerms($requestData['exclude'] ?? []); - - return $this; - } - - protected function assemble() - { - $groupingCallback = $this->getGroupingCallback(); - if ($groupingCallback) { - $provider = yield_groups($this->provider, $groupingCallback); - } else { - $provider = [null => $this->provider]; - } - - /** @var iterable>> $provider */ - foreach ($provider as $group => $suggestions) { - if ($group) { - $this->addHtml( - new HtmlElement( - 'li', - Attributes::create([ - 'class' => 'suggestion-title' - ]), - Text::create($group) - ) - ); - } - - foreach ($suggestions as $data) { - $attributes = [ - 'type' => 'button', - 'value' => $data['label'] ?? $data['search'] - ]; - foreach ($data as $name => $value) { - $attributes["data-$name"] = $value; - } - - $this->addHtml( - new HtmlElement( - 'li', - null, - new HtmlElement( - 'input', - Attributes::create($attributes) - ) - ) - ); - } - } - - if ($this->isEmpty()) { - $this->addHtml(new HtmlElement( - 'li', - Attributes::create(['class' => 'nothing-to-suggest']), - new HtmlElement('em', null, Text::create($this->translate('Nothing to suggest'))) - )); - } - } } diff --git a/vendor/ipl/web/src/Layout/Controls.php b/vendor/ipl/web/src/Layout/Controls.php index 8763775..9b52613 100644 --- a/vendor/ipl/web/src/Layout/Controls.php +++ b/vendor/ipl/web/src/Layout/Controls.php @@ -43,7 +43,7 @@ class Controls extends BaseHtmlElement return $this; } - public function isEmpty() + public function isEmpty(): bool { if (! parent::isEmpty()) { return false; diff --git a/vendor/ipl/web/src/Widget/Ball.php b/vendor/ipl/web/src/Widget/Ball.php index 77b9eba..61b7284 100644 --- a/vendor/ipl/web/src/Widget/Ball.php +++ b/vendor/ipl/web/src/Widget/Ball.php @@ -91,7 +91,7 @@ class Ball extends BaseHtmlElement * * @return $this */ - public function addHtml(ValidHtml ...$content): Ball + public function addHtml(ValidHtml ...$content): static { if (count($content) === 1 && $content[0] instanceof Text) { $content[0] = new HtmlElement('span', null, $content[0]); diff --git a/vendor/ipl/web/src/Widget/ButtonLink.php b/vendor/ipl/web/src/Widget/ButtonLink.php index 2da5dfd..b9cd1fa 100644 --- a/vendor/ipl/web/src/Widget/ButtonLink.php +++ b/vendor/ipl/web/src/Widget/ButtonLink.php @@ -2,13 +2,54 @@ namespace ipl\Web\Widget; +use ipl\Html\Attribute; + /** * Button like link generally pointing to CRUD actions */ class ButtonLink extends ActionLink { protected $defaultAttributes = [ - 'class' => 'button-link', - 'data-base-target' => '_main' + 'role' => 'button', + 'class' => 'button-link', + 'data-base-target' => '_main' ]; + + /** @var ?string The explanation why the button is disabled */ + protected ?string $disabledExplanation = null; + + /** + * Disable the button with explanation + * + * @param string $explanation Why the button is disabled + * + * @return $this + */ + public function disable(string $explanation): self + { + $this->disabledExplanation = $explanation; + + return $this; + } + + public function createHrefAttribute(): ?Attribute + { + if ($this->disabledExplanation) { + return null; + } + + return parent::createHrefAttribute(); + } + + public function assemble(): void + { + parent::assemble(); + + if ($this->disabledExplanation) { + $this->addAttributes([ + 'aria-disabled' => 'true', + 'title' => $this->disabledExplanation, + ]); + } + } } diff --git a/vendor/ramsey/uuid/composer.json b/vendor/ramsey/uuid/composer.json index db44292..b34d9b1 100644 --- a/vendor/ramsey/uuid/composer.json +++ b/vendor/ramsey/uuid/composer.json @@ -10,7 +10,7 @@ ], "require": { "php": "^8.0", - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "ramsey/collection": "^1.2 || ^2.0" }, "require-dev": { diff --git a/vendor/ramsey/uuid/src/Generator/UnixTimeGenerator.php b/vendor/ramsey/uuid/src/Generator/UnixTimeGenerator.php index 024ed51..a2615f1 100644 --- a/vendor/ramsey/uuid/src/Generator/UnixTimeGenerator.php +++ b/vendor/ramsey/uuid/src/Generator/UnixTimeGenerator.php @@ -15,7 +15,6 @@ declare(strict_types=1); namespace Ramsey\Uuid\Generator; use Brick\Math\BigInteger; -use DateTimeImmutable; use DateTimeInterface; use Ramsey\Uuid\Type\Hexadecimal; @@ -68,7 +67,12 @@ class UnixTimeGenerator implements TimeGeneratorInterface */ public function generate($node = null, ?int $clockSeq = null, ?DateTimeInterface $dateTime = null): string { - $time = ($dateTime ?? new DateTimeImmutable('now'))->format('Uv'); + if ($dateTime === null) { + $time = microtime(false); + $time = substr($time, 11) . substr($time, 2, 3); + } else { + $time = $dateTime->format('Uv'); + } if ($time > self::$time || ($dateTime !== null && $time !== self::$time)) { $this->randomize($time); diff --git a/vendor/ramsey/uuid/src/Math/BrickMathCalculator.php b/vendor/ramsey/uuid/src/Math/BrickMathCalculator.php index 869835e..649f580 100644 --- a/vendor/ramsey/uuid/src/Math/BrickMathCalculator.php +++ b/vendor/ramsey/uuid/src/Math/BrickMathCalculator.php @@ -49,10 +49,10 @@ final class BrickMathCalculator implements CalculatorInterface $sum = BigInteger::of($augend->toString()); foreach ($addends as $addend) { - $sum = $sum->plus($addend->toString()); /** @phpstan-ignore possiblyImpure.methodCall */ + $sum = $sum->plus($addend->toString()); } - /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.new */ + /** @phpstan-ignore possiblyImpure.new */ return new IntegerObject((string) $sum); } @@ -61,10 +61,10 @@ final class BrickMathCalculator implements CalculatorInterface $difference = BigInteger::of($minuend->toString()); foreach ($subtrahends as $subtrahend) { - $difference = $difference->minus($subtrahend->toString()); /** @phpstan-ignore possiblyImpure.methodCall */ + $difference = $difference->minus($subtrahend->toString()); } - /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.new */ + /** @phpstan-ignore possiblyImpure.new */ return new IntegerObject((string) $difference); } @@ -73,11 +73,10 @@ final class BrickMathCalculator implements CalculatorInterface $product = BigInteger::of($multiplicand->toString()); foreach ($multipliers as $multiplier) { - /** @phpstan-ignore possiblyImpure.methodCall */ $product = $product->multipliedBy($multiplier->toString()); } - /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.new */ + /** @phpstan-ignore possiblyImpure.new */ return new IntegerObject((string) $product); } @@ -93,23 +92,22 @@ final class BrickMathCalculator implements CalculatorInterface $quotient = BigDecimal::of($dividend->toString()); foreach ($divisors as $divisor) { - /** @phpstan-ignore possiblyImpure.methodCall */ $quotient = $quotient->dividedBy($divisor->toString(), $scale, $brickRounding); } if ($scale === 0) { - /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.methodCall, possiblyImpure.new */ + /** @phpstan-ignore possiblyImpure.new */ return new IntegerObject((string) $quotient->toBigInteger()); } - /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.new */ + /** @phpstan-ignore possiblyImpure.new */ return new Decimal((string) $quotient); } public function fromBase(string $value, int $base): IntegerObject { try { - /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.new */ + /** @phpstan-ignore possiblyImpure.new */ return new IntegerObject((string) BigInteger::fromBase($value, $base)); } catch (MathException | \InvalidArgumentException $exception) { throw new InvalidArgumentException( @@ -123,7 +121,6 @@ final class BrickMathCalculator implements CalculatorInterface public function toBase(IntegerObject $value, int $base): string { try { - /** @phpstan-ignore possiblyImpure.methodCall */ return BigInteger::of($value->toString())->toBase($base); } catch (MathException | \InvalidArgumentException $exception) { throw new InvalidArgumentException( diff --git a/vendor/symfony/polyfill-php84/Php84.php b/vendor/symfony/polyfill-php84/Php84.php index 17668e6..1eea63a 100644 --- a/vendor/symfony/polyfill-php84/Php84.php +++ b/vendor/symfony/polyfill-php84/Php84.php @@ -174,4 +174,44 @@ final class Php84 return mb_convert_encoding($string, $encoding, 'UTF-8'); } + + public static function grapheme_str_split(string $string, int $length) + { + if (0 > $length || 1073741823 < $length) { + throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823.'); + } + + if ('' === $string) { + return []; + } + + $regex = ((float) \PCRE_VERSION < 10 ? (float) \PCRE_VERSION >= 8.32 : (float) \PCRE_VERSION >= 10.39) + ? '\X' + : '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])'; + + if (!preg_match_all('/'. $regex .'/u', $string, $matches)) { + return false; + } + + if (1 === $length) { + return $matches[0]; + } + + $chunks = array_chunk($matches[0], $length); + foreach ($chunks as &$chunk) { + $chunk = implode('', $chunk); + } + + return $chunks; + } + + public static function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array + { + if (null === $quot = \bcdiv($num1, $num2, 0)) { + return null; + } + $scale = $scale ?? (\PHP_VERSION_ID >= 70300 ? \bcscale() : (ini_get('bcmath.scale') ?: 0)); + + return [$quot, \bcmod($num1, $num2, $scale)]; + } } diff --git a/vendor/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php b/vendor/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php new file mode 100644 index 0000000..f4c8448 --- /dev/null +++ b/vendor/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80400) { + /** + * @author Daniel Scherzer + */ + final class ReflectionConstant + { + /** + * @var string + * + * @readonly + */ + public $name; + + private $value; + private $deprecated; + + private static $persistentConstants = []; + + public function __construct(string $name) + { + if (!defined($name) || false !== strpos($name, '::')) { + throw new ReflectionException("Constant \"$name\" does not exist"); + } + + $this->name = ltrim($name, '\\'); + $deprecated = false; + $eh = set_error_handler(static function ($type, $msg, $file, $line) use ($name, &$deprecated, &$eh) { + if (\E_DEPRECATED === $type && "Constant $name is deprecated" === $msg) { + return $deprecated = true; + } + + return $eh && $eh($type, $msg, $file, $line); + }); + + try { + $this->value = constant($name); + $this->deprecated = $deprecated; + } finally { + restore_error_handler(); + } + } + + public function getName(): string + { + return $this->name; + } + + public function getValue() + { + return $this->value; + } + + public function getNamespaceName(): string + { + if (false === $slashPos = strrpos($this->name, '\\')) { + return ''; + } + + return substr($this->name, 0, $slashPos); + } + + public function getShortName(): string + { + if (false === $slashPos = strrpos($this->name, '\\')) { + return $this->name; + } + + return substr($this->name, $slashPos + 1); + } + + public function isDeprecated(): bool + { + return $this->deprecated; + } + + public function __toString(): string + { + // A constant is persistent if provided by PHP itself rather than + // being defined by users. If we got here, we know that it *is* + // defined, so we just need to figure out if it is defined by the + // user or not + if (!self::$persistentConstants) { + $persistentConstants = get_defined_constants(true); + unset($persistentConstants['user']); + foreach ($persistentConstants as $constants) { + self::$persistentConstants += $constants; + } + } + $persistent = array_key_exists($this->name, self::$persistentConstants); + + // Can't match the inclusion of `no_file_cache` but the rest is + // possible to match + $result = 'Constant [ '; + if ($persistent || $this->deprecated) { + $result .= '<'; + if ($persistent) { + $result .= 'persistent'; + if ($this->deprecated) { + $result .= ', '; + } + } + if ($this->deprecated) { + $result .= 'deprecated'; + } + $result .= '> '; + } + // Cannot just use gettype() to match zend_zval_type_name() + if (is_object($this->value)) { + $result .= \PHP_VERSION_ID >= 80000 ? get_debug_type($this->value) : gettype($this->value); + } elseif (is_bool($this->value)) { + $result .= 'bool'; + } elseif (is_int($this->value)) { + $result .= 'int'; + } elseif (is_float($this->value)) { + $result .= 'float'; + } elseif (null === $this->value) { + $result .= 'null'; + } else { + $result .= gettype($this->value); + } + $result .= ' '; + $result .= $this->name; + $result .= ' ] { '; + if (is_array($this->value)) { + $result .= 'Array'; + } else { + // This will throw an exception if the value is an object that + // cannot be converted to string; that is expected and matches + // the behavior of zval_get_string_func() + $result .= (string) $this->value; + } + $result .= " }\n"; + + return $result; + } + + public function __sleep(): array + { + throw new Exception("Serialization of 'ReflectionConstant' is not allowed"); + } + + public function __wakeup(): void + { + throw new Exception("Unserialization of 'ReflectionConstant' is not allowed"); + } + } +} diff --git a/vendor/symfony/polyfill-php84/bootstrap.php b/vendor/symfony/polyfill-php84/bootstrap.php index 2a97207..4bd1c17 100644 --- a/vendor/symfony/polyfill-php84/bootstrap.php +++ b/vendor/symfony/polyfill-php84/bootstrap.php @@ -66,3 +66,17 @@ if (extension_loaded('mbstring')) { function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_rtrim($string, $characters, $encoding); } } } + +if (extension_loaded('bcmath')) { + if (!function_exists('bcdivmod')) { + function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array { return p\Php84::bcdivmod($num1, $num2, $scale); } + } +} + +if (\PHP_VERSION_ID >= 80200) { + return require __DIR__.'/bootstrap82.php'; +} + +if (extension_loaded('intl') && !function_exists('grapheme_str_split')) { + function grapheme_str_split(string $string, int $length = 1) { return p\Php84::grapheme_str_split($string, $length); } +} diff --git a/vendor/symfony/polyfill-php84/bootstrap82.php b/vendor/symfony/polyfill-php84/bootstrap82.php new file mode 100644 index 0000000..216ad02 --- /dev/null +++ b/vendor/symfony/polyfill-php84/bootstrap82.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php84 as p; + +if (\PHP_VERSION_ID >= 80400) { + return; +} + +if (extension_loaded('intl') && !function_exists('grapheme_str_split')) { + function grapheme_str_split(string $string, int $length = 1): array|false { return p\Php84::grapheme_str_split($string, $length); } +}