From ed7105dc808a92fb3e1afb7f4293e29843a96ffc Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Thu, 13 Jan 2022 11:56:28 +0000 Subject: [PATCH 01/37] DTO generation in PHP --- messages/jsonschema/scripts/codegen.rb | 45 +- .../scripts/templates/php.enum.php.erb | 0 .../jsonschema/scripts/templates/php.php.erb | 29 + messages/php/Makefile | 7 + messages/php/src/messages.dtos.php | 1380 +++++++++++++++++ 5 files changed, 1459 insertions(+), 2 deletions(-) create mode 100644 messages/jsonschema/scripts/templates/php.enum.php.erb create mode 100644 messages/jsonschema/scripts/templates/php.php.erb create mode 100644 messages/php/Makefile create mode 100644 messages/php/src/messages.dtos.php diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index fbe548cb147..679fefbd432 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -76,6 +76,7 @@ def type_for(parent_type_name, property_name, property) type = property['type'] items = property['items'] enum = property['enum'] + if ref property_type_from_ref(property['$ref']) elsif type @@ -84,8 +85,7 @@ def type_for(parent_type_name, property_name, property) else raise "No type mapping for JSONSchema type #{type}. Schema:\n#{JSON.pretty_generate(property)}" unless @language_type_by_schema_type[type] if enum - enum_type_name = "#{parent_type_name}#{capitalize(property_name)}" - @enums.add({ name: enum_type_name, values: enum }) + enum_type_name = enum_name(parent_type_name, property_name, enum) property_type_from_enum(enum_type_name) else @language_type_by_schema_type[type] @@ -105,6 +105,12 @@ def property_type_from_enum(enum) enum end + def enum_name(parent_type_name, property_name, enum) + enum_type_name = "#{parent_type_name}#{capitalize(property_name)}" + @enums.add({ name: enum_type_name, values: enum }) + enum_type_name + end + def class_name(ref) File.basename(ref, '.json') end @@ -295,6 +301,41 @@ def array_type_for(type_name) end end +class Php < Codegen + def initialize(paths) + template = File.read("#{TEMPLATES_DIRECTORY}/php.php.erb") + enum_template = File.read("#{TEMPLATES_DIRECTORY}/php.enum.php.erb") + + language_type_by_schema_type = { + 'string' => 'string', + 'integer' => 'int', + 'boolean' => 'bool', + } + super(paths, template, enum_template, language_type_by_schema_type) + end + + def format_description(raw_description, indent_string: " ") + return '' if raw_description.nil? + + raw_description + .split("\n") + .map { |description_line| " * #{description_line}" } + .join("\n#{indent_string}") + end + + def array_type_for(type_name) + "array" + end + + def enum_name(parent_type_name, property_name, enum) + enum_type_name = "#{class_name(parent_type_name)}\\#{capitalize(property_name)}" + @enums.add({ name: enum_type_name, values: enum }) + enum_type_name + end + +end + + clazz = Object.const_get(ARGV[0]) path = ARGV[1] paths = File.file?(path) ? [path] : Dir["#{path}/*.json"] diff --git a/messages/jsonschema/scripts/templates/php.enum.php.erb b/messages/jsonschema/scripts/templates/php.enum.php.erb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb new file mode 100644 index 00000000000..79f0675718f --- /dev/null +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -0,0 +1,29 @@ + +/** + * Represents the <%= class_name(key) %> message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * +<%= format_description(schema['description'], indent_string: '') %> + */ +final class <%= class_name(key) %> +{ + public function __construct( + <%- schema['properties'].each do |property_name, property| -%> + <%- if property['description'] -%> + /** + <%= format_description(property['description']) %> + */ + <%- end -%> + public readonly <%= type_for(key, property_name, property) %> $<%= property_name %>, + + <%- end -%> + ){} +} + +<% end %> diff --git a/messages/php/Makefile b/messages/php/Makefile new file mode 100644 index 00000000000..f36b7d2f467 --- /dev/null +++ b/messages/php/Makefile @@ -0,0 +1,7 @@ + +JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") + +.deps: src/messages.dtos.php + +src/messages.dtos.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb + ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ diff --git a/messages/php/src/messages.dtos.php b/messages/php/src/messages.dtos.php new file mode 100644 index 00000000000..2169ba27428 --- /dev/null +++ b/messages/php/src/messages.dtos.php @@ -0,0 +1,1380 @@ + IDENTITY + * - byte array => BASE64 + * - stream => BASE64 + */ + public readonly Attachment\ContentEncoding $contentEncoding, + + /** + * * + * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) + */ + public readonly string $fileName, + + /** + * * + * The media type of the data. This can be any valid + * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) + * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` + * and `text/x.cucumber.stacktrace+plain` + */ + public readonly string $mediaType, + + public readonly Source $source, + + public readonly string $testCaseStartedId, + + public readonly string $testStepId, + + /** + * * + * A URL where the attachment can be retrieved. This field should not be set by Cucumber. + * It should be set by a program that reads a message stream and does the following for + * each Attachment message: + * + * - Writes the body (after base64 decoding if necessary) to a new file. + * - Sets `body` and `contentEncoding` to `null` + * - Writes out the new attachment message + * + * This will result in a smaller message stream, which can improve performance and + * reduce bandwidth of message consumers. It also makes it easier to process and download attachments + * separately from reports. + */ + public readonly string $url, + + ){} +} + + +/** + * Represents the Duration message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * The structure is pretty close of the Timestamp one. For clarity, a second type + * of message is used. + */ +final class Duration +{ + public function __construct( + public readonly int $seconds, + + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + public readonly int $nanos, + + ){} +} + + +/** + * Represents the Envelope message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * When removing a field, replace it with reserved, rather than deleting the line. + * When adding a field, add it to the end and increment the number by one. + * See https://developers.google.com/protocol-buffers/docs/proto#updating for details + * + * * + * All the messages that are passed between different components/processes are Envelope + * messages. + */ +final class Envelope +{ + public function __construct( + public readonly Attachment $attachment, + + public readonly GherkinDocument $gherkinDocument, + + public readonly Hook $hook, + + public readonly Meta $meta, + + public readonly ParameterType $parameterType, + + public readonly ParseError $parseError, + + public readonly Pickle $pickle, + + public readonly Source $source, + + public readonly StepDefinition $stepDefinition, + + public readonly TestCase $testCase, + + public readonly TestCaseFinished $testCaseFinished, + + public readonly TestCaseStarted $testCaseStarted, + + public readonly TestRunFinished $testRunFinished, + + public readonly TestRunStarted $testRunStarted, + + public readonly TestStepFinished $testStepFinished, + + public readonly TestStepStarted $testStepStarted, + + public readonly UndefinedParameterType $undefinedParameterType, + + ){} +} + + +/** + * Represents the GherkinDocument message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document. + * Cucumber implementations should *not* depend on `GherkinDocument` or any of its + * children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead. + * + * The only consumers of `GherkinDocument` should only be formatters that produce + * "rich" output, resembling the original Gherkin document. + */ +final class GherkinDocument +{ + public function __construct( + /** + * * + * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + * of the source, typically a file path relative to the root directory + */ + public readonly string $uri, + + public readonly Feature $feature, + + /** + * All the comments in the Gherkin document + */ + public readonly array $comments, + + ){} +} + + +/** + * Represents the Background message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Background +{ + public function __construct( + /** + * The location of the `Background` keyword + */ + public readonly Location $location, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + public readonly array $steps, + + public readonly string $id, + + ){} +} + + +/** + * Represents the Comment message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * A comment in a Gherkin document + */ +final class Comment +{ + public function __construct( + /** + * The location of the comment + */ + public readonly Location $location, + + /** + * The text of the comment + */ + public readonly string $text, + + ){} +} + + +/** + * Represents the DataTable message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class DataTable +{ + public function __construct( + public readonly Location $location, + + public readonly array $rows, + + ){} +} + + +/** + * Represents the DocString message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class DocString +{ + public function __construct( + public readonly Location $location, + + public readonly string $mediaType, + + public readonly string $content, + + public readonly string $delimiter, + + ){} +} + + +/** + * Represents the Examples message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Examples +{ + public function __construct( + /** + * The location of the `Examples` keyword + */ + public readonly Location $location, + + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + public readonly TableRow $tableHeader, + + public readonly array $tableBody, + + public readonly string $id, + + ){} +} + + +/** + * Represents the Feature message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Feature +{ + public function __construct( + /** + * The location of the `Feature` keyword + */ + public readonly Location $location, + + /** + * All the tags placed above the `Feature` keyword + */ + public readonly array $tags, + + /** + * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document + */ + public readonly string $language, + + /** + * The text of the `Feature` keyword (in the language specified by `language`) + */ + public readonly string $keyword, + + /** + * The name of the feature (the text following the `keyword`) + */ + public readonly string $name, + + /** + * The line(s) underneath the line with the `keyword` that are used as description + */ + public readonly string $description, + + /** + * Zero or more children + */ + public readonly array $children, + + ){} +} + + +/** + * Represents the FeatureChild message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * A child node of a `Feature` node + */ +final class FeatureChild +{ + public function __construct( + public readonly Rule $rule, + + public readonly Background $background, + + public readonly Scenario $scenario, + + ){} +} + + +/** + * Represents the Rule message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Rule +{ + public function __construct( + /** + * The location of the `Rule` keyword + */ + public readonly Location $location, + + /** + * All the tags placed above the `Rule` keyword + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + public readonly array $children, + + public readonly string $id, + + ){} +} + + +/** + * Represents the RuleChild message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * A child node of a `Rule` node + */ +final class RuleChild +{ + public function __construct( + public readonly Background $background, + + public readonly Scenario $scenario, + + ){} +} + + +/** + * Represents the Scenario message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Scenario +{ + public function __construct( + /** + * The location of the `Scenario` keyword + */ + public readonly Location $location, + + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + public readonly array $steps, + + public readonly array $examples, + + public readonly string $id, + + ){} +} + + +/** + * Represents the Step message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * A step + */ +final class Step +{ + public function __construct( + /** + * The location of the steps' `keyword` + */ + public readonly Location $location, + + public readonly string $keyword, + + public readonly string $text, + + public readonly DocString $docString, + + public readonly DataTable $dataTable, + + /** + * Unique ID to be able to reference the Step from PickleStep + */ + public readonly string $id, + + ){} +} + + +/** + * Represents the TableCell message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * A cell in a `TableRow` + */ +final class TableCell +{ + public function __construct( + /** + * The location of the cell + */ + public readonly Location $location, + + /** + * The value of the cell + */ + public readonly string $value, + + ){} +} + + +/** + * Represents the TableRow message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * A row in a table + */ +final class TableRow +{ + public function __construct( + /** + * The location of the first cell in the row + */ + public readonly Location $location, + + /** + * Cells in the row + */ + public readonly array $cells, + + public readonly string $id, + + ){} +} + + +/** + * Represents the Tag message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * A tag + */ +final class Tag +{ + public function __construct( + /** + * Location of the tag + */ + public readonly Location $location, + + /** + * The name of the tag (including the leading `@`) + */ + public readonly string $name, + + /** + * Unique ID to be able to reference the Tag from PickleTag + */ + public readonly string $id, + + ){} +} + + +/** + * Represents the Hook message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Hook +{ + public function __construct( + public readonly string $id, + + public readonly SourceReference $sourceReference, + + public readonly string $tagExpression, + + ){} +} + + +/** + * Represents the Location message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * Points to a line and a column in a text file + */ +final class Location +{ + public function __construct( + public readonly int $line, + + public readonly int $column, + + ){} +} + + +/** + * Represents the Meta message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * This message contains meta information about the environment. Consumers can use + * this for various purposes. + */ +final class Meta +{ + public function __construct( + /** + * * + * The [SEMVER](https://semver.org/) version number of the protocol + */ + public readonly string $protocolVersion, + + /** + * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. + */ + public readonly Product $implementation, + + /** + * Java, Ruby, Node.js etc + */ + public readonly Product $runtime, + + /** + * Windows, Linux, MacOS etc + */ + public readonly Product $os, + + /** + * 386, arm, amd64 etc + */ + public readonly Product $cpu, + + public readonly Ci $ci, + + ){} +} + + +/** + * Represents the Ci message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * CI environment + */ +final class Ci +{ + public function __construct( + /** + * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. + */ + public readonly string $name, + + /** + * Link to the build + */ + public readonly string $url, + + /** + * The build number. Some CI servers use non-numeric build numbers, which is why this is a string + */ + public readonly string $buildNumber, + + public readonly Git $git, + + ){} +} + + +/** + * Represents the Git message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * Information about Git, provided by the Build/CI server as environment + * variables. + */ +final class Git +{ + public function __construct( + public readonly string $remote, + + public readonly string $revision, + + public readonly string $branch, + + public readonly string $tag, + + ){} +} + + +/** + * Represents the Product message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * Used to describe various properties of Meta + */ +final class Product +{ + public function __construct( + /** + * The product name + */ + public readonly string $name, + + /** + * The product version + */ + public readonly string $version, + + ){} +} + + +/** + * Represents the ParameterType message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class ParameterType +{ + public function __construct( + /** + * The name is unique, so we don't need an id. + */ + public readonly string $name, + + public readonly array $regularExpressions, + + public readonly bool $preferForRegularExpressionMatch, + + public readonly bool $useForSnippets, + + public readonly string $id, + + ){} +} + + +/** + * Represents the ParseError message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class ParseError +{ + public function __construct( + public readonly SourceReference $source, + + public readonly string $message, + + ){} +} + + +/** + * Represents the Pickle message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * //// Pickles + * + * * + * A `Pickle` represents a template for a `TestCase`. It is typically derived + * from another format, such as [GherkinDocument](#io.cucumber.messages.GherkinDocument). + * In the future a `Pickle` may be derived from other formats such as Markdown or + * Excel files. + * + * By making `Pickle` the main data structure Cucumber uses for execution, the + * implementation of Cucumber itself becomes simpler, as it doesn't have to deal + * with the complex structure of a [GherkinDocument](#io.cucumber.messages.GherkinDocument). + * + * Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` + */ +final class Pickle +{ + public function __construct( + /** + * * + * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash + * from the source data and the `locations` of the pickle. + * This ID will change if source the file is modified. + */ + public readonly string $id, + + /** + * The uri of the source file + */ + public readonly string $uri, + + /** + * The name of the pickle + */ + public readonly string $name, + + /** + * The language of the pickle + */ + public readonly string $language, + + /** + * One or more steps + */ + public readonly array $steps, + + /** + * * + * One or more tags. If this pickle is constructed from a Gherkin document, + * It includes inherited tags from the `Feature` as well. + */ + public readonly array $tags, + + /** + * * + * Points to the AST node locations of the pickle. The last one represents the unique + * id of the pickle. A pickle constructed from `Examples` will have the first + * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. + */ + public readonly array $astNodeIds, + + ){} +} + + +/** + * Represents the PickleDocString message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class PickleDocString +{ + public function __construct( + public readonly string $mediaType, + + public readonly string $content, + + ){} +} + + +/** + * Represents the PickleStep message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * An executable step + */ +final class PickleStep +{ + public function __construct( + public readonly PickleStepArgument $argument, + + /** + * References the IDs of the source of the step. For Gherkin, this can be + * the ID of a Step, and possibly also the ID of a TableRow + */ + public readonly array $astNodeIds, + + /** + * A unique ID for the PickleStep + */ + public readonly string $id, + + public readonly string $text, + + ){} +} + + +/** + * Represents the PickleStepArgument message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * An optional argument + */ +final class PickleStepArgument +{ + public function __construct( + public readonly PickleDocString $docString, + + public readonly PickleTable $dataTable, + + ){} +} + + +/** + * Represents the PickleTable message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class PickleTable +{ + public function __construct( + public readonly array $rows, + + ){} +} + + +/** + * Represents the PickleTableCell message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class PickleTableCell +{ + public function __construct( + public readonly string $value, + + ){} +} + + +/** + * Represents the PickleTableRow message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class PickleTableRow +{ + public function __construct( + public readonly array $cells, + + ){} +} + + +/** + * Represents the PickleTag message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * A tag + */ +final class PickleTag +{ + public function __construct( + public readonly string $name, + + /** + * Points to the AST node this was created from + */ + public readonly string $astNodeId, + + ){} +} + + +/** + * Represents the Source message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * //// Source + * + * * + * A source file, typically a Gherkin document or Java/Ruby/JavaScript source code + */ +final class Source +{ + public function __construct( + /** + * * + * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + * of the source, typically a file path relative to the root directory + */ + public readonly string $uri, + + /** + * The contents of the file + */ + public readonly string $data, + + /** + * The media type of the file. Can be used to specify custom types, such as + * text/x.cucumber.gherkin+plain + */ + public readonly Source\MediaType $mediaType, + + ){} +} + + +/** + * Represents the SourceReference message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * Points to a [Source](#io.cucumber.messages.Source) identified by `uri` and a + * [Location](#io.cucumber.messages.Location) within that file. + */ +final class SourceReference +{ + public function __construct( + public readonly string $uri, + + public readonly JavaMethod $javaMethod, + + public readonly JavaStackTraceElement $javaStackTraceElement, + + public readonly Location $location, + + ){} +} + + +/** + * Represents the JavaMethod message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class JavaMethod +{ + public function __construct( + public readonly string $className, + + public readonly string $methodName, + + public readonly array $methodParameterTypes, + + ){} +} + + +/** + * Represents the JavaStackTraceElement message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class JavaStackTraceElement +{ + public function __construct( + public readonly string $className, + + public readonly string $fileName, + + public readonly string $methodName, + + ){} +} + + +/** + * Represents the StepDefinition message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class StepDefinition +{ + public function __construct( + public readonly string $id, + + public readonly StepDefinitionPattern $pattern, + + public readonly SourceReference $sourceReference, + + ){} +} + + +/** + * Represents the StepDefinitionPattern message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class StepDefinitionPattern +{ + public function __construct( + public readonly string $source, + + public readonly StepDefinitionPattern\Type $type, + + ){} +} + + +/** + * Represents the TestCase message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * //// TestCases + * + * * + * A `TestCase` contains a sequence of `TestStep`s. + */ +final class TestCase +{ + public function __construct( + public readonly string $id, + + /** + * The ID of the `Pickle` this `TestCase` is derived from. + */ + public readonly string $pickleId, + + public readonly array $testSteps, + + ){} +} + + +/** + * Represents the Group message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Group +{ + public function __construct( + public readonly array $children, + + public readonly int $start, + + public readonly string $value, + + ){} +} + + +/** + * Represents the StepMatchArgument message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * Represents a single argument extracted from a step match and passed to a step definition. + * This is used for the following purposes: + * - Construct an argument to pass to a step definition (possibly through a parameter type transform) + * - Highlight the matched parameter in rich formatters such as the HTML formatter + * + * This message closely matches the `Argument` class in the `cucumber-expressions` library. + */ +final class StepMatchArgument +{ + public function __construct( + /** + * * + * Represents the outermost capture group of an argument. This message closely matches the + * `Group` class in the `cucumber-expressions` library. + */ + public readonly Group $group, + + public readonly string $parameterTypeName, + + ){} +} + + +/** + * Represents the StepMatchArgumentsList message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class StepMatchArgumentsList +{ + public function __construct( + public readonly array $stepMatchArguments, + + ){} +} + + +/** + * Represents the TestStep message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + * * + * A `TestStep` is derived from either a `PickleStep` + * combined with a `StepDefinition`, or from a `Hook`. + */ +final class TestStep +{ + public function __construct( + /** + * Pointer to the `Hook` (if derived from a Hook) + */ + public readonly string $hookId, + + public readonly string $id, + + /** + * Pointer to the `PickleStep` (if derived from a `PickleStep`) + */ + public readonly string $pickleStepId, + + /** + * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) + */ + public readonly array $stepDefinitionIds, + + /** + * A list of list of StepMatchArgument (if derived from a `PickleStep`). + * Each element represents a matching step definition. A size of 0 means `UNDEFINED`, + * and a size of 2+ means `AMBIGUOUS` + */ + public readonly array $stepMatchArgumentsLists, + + ){} +} + + +/** + * Represents the TestCaseFinished message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestCaseFinished +{ + public function __construct( + public readonly string $testCaseStartedId, + + public readonly Timestamp $timestamp, + + public readonly bool $willBeRetried, + + ){} +} + + +/** + * Represents the TestCaseStarted message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestCaseStarted +{ + public function __construct( + /** + * * + * The first attempt should have value 0, and for each retry the value + * should increase by 1. + */ + public readonly int $attempt, + + /** + * * + * Because a `TestCase` can be run multiple times (in case of a retry), + * we use this field to group messages relating to the same attempt. + */ + public readonly string $id, + + public readonly string $testCaseId, + + public readonly Timestamp $timestamp, + + ){} +} + + +/** + * Represents the TestRunFinished message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestRunFinished +{ + public function __construct( + /** + * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. + * If there are undefined parameter types, the message is simply + * "The following parameter type(s() are not defined: xxx, yyy". + * The independent `UndefinedParameterType` messages can be used to generate + * snippets for those parameter types. + */ + public readonly string $message, + + /** + * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) + */ + public readonly bool $success, + + /** + * Timestamp when the TestRun is finished + */ + public readonly Timestamp $timestamp, + + ){} +} + + +/** + * Represents the TestRunStarted message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestRunStarted +{ + public function __construct( + public readonly Timestamp $timestamp, + + ){} +} + + +/** + * Represents the TestStepFinished message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestStepFinished +{ + public function __construct( + public readonly string $testCaseStartedId, + + public readonly string $testStepId, + + public readonly TestStepResult $testStepResult, + + public readonly Timestamp $timestamp, + + ){} +} + + +/** + * Represents the TestStepResult message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestStepResult +{ + public function __construct( + public readonly Duration $duration, + + public readonly string $message, + + public readonly TestStepResult\Status $status, + + ){} +} + + +/** + * Represents the TestStepStarted message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class TestStepStarted +{ + public function __construct( + public readonly string $testCaseStartedId, + + public readonly string $testStepId, + + public readonly Timestamp $timestamp, + + ){} +} + + +/** + * Represents the Timestamp message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class Timestamp +{ + public function __construct( + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + */ + public readonly int $seconds, + + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + public readonly int $nanos, + + ){} +} + + +/** + * Represents the UndefinedParameterType message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * + + */ +final class UndefinedParameterType +{ + public function __construct( + public readonly string $expression, + + public readonly string $name, + + ){} +} + + From d621ae0f687c6ec1b1e2d2b8bc029e8f237405f4 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Thu, 13 Jan 2022 15:30:03 +0000 Subject: [PATCH 02/37] JSON encoding and decoding in PHP --- messages/jsonschema/scripts/codegen.rb | 45 +- .../scripts/templates/php.enum.php.erb | 11 + .../jsonschema/scripts/templates/php.php.erb | 38 +- messages/php/.gitignore | 3 + messages/php/Makefile | 8 +- messages/php/composer.json | 24 + messages/php/phpunit.xml | 26 + messages/php/psalm.xml | 16 + messages/php/src/JsonEncodingTrait.php | 32 + messages/php/src/generated/messages.php | 2132 +++++++++++++++++ messages/php/src/messages.dtos.php | 1380 ----------- messages/php/tests/AcceptanceTest.php | 31 + 12 files changed, 2354 insertions(+), 1392 deletions(-) create mode 100644 messages/php/.gitignore create mode 100644 messages/php/composer.json create mode 100644 messages/php/phpunit.xml create mode 100644 messages/php/psalm.xml create mode 100644 messages/php/src/JsonEncodingTrait.php create mode 100644 messages/php/src/generated/messages.php delete mode 100644 messages/php/src/messages.dtos.php create mode 100644 messages/php/tests/AcceptanceTest.php diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 679fefbd432..131baac13c3 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -319,7 +319,9 @@ def format_description(raw_description, indent_string: " ") raw_description .split("\n") - .map { |description_line| " * #{description_line}" } + .map { |line| line.strip() } + .filter { |line| line != '*' } + .map { |line| " * #{line}" } .join("\n#{indent_string}") end @@ -333,6 +335,47 @@ def enum_name(parent_type_name, property_name, enum) enum_type_name end + def array_contents_type(parent_type_name, property_name, property) + type_for(parent_type_name, nil, property['items']) + end + + def is_nullable(property_name, schema) + !(schema['required'] || []).index(property_name) + end + + def is_scalar(property) + property.has_key?('type') && @language_type_by_schema_type.has_key?(property['type']) + end + + def scalar_type_for(property) + raise "No type mapping for JSONSchema type #{type}. Schema:\n#{JSON.pretty_generate(property)}" unless @language_type_by_schema_type[property['type']] + @language_type_by_schema_type[property['type']] + end + + def constructor_for(parent_type, property, property_name, schema, arr_name) + constr = non_nullable_constructor_for(parent_type, property, property_name, schema, arr_name) + + is_nullable(property_name, schema) ? "isset($#{arr_name}['#{property_name}']) ? #{constr} : null" : constr + end + + def non_nullable_constructor_for(parent_type, property, property_name, schema, arr_name) + source = property_name.nil? ? "#{arr_name}" : "#{arr_name}['#{property_name}']" + if is_scalar(property) + if property['enum'] + "#{enum_name(parent_type, property_name, property['enum'])}::from((#{scalar_type_for(property)}) $#{source})" + else + "(#{scalar_type_for(property)}) $#{source}" + end + else + type = type_for(parent_type, property_name, property) + if type == 'array' + constructor = non_nullable_constructor_for(parent_type, property['items'], nil, schema, "member") + "array_map(fn(mixed $member) => #{constructor} , $#{source})" + else + "#{type_for(parent_type, property_name, property)}::fromArray($#{source})" + end + end + end end diff --git a/messages/jsonschema/scripts/templates/php.enum.php.erb b/messages/jsonschema/scripts/templates/php.enum.php.erb index e69de29bb2d..321bde05f72 100644 --- a/messages/jsonschema/scripts/templates/php.enum.php.erb +++ b/messages/jsonschema/scripts/templates/php.enum.php.erb @@ -0,0 +1,11 @@ +<%- namespaces = enum[:name].split('\\') -%> +namespace Cucumber\Messages\<%= namespaces.slice(0,1)[0] %>; + +enum <%= namespaces[-1] %> : string +{ +<%- enum[:values].each_with_index do |value, index| -%> + case <%= enum_constant(value) %> = '<%= value %>'; +<%- end -%> +} + +<%# block to preserve linebreaks %> diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index 79f0675718f..fb70636e8d2 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -5,25 +5,45 @@ */ namespace Cucumber\Messages; + +use \JsonSerializable; <% @schemas.sort.each do |key, schema| %> /** - * Represents the <%= class_name(key) %> message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. + * Represents the <%= class_name(key) %> message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme * -<%= format_description(schema['description'], indent_string: '') %> - */ -final class <%= class_name(key) %> +<%=- format_description(schema['description'], indent_string: '') -%> */ +final class <%= class_name(key) %> implements JsonSerializable { - public function __construct( + use JsonEncodingTrait; + + private function __construct( <%- schema['properties'].each do |property_name, property| -%> - <%- if property['description'] -%> + + <%- property_type = type_for(key, property_name, property) -%> + <%- if property['description'] or property_type == 'array' -%> /** - <%= format_description(property['description']) %> + <%- if property['description'] -%> + <%= format_description(property['description']) %> + <%- end -%> + <%- if property_type == 'array' -%> + * @param <%= is_nullable(property_name, schema) ? '?' : '' %>list<<%= array_contents_type(key, property_name, property) %>> $<%= property_name %> + <%- end -%> */ <%- end -%> - public readonly <%= type_for(key, property_name, property) %> $<%= property_name %>, - + public readonly <%= is_nullable(property_name, schema) ? '?' : '' %><%= property_type %> $<%= property_name %>, <%- end -%> + ){} + + public static function fromArray(array $arr) : self + { + return new self( + <%- schema['properties'].each do |property_name, property| -%> + <%= constructor_for(key, property, property_name, schema, 'arr') %>, + <%- end -%> + ); + } } <% end %> diff --git a/messages/php/.gitignore b/messages/php/.gitignore new file mode 100644 index 00000000000..999d5412dc2 --- /dev/null +++ b/messages/php/.gitignore @@ -0,0 +1,3 @@ +vendor +composer.lock +.phpunit.cache diff --git a/messages/php/Makefile b/messages/php/Makefile index f36b7d2f467..1a807e42bef 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -1,7 +1,11 @@ JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") -.deps: src/messages.dtos.php +.deps: src/generated/messages.php -src/messages.dtos.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb +src/generated/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ + +test: .deps + vendor/bin/psalm + vendor/bin/phpunit diff --git a/messages/php/composer.json b/messages/php/composer.json new file mode 100644 index 00000000000..0a6708f74b3 --- /dev/null +++ b/messages/php/composer.json @@ -0,0 +1,24 @@ +{ + "name": "cucumber/messages", + "description": "JSON schema-based messages for Cucumber's inter-process communication", + "author": "Cucumber Limited ", + "license": "MIT", + "type": "library", + "require": { + "php": "^8.1", + "ext-json": "*" + }, + "require-dev": { + "vimeo/psalm": "^4.18", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.16.1" + }, + "autoload": { + "psr-4": { + "Cucumber\\Messages\\": "src" + }, + "files": [ + "src/generated/messages.php" + ] + } +} diff --git a/messages/php/phpunit.xml b/messages/php/phpunit.xml new file mode 100644 index 00000000000..fd5e5582fcb --- /dev/null +++ b/messages/php/phpunit.xml @@ -0,0 +1,26 @@ + + + + + tests + + + + + + src + + + diff --git a/messages/php/psalm.xml b/messages/php/psalm.xml new file mode 100644 index 00000000000..23ea9bbbd7e --- /dev/null +++ b/messages/php/psalm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/messages/php/src/JsonEncodingTrait.php b/messages/php/src/JsonEncodingTrait.php new file mode 100644 index 00000000000..8e315941c6e --- /dev/null +++ b/messages/php/src/JsonEncodingTrait.php @@ -0,0 +1,32 @@ + !is_null($x) + ); + } +} diff --git a/messages/php/src/generated/messages.php b/messages/php/src/generated/messages.php new file mode 100644 index 00000000000..8d81beba112 --- /dev/null +++ b/messages/php/src/generated/messages.php @@ -0,0 +1,2132 @@ + IDENTITY + * - byte array => BASE64 + * - stream => BASE64 + */ + public readonly Attachment\ContentEncoding $contentEncoding, + + /** + * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) + */ + public readonly ?string $fileName, + + /** + * The media type of the data. This can be any valid + * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) + * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` + * and `text/x.cucumber.stacktrace+plain` + */ + public readonly string $mediaType, + + public readonly ?Source $source, + + public readonly ?string $testCaseStartedId, + + public readonly ?string $testStepId, + + /** + * A URL where the attachment can be retrieved. This field should not be set by Cucumber. + * It should be set by a program that reads a message stream and does the following for + * each Attachment message: + * + * - Writes the body (after base64 decoding if necessary) to a new file. + * - Sets `body` and `contentEncoding` to `null` + * - Writes out the new attachment message + * + * This will result in a smaller message stream, which can improve performance and + * reduce bandwidth of message consumers. It also makes it easier to process and download attachments + * separately from reports. + */ + public readonly ?string $url, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['body'], + Attachment\ContentEncoding::from((string) $arr['contentEncoding']), + isset($arr['fileName']) ? (string) $arr['fileName'] : null, + (string) $arr['mediaType'], + isset($arr['source']) ? Source::fromArray($arr['source']) : null, + isset($arr['testCaseStartedId']) ? (string) $arr['testCaseStartedId'] : null, + isset($arr['testStepId']) ? (string) $arr['testStepId'] : null, + isset($arr['url']) ? (string) $arr['url'] : null, + ); + } +} + + +/** + * Represents the Duration message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * The structure is pretty close of the Timestamp one. For clarity, a second type + * of message is used. */ +final class Duration implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly int $seconds, + + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + public readonly int $nanos, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (int) $arr['seconds'], + (int) $arr['nanos'], + ); + } +} + + +/** + * Represents the Envelope message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * When removing a field, replace it with reserved, rather than deleting the line. + * When adding a field, add it to the end and increment the number by one. + * See https://developers.google.com/protocol-buffers/docs/proto#updating for details + * + * All the messages that are passed between different components/processes are Envelope + * messages. */ +final class Envelope implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?Attachment $attachment, + + public readonly ?GherkinDocument $gherkinDocument, + + public readonly ?Hook $hook, + + public readonly ?Meta $meta, + + public readonly ?ParameterType $parameterType, + + public readonly ?ParseError $parseError, + + public readonly ?Pickle $pickle, + + public readonly ?Source $source, + + public readonly ?StepDefinition $stepDefinition, + + public readonly ?TestCase $testCase, + + public readonly ?TestCaseFinished $testCaseFinished, + + public readonly ?TestCaseStarted $testCaseStarted, + + public readonly ?TestRunFinished $testRunFinished, + + public readonly ?TestRunStarted $testRunStarted, + + public readonly ?TestStepFinished $testStepFinished, + + public readonly ?TestStepStarted $testStepStarted, + + public readonly ?UndefinedParameterType $undefinedParameterType, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['attachment']) ? Attachment::fromArray($arr['attachment']) : null, + isset($arr['gherkinDocument']) ? GherkinDocument::fromArray($arr['gherkinDocument']) : null, + isset($arr['hook']) ? Hook::fromArray($arr['hook']) : null, + isset($arr['meta']) ? Meta::fromArray($arr['meta']) : null, + isset($arr['parameterType']) ? ParameterType::fromArray($arr['parameterType']) : null, + isset($arr['parseError']) ? ParseError::fromArray($arr['parseError']) : null, + isset($arr['pickle']) ? Pickle::fromArray($arr['pickle']) : null, + isset($arr['source']) ? Source::fromArray($arr['source']) : null, + isset($arr['stepDefinition']) ? StepDefinition::fromArray($arr['stepDefinition']) : null, + isset($arr['testCase']) ? TestCase::fromArray($arr['testCase']) : null, + isset($arr['testCaseFinished']) ? TestCaseFinished::fromArray($arr['testCaseFinished']) : null, + isset($arr['testCaseStarted']) ? TestCaseStarted::fromArray($arr['testCaseStarted']) : null, + isset($arr['testRunFinished']) ? TestRunFinished::fromArray($arr['testRunFinished']) : null, + isset($arr['testRunStarted']) ? TestRunStarted::fromArray($arr['testRunStarted']) : null, + isset($arr['testStepFinished']) ? TestStepFinished::fromArray($arr['testStepFinished']) : null, + isset($arr['testStepStarted']) ? TestStepStarted::fromArray($arr['testStepStarted']) : null, + isset($arr['undefinedParameterType']) ? UndefinedParameterType::fromArray($arr['undefinedParameterType']) : null, + ); + } +} + + +/** + * Represents the GherkinDocument message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document. + * Cucumber implementations should *not* depend on `GherkinDocument` or any of its + * children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead. + * + * The only consumers of `GherkinDocument` should only be formatters that produce + * "rich" output, resembling the original Gherkin document. */ +final class GherkinDocument implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + * of the source, typically a file path relative to the root directory + */ + public readonly ?string $uri, + + public readonly ?Feature $feature, + + /** + * All the comments in the Gherkin document + * @param list $comments + */ + public readonly array $comments, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['uri']) ? (string) $arr['uri'] : null, + isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null, + array_map(fn(mixed $member) => Comment::fromArray($member) , $arr['comments']), + ); + } +} + + +/** + * Represents the Background message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Background implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the `Background` keyword + */ + public readonly Location $location, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + /** + * @param list $steps + */ + public readonly array $steps, + + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(mixed $member) => Step::fromArray($member) , $arr['steps']), + (string) $arr['id'], + ); + } +} + + +/** + * Represents the Comment message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A comment in a Gherkin document */ +final class Comment implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the comment + */ + public readonly Location $location, + + /** + * The text of the comment + */ + public readonly string $text, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + (string) $arr['text'], + ); + } +} + + +/** + * Represents the DataTable message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class DataTable implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly Location $location, + + /** + * @param list $rows + */ + public readonly array $rows, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + array_map(fn(mixed $member) => TableRow::fromArray($member) , $arr['rows']), + ); + } +} + + +/** + * Represents the DocString message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class DocString implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly Location $location, + + public readonly ?string $mediaType, + + public readonly string $content, + + public readonly string $delimiter, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, + (string) $arr['content'], + (string) $arr['delimiter'], + ); + } +} + + +/** + * Represents the Examples message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Examples implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the `Examples` keyword + */ + public readonly Location $location, + + /** + * @param list $tags + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + public readonly ?TableRow $tableHeader, + + /** + * @param list $tableBody + */ + public readonly array $tableBody, + + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, + array_map(fn(mixed $member) => TableRow::fromArray($member) , $arr['tableBody']), + (string) $arr['id'], + ); + } +} + + +/** + * Represents the Feature message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Feature implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the `Feature` keyword + */ + public readonly Location $location, + + /** + * All the tags placed above the `Feature` keyword + * @param list $tags + */ + public readonly array $tags, + + /** + * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document + */ + public readonly string $language, + + /** + * The text of the `Feature` keyword (in the language specified by `language`) + */ + public readonly string $keyword, + + /** + * The name of the feature (the text following the `keyword`) + */ + public readonly string $name, + + /** + * The line(s) underneath the line with the `keyword` that are used as description + */ + public readonly string $description, + + /** + * Zero or more children + * @param list $children + */ + public readonly array $children, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['language'], + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(mixed $member) => FeatureChild::fromArray($member) , $arr['children']), + ); + } +} + + +/** + * Represents the FeatureChild message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A child node of a `Feature` node */ +final class FeatureChild implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?Rule $rule, + + public readonly ?Background $background, + + public readonly ?Scenario $scenario, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['rule']) ? Rule::fromArray($arr['rule']) : null, + isset($arr['background']) ? Background::fromArray($arr['background']) : null, + isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, + ); + } +} + + +/** + * Represents the Rule message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Rule implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the `Rule` keyword + */ + public readonly Location $location, + + /** + * All the tags placed above the `Rule` keyword + * @param list $tags + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + /** + * @param list $children + */ + public readonly array $children, + + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(mixed $member) => RuleChild::fromArray($member) , $arr['children']), + (string) $arr['id'], + ); + } +} + + +/** + * Represents the RuleChild message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A child node of a `Rule` node */ +final class RuleChild implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?Background $background, + + public readonly ?Scenario $scenario, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['background']) ? Background::fromArray($arr['background']) : null, + isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, + ); + } +} + + +/** + * Represents the Scenario message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Scenario implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the `Scenario` keyword + */ + public readonly Location $location, + + /** + * @param list $tags + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + /** + * @param list $steps + */ + public readonly array $steps, + + /** + * @param list $examples + */ + public readonly array $examples, + + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(mixed $member) => Step::fromArray($member) , $arr['steps']), + array_map(fn(mixed $member) => Examples::fromArray($member) , $arr['examples']), + (string) $arr['id'], + ); + } +} + + +/** + * Represents the Step message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A step */ +final class Step implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the steps' `keyword` + */ + public readonly Location $location, + + public readonly string $keyword, + + public readonly string $text, + + public readonly ?DocString $docString, + + public readonly ?DataTable $dataTable, + + /** + * Unique ID to be able to reference the Step from PickleStep + */ + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + (string) $arr['keyword'], + (string) $arr['text'], + isset($arr['docString']) ? DocString::fromArray($arr['docString']) : null, + isset($arr['dataTable']) ? DataTable::fromArray($arr['dataTable']) : null, + (string) $arr['id'], + ); + } +} + + +/** + * Represents the TableCell message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A cell in a `TableRow` */ +final class TableCell implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the cell + */ + public readonly Location $location, + + /** + * The value of the cell + */ + public readonly string $value, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + (string) $arr['value'], + ); + } +} + + +/** + * Represents the TableRow message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A row in a table */ +final class TableRow implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The location of the first cell in the row + */ + public readonly Location $location, + + /** + * Cells in the row + * @param list $cells + */ + public readonly array $cells, + + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + array_map(fn(mixed $member) => TableCell::fromArray($member) , $arr['cells']), + (string) $arr['id'], + ); + } +} + + +/** + * Represents the Tag message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A tag */ +final class Tag implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * Location of the tag + */ + public readonly Location $location, + + /** + * The name of the tag (including the leading `@`) + */ + public readonly string $name, + + /** + * Unique ID to be able to reference the Tag from PickleTag + */ + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Location::fromArray($arr['location']), + (string) $arr['name'], + (string) $arr['id'], + ); + } +} + + +/** + * Represents the Hook message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Hook implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $id, + + public readonly SourceReference $sourceReference, + + public readonly ?string $tagExpression, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['id'], + SourceReference::fromArray($arr['sourceReference']), + isset($arr['tagExpression']) ? (string) $arr['tagExpression'] : null, + ); + } +} + + +/** + * Represents the Location message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * Points to a line and a column in a text file */ +final class Location implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly int $line, + + public readonly ?int $column, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (int) $arr['line'], + isset($arr['column']) ? (int) $arr['column'] : null, + ); + } +} + + +/** + * Represents the Meta message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * This message contains meta information about the environment. Consumers can use + * this for various purposes. */ +final class Meta implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The [SEMVER](https://semver.org/) version number of the protocol + */ + public readonly string $protocolVersion, + + /** + * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. + */ + public readonly Product $implementation, + + /** + * Java, Ruby, Node.js etc + */ + public readonly Product $runtime, + + /** + * Windows, Linux, MacOS etc + */ + public readonly Product $os, + + /** + * 386, arm, amd64 etc + */ + public readonly Product $cpu, + + public readonly ?Ci $ci, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['protocolVersion'], + Product::fromArray($arr['implementation']), + Product::fromArray($arr['runtime']), + Product::fromArray($arr['os']), + Product::fromArray($arr['cpu']), + isset($arr['ci']) ? Ci::fromArray($arr['ci']) : null, + ); + } +} + + +/** + * Represents the Ci message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * CI environment */ +final class Ci implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. + */ + public readonly string $name, + + /** + * Link to the build + */ + public readonly ?string $url, + + /** + * The build number. Some CI servers use non-numeric build numbers, which is why this is a string + */ + public readonly ?string $buildNumber, + + public readonly ?Git $git, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['name'], + isset($arr['url']) ? (string) $arr['url'] : null, + isset($arr['buildNumber']) ? (string) $arr['buildNumber'] : null, + isset($arr['git']) ? Git::fromArray($arr['git']) : null, + ); + } +} + + +/** + * Represents the Git message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * Information about Git, provided by the Build/CI server as environment + * variables. */ +final class Git implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $remote, + + public readonly string $revision, + + public readonly ?string $branch, + + public readonly ?string $tag, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['remote'], + (string) $arr['revision'], + isset($arr['branch']) ? (string) $arr['branch'] : null, + isset($arr['tag']) ? (string) $arr['tag'] : null, + ); + } +} + + +/** + * Represents the Product message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * Used to describe various properties of Meta */ +final class Product implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The product name + */ + public readonly string $name, + + /** + * The product version + */ + public readonly ?string $version, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['name'], + isset($arr['version']) ? (string) $arr['version'] : null, + ); + } +} + + +/** + * Represents the ParameterType message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class ParameterType implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The name is unique, so we don't need an id. + */ + public readonly string $name, + + /** + * @param list $regularExpressions + */ + public readonly array $regularExpressions, + + public readonly bool $preferForRegularExpressionMatch, + + public readonly bool $useForSnippets, + + public readonly string $id, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['name'], + array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), + (bool) $arr['preferForRegularExpressionMatch'], + (bool) $arr['useForSnippets'], + (string) $arr['id'], + ); + } +} + + +/** + * Represents the ParseError message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class ParseError implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly SourceReference $source, + + public readonly string $message, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + SourceReference::fromArray($arr['source']), + (string) $arr['message'], + ); + } +} + + +/** + * Represents the Pickle message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * //// Pickles + * + * A `Pickle` represents a template for a `TestCase`. It is typically derived + * from another format, such as [GherkinDocument](#io.cucumber.messages.GherkinDocument). + * In the future a `Pickle` may be derived from other formats such as Markdown or + * Excel files. + * + * By making `Pickle` the main data structure Cucumber uses for execution, the + * implementation of Cucumber itself becomes simpler, as it doesn't have to deal + * with the complex structure of a [GherkinDocument](#io.cucumber.messages.GherkinDocument). + * + * Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` */ +final class Pickle implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash + * from the source data and the `locations` of the pickle. + * This ID will change if source the file is modified. + */ + public readonly string $id, + + /** + * The uri of the source file + */ + public readonly string $uri, + + /** + * The name of the pickle + */ + public readonly string $name, + + /** + * The language of the pickle + */ + public readonly string $language, + + /** + * One or more steps + * @param list $steps + */ + public readonly array $steps, + + /** + * One or more tags. If this pickle is constructed from a Gherkin document, + * It includes inherited tags from the `Feature` as well. + * @param list $tags + */ + public readonly array $tags, + + /** + * Points to the AST node locations of the pickle. The last one represents the unique + * id of the pickle. A pickle constructed from `Examples` will have the first + * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. + * @param list $astNodeIds + */ + public readonly array $astNodeIds, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['id'], + (string) $arr['uri'], + (string) $arr['name'], + (string) $arr['language'], + array_map(fn(mixed $member) => PickleStep::fromArray($member) , $arr['steps']), + array_map(fn(mixed $member) => PickleTag::fromArray($member) , $arr['tags']), + array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + ); + } +} + + +/** + * Represents the PickleDocString message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class PickleDocString implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?string $mediaType, + + public readonly string $content, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, + (string) $arr['content'], + ); + } +} + + +/** + * Represents the PickleStep message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * An executable step */ +final class PickleStep implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?PickleStepArgument $argument, + + /** + * References the IDs of the source of the step. For Gherkin, this can be + * the ID of a Step, and possibly also the ID of a TableRow + * @param list $astNodeIds + */ + public readonly array $astNodeIds, + + /** + * A unique ID for the PickleStep + */ + public readonly string $id, + + public readonly string $text, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['argument']) ? PickleStepArgument::fromArray($arr['argument']) : null, + array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + (string) $arr['id'], + (string) $arr['text'], + ); + } +} + + +/** + * Represents the PickleStepArgument message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * An optional argument */ +final class PickleStepArgument implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?PickleDocString $docString, + + public readonly ?PickleTable $dataTable, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['docString']) ? PickleDocString::fromArray($arr['docString']) : null, + isset($arr['dataTable']) ? PickleTable::fromArray($arr['dataTable']) : null, + ); + } +} + + +/** + * Represents the PickleTable message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class PickleTable implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * @param list $rows + */ + public readonly array $rows, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + array_map(fn(mixed $member) => PickleTableRow::fromArray($member) , $arr['rows']), + ); + } +} + + +/** + * Represents the PickleTableCell message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class PickleTableCell implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $value, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['value'], + ); + } +} + + +/** + * Represents the PickleTableRow message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class PickleTableRow implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * @param list $cells + */ + public readonly array $cells, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + array_map(fn(mixed $member) => PickleTableCell::fromArray($member) , $arr['cells']), + ); + } +} + + +/** + * Represents the PickleTag message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A tag */ +final class PickleTag implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $name, + + /** + * Points to the AST node this was created from + */ + public readonly string $astNodeId, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['name'], + (string) $arr['astNodeId'], + ); + } +} + + +/** + * Represents the Source message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * //// Source + * + * A source file, typically a Gherkin document or Java/Ruby/JavaScript source code */ +final class Source implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + * of the source, typically a file path relative to the root directory + */ + public readonly string $uri, + + /** + * The contents of the file + */ + public readonly string $data, + + /** + * The media type of the file. Can be used to specify custom types, such as + * text/x.cucumber.gherkin+plain + */ + public readonly Source\MediaType $mediaType, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['uri'], + (string) $arr['data'], + Source\MediaType::from((string) $arr['mediaType']), + ); + } +} + + +/** + * Represents the SourceReference message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * Points to a [Source](#io.cucumber.messages.Source) identified by `uri` and a + * [Location](#io.cucumber.messages.Location) within that file. */ +final class SourceReference implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly ?string $uri, + + public readonly ?JavaMethod $javaMethod, + + public readonly ?JavaStackTraceElement $javaStackTraceElement, + + public readonly ?Location $location, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['uri']) ? (string) $arr['uri'] : null, + isset($arr['javaMethod']) ? JavaMethod::fromArray($arr['javaMethod']) : null, + isset($arr['javaStackTraceElement']) ? JavaStackTraceElement::fromArray($arr['javaStackTraceElement']) : null, + isset($arr['location']) ? Location::fromArray($arr['location']) : null, + ); + } +} + + +/** + * Represents the JavaMethod message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class JavaMethod implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $className, + + public readonly string $methodName, + + /** + * @param list $methodParameterTypes + */ + public readonly array $methodParameterTypes, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['className'], + (string) $arr['methodName'], + array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), + ); + } +} + + +/** + * Represents the JavaStackTraceElement message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class JavaStackTraceElement implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $className, + + public readonly string $fileName, + + public readonly string $methodName, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['className'], + (string) $arr['fileName'], + (string) $arr['methodName'], + ); + } +} + + +/** + * Represents the StepDefinition message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class StepDefinition implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $id, + + public readonly StepDefinitionPattern $pattern, + + public readonly SourceReference $sourceReference, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['id'], + StepDefinitionPattern::fromArray($arr['pattern']), + SourceReference::fromArray($arr['sourceReference']), + ); + } +} + + +/** + * Represents the StepDefinitionPattern message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class StepDefinitionPattern implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $source, + + public readonly StepDefinitionPattern\Type $type, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['source'], + StepDefinitionPattern\Type::from((string) $arr['type']), + ); + } +} + + +/** + * Represents the TestCase message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * //// TestCases + * + * A `TestCase` contains a sequence of `TestStep`s. */ +final class TestCase implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $id, + + /** + * The ID of the `Pickle` this `TestCase` is derived from. + */ + public readonly string $pickleId, + + /** + * @param list $testSteps + */ + public readonly array $testSteps, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['id'], + (string) $arr['pickleId'], + array_map(fn(mixed $member) => TestStep::fromArray($member) , $arr['testSteps']), + ); + } +} + + +/** + * Represents the Group message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Group implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * @param list $children + */ + public readonly array $children, + + public readonly ?int $start, + + public readonly ?string $value, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + array_map(fn(mixed $member) => Group::fromArray($member) , $arr['children']), + isset($arr['start']) ? (int) $arr['start'] : null, + isset($arr['value']) ? (string) $arr['value'] : null, + ); + } +} + + +/** + * Represents the StepMatchArgument message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * Represents a single argument extracted from a step match and passed to a step definition. + * This is used for the following purposes: + * - Construct an argument to pass to a step definition (possibly through a parameter type transform) + * - Highlight the matched parameter in rich formatters such as the HTML formatter + * + * This message closely matches the `Argument` class in the `cucumber-expressions` library. */ +final class StepMatchArgument implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * Represents the outermost capture group of an argument. This message closely matches the + * `Group` class in the `cucumber-expressions` library. + */ + public readonly Group $group, + + public readonly ?string $parameterTypeName, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Group::fromArray($arr['group']), + isset($arr['parameterTypeName']) ? (string) $arr['parameterTypeName'] : null, + ); + } +} + + +/** + * Represents the StepMatchArgumentsList message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class StepMatchArgumentsList implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * @param list $stepMatchArguments + */ + public readonly array $stepMatchArguments, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + array_map(fn(mixed $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), + ); + } +} + + +/** + * Represents the TestStep message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + * A `TestStep` is derived from either a `PickleStep` + * combined with a `StepDefinition`, or from a `Hook`. */ +final class TestStep implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * Pointer to the `Hook` (if derived from a Hook) + */ + public readonly ?string $hookId, + + public readonly string $id, + + /** + * Pointer to the `PickleStep` (if derived from a `PickleStep`) + */ + public readonly ?string $pickleStepId, + + /** + * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) + * @param ?list $stepDefinitionIds + */ + public readonly ?array $stepDefinitionIds, + + /** + * A list of list of StepMatchArgument (if derived from a `PickleStep`). + * Each element represents a matching step definition. A size of 0 means `UNDEFINED`, + * and a size of 2+ means `AMBIGUOUS` + * @param ?list $stepMatchArgumentsLists + */ + public readonly ?array $stepMatchArgumentsLists, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['hookId']) ? (string) $arr['hookId'] : null, + (string) $arr['id'], + isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, + isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, + isset($arr['stepMatchArgumentsLists']) ? array_map(fn(mixed $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, + ); + } +} + + +/** + * Represents the TestCaseFinished message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestCaseFinished implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $testCaseStartedId, + + public readonly Timestamp $timestamp, + + public readonly bool $willBeRetried, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['testCaseStartedId'], + Timestamp::fromArray($arr['timestamp']), + (bool) $arr['willBeRetried'], + ); + } +} + + +/** + * Represents the TestCaseStarted message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestCaseStarted implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * The first attempt should have value 0, and for each retry the value + * should increase by 1. + */ + public readonly int $attempt, + + /** + * Because a `TestCase` can be run multiple times (in case of a retry), + * we use this field to group messages relating to the same attempt. + */ + public readonly string $id, + + public readonly string $testCaseId, + + public readonly Timestamp $timestamp, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (int) $arr['attempt'], + (string) $arr['id'], + (string) $arr['testCaseId'], + Timestamp::fromArray($arr['timestamp']), + ); + } +} + + +/** + * Represents the TestRunFinished message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestRunFinished implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. + * If there are undefined parameter types, the message is simply + * "The following parameter type(s() are not defined: xxx, yyy". + * The independent `UndefinedParameterType` messages can be used to generate + * snippets for those parameter types. + */ + public readonly ?string $message, + + /** + * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) + */ + public readonly bool $success, + + /** + * Timestamp when the TestRun is finished + */ + public readonly Timestamp $timestamp, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + isset($arr['message']) ? (string) $arr['message'] : null, + (bool) $arr['success'], + Timestamp::fromArray($arr['timestamp']), + ); + } +} + + +/** + * Represents the TestRunStarted message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestRunStarted implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly Timestamp $timestamp, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Timestamp::fromArray($arr['timestamp']), + ); + } +} + + +/** + * Represents the TestStepFinished message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestStepFinished implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $testCaseStartedId, + + public readonly string $testStepId, + + public readonly TestStepResult $testStepResult, + + public readonly Timestamp $timestamp, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['testCaseStartedId'], + (string) $arr['testStepId'], + TestStepResult::fromArray($arr['testStepResult']), + Timestamp::fromArray($arr['timestamp']), + ); + } +} + + +/** + * Represents the TestStepResult message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestStepResult implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly Duration $duration, + + public readonly ?string $message, + + public readonly TestStepResult\Status $status, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + Duration::fromArray($arr['duration']), + isset($arr['message']) ? (string) $arr['message'] : null, + TestStepResult\Status::from((string) $arr['status']), + ); + } +} + + +/** + * Represents the TestStepStarted message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class TestStepStarted implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $testCaseStartedId, + + public readonly string $testStepId, + + public readonly Timestamp $timestamp, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['testCaseStartedId'], + (string) $arr['testStepId'], + Timestamp::fromArray($arr['timestamp']), + ); + } +} + + +/** + * Represents the Timestamp message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class Timestamp implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + */ + public readonly int $seconds, + + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + public readonly int $nanos, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (int) $arr['seconds'], + (int) $arr['nanos'], + ); + } +} + + +/** + * Represents the UndefinedParameterType message in Cucumber's message protocol + * @see https://github.com/cucumber/common/tree/main/messages#readme + * + */ +final class UndefinedParameterType implements JsonSerializable +{ + use JsonEncodingTrait; + + private function __construct( + + public readonly string $expression, + + public readonly string $name, + + ){} + + public static function fromArray(array $arr) : self + { + return new self( + (string) $arr['expression'], + (string) $arr['name'], + ); + } +} + + +namespace Cucumber\Messages\Attachment; + +enum ContentEncoding : string +{ + case IDENTITY = 'IDENTITY'; + case BASE64 = 'BASE64'; +} + + +namespace Cucumber\Messages\Source; + +enum MediaType : string +{ + case TEXT_X_CUCUMBER_GHERKIN_PLAIN = 'text/x.cucumber.gherkin+plain'; + case TEXT_X_CUCUMBER_GHERKIN_MARKDOWN = 'text/x.cucumber.gherkin+markdown'; +} + + +namespace Cucumber\Messages\StepDefinitionPattern; + +enum Type : string +{ + case CUCUMBER_EXPRESSION = 'CUCUMBER_EXPRESSION'; + case REGULAR_EXPRESSION = 'REGULAR_EXPRESSION'; +} + + +namespace Cucumber\Messages\TestStepResult; + +enum Status : string +{ + case UNKNOWN = 'UNKNOWN'; + case PASSED = 'PASSED'; + case SKIPPED = 'SKIPPED'; + case PENDING = 'PENDING'; + case UNDEFINED = 'UNDEFINED'; + case AMBIGUOUS = 'AMBIGUOUS'; + case FAILED = 'FAILED'; +} + + diff --git a/messages/php/src/messages.dtos.php b/messages/php/src/messages.dtos.php deleted file mode 100644 index 2169ba27428..00000000000 --- a/messages/php/src/messages.dtos.php +++ /dev/null @@ -1,1380 +0,0 @@ - IDENTITY - * - byte array => BASE64 - * - stream => BASE64 - */ - public readonly Attachment\ContentEncoding $contentEncoding, - - /** - * * - * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) - */ - public readonly string $fileName, - - /** - * * - * The media type of the data. This can be any valid - * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) - * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` - * and `text/x.cucumber.stacktrace+plain` - */ - public readonly string $mediaType, - - public readonly Source $source, - - public readonly string $testCaseStartedId, - - public readonly string $testStepId, - - /** - * * - * A URL where the attachment can be retrieved. This field should not be set by Cucumber. - * It should be set by a program that reads a message stream and does the following for - * each Attachment message: - * - * - Writes the body (after base64 decoding if necessary) to a new file. - * - Sets `body` and `contentEncoding` to `null` - * - Writes out the new attachment message - * - * This will result in a smaller message stream, which can improve performance and - * reduce bandwidth of message consumers. It also makes it easier to process and download attachments - * separately from reports. - */ - public readonly string $url, - - ){} -} - - -/** - * Represents the Duration message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * The structure is pretty close of the Timestamp one. For clarity, a second type - * of message is used. - */ -final class Duration -{ - public function __construct( - public readonly int $seconds, - - /** - * Non-negative fractions of a second at nanosecond resolution. Negative - * second values with fractions must still have non-negative nanos values - * that count forward in time. Must be from 0 to 999,999,999 - * inclusive. - */ - public readonly int $nanos, - - ){} -} - - -/** - * Represents the Envelope message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * When removing a field, replace it with reserved, rather than deleting the line. - * When adding a field, add it to the end and increment the number by one. - * See https://developers.google.com/protocol-buffers/docs/proto#updating for details - * - * * - * All the messages that are passed between different components/processes are Envelope - * messages. - */ -final class Envelope -{ - public function __construct( - public readonly Attachment $attachment, - - public readonly GherkinDocument $gherkinDocument, - - public readonly Hook $hook, - - public readonly Meta $meta, - - public readonly ParameterType $parameterType, - - public readonly ParseError $parseError, - - public readonly Pickle $pickle, - - public readonly Source $source, - - public readonly StepDefinition $stepDefinition, - - public readonly TestCase $testCase, - - public readonly TestCaseFinished $testCaseFinished, - - public readonly TestCaseStarted $testCaseStarted, - - public readonly TestRunFinished $testRunFinished, - - public readonly TestRunStarted $testRunStarted, - - public readonly TestStepFinished $testStepFinished, - - public readonly TestStepStarted $testStepStarted, - - public readonly UndefinedParameterType $undefinedParameterType, - - ){} -} - - -/** - * Represents the GherkinDocument message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document. - * Cucumber implementations should *not* depend on `GherkinDocument` or any of its - * children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead. - * - * The only consumers of `GherkinDocument` should only be formatters that produce - * "rich" output, resembling the original Gherkin document. - */ -final class GherkinDocument -{ - public function __construct( - /** - * * - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) - * of the source, typically a file path relative to the root directory - */ - public readonly string $uri, - - public readonly Feature $feature, - - /** - * All the comments in the Gherkin document - */ - public readonly array $comments, - - ){} -} - - -/** - * Represents the Background message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Background -{ - public function __construct( - /** - * The location of the `Background` keyword - */ - public readonly Location $location, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - public readonly array $steps, - - public readonly string $id, - - ){} -} - - -/** - * Represents the Comment message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * A comment in a Gherkin document - */ -final class Comment -{ - public function __construct( - /** - * The location of the comment - */ - public readonly Location $location, - - /** - * The text of the comment - */ - public readonly string $text, - - ){} -} - - -/** - * Represents the DataTable message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class DataTable -{ - public function __construct( - public readonly Location $location, - - public readonly array $rows, - - ){} -} - - -/** - * Represents the DocString message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class DocString -{ - public function __construct( - public readonly Location $location, - - public readonly string $mediaType, - - public readonly string $content, - - public readonly string $delimiter, - - ){} -} - - -/** - * Represents the Examples message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Examples -{ - public function __construct( - /** - * The location of the `Examples` keyword - */ - public readonly Location $location, - - public readonly array $tags, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - public readonly TableRow $tableHeader, - - public readonly array $tableBody, - - public readonly string $id, - - ){} -} - - -/** - * Represents the Feature message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Feature -{ - public function __construct( - /** - * The location of the `Feature` keyword - */ - public readonly Location $location, - - /** - * All the tags placed above the `Feature` keyword - */ - public readonly array $tags, - - /** - * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document - */ - public readonly string $language, - - /** - * The text of the `Feature` keyword (in the language specified by `language`) - */ - public readonly string $keyword, - - /** - * The name of the feature (the text following the `keyword`) - */ - public readonly string $name, - - /** - * The line(s) underneath the line with the `keyword` that are used as description - */ - public readonly string $description, - - /** - * Zero or more children - */ - public readonly array $children, - - ){} -} - - -/** - * Represents the FeatureChild message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * A child node of a `Feature` node - */ -final class FeatureChild -{ - public function __construct( - public readonly Rule $rule, - - public readonly Background $background, - - public readonly Scenario $scenario, - - ){} -} - - -/** - * Represents the Rule message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Rule -{ - public function __construct( - /** - * The location of the `Rule` keyword - */ - public readonly Location $location, - - /** - * All the tags placed above the `Rule` keyword - */ - public readonly array $tags, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - public readonly array $children, - - public readonly string $id, - - ){} -} - - -/** - * Represents the RuleChild message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * A child node of a `Rule` node - */ -final class RuleChild -{ - public function __construct( - public readonly Background $background, - - public readonly Scenario $scenario, - - ){} -} - - -/** - * Represents the Scenario message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Scenario -{ - public function __construct( - /** - * The location of the `Scenario` keyword - */ - public readonly Location $location, - - public readonly array $tags, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - public readonly array $steps, - - public readonly array $examples, - - public readonly string $id, - - ){} -} - - -/** - * Represents the Step message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * A step - */ -final class Step -{ - public function __construct( - /** - * The location of the steps' `keyword` - */ - public readonly Location $location, - - public readonly string $keyword, - - public readonly string $text, - - public readonly DocString $docString, - - public readonly DataTable $dataTable, - - /** - * Unique ID to be able to reference the Step from PickleStep - */ - public readonly string $id, - - ){} -} - - -/** - * Represents the TableCell message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * A cell in a `TableRow` - */ -final class TableCell -{ - public function __construct( - /** - * The location of the cell - */ - public readonly Location $location, - - /** - * The value of the cell - */ - public readonly string $value, - - ){} -} - - -/** - * Represents the TableRow message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * A row in a table - */ -final class TableRow -{ - public function __construct( - /** - * The location of the first cell in the row - */ - public readonly Location $location, - - /** - * Cells in the row - */ - public readonly array $cells, - - public readonly string $id, - - ){} -} - - -/** - * Represents the Tag message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * A tag - */ -final class Tag -{ - public function __construct( - /** - * Location of the tag - */ - public readonly Location $location, - - /** - * The name of the tag (including the leading `@`) - */ - public readonly string $name, - - /** - * Unique ID to be able to reference the Tag from PickleTag - */ - public readonly string $id, - - ){} -} - - -/** - * Represents the Hook message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Hook -{ - public function __construct( - public readonly string $id, - - public readonly SourceReference $sourceReference, - - public readonly string $tagExpression, - - ){} -} - - -/** - * Represents the Location message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * Points to a line and a column in a text file - */ -final class Location -{ - public function __construct( - public readonly int $line, - - public readonly int $column, - - ){} -} - - -/** - * Represents the Meta message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * This message contains meta information about the environment. Consumers can use - * this for various purposes. - */ -final class Meta -{ - public function __construct( - /** - * * - * The [SEMVER](https://semver.org/) version number of the protocol - */ - public readonly string $protocolVersion, - - /** - * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. - */ - public readonly Product $implementation, - - /** - * Java, Ruby, Node.js etc - */ - public readonly Product $runtime, - - /** - * Windows, Linux, MacOS etc - */ - public readonly Product $os, - - /** - * 386, arm, amd64 etc - */ - public readonly Product $cpu, - - public readonly Ci $ci, - - ){} -} - - -/** - * Represents the Ci message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * CI environment - */ -final class Ci -{ - public function __construct( - /** - * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. - */ - public readonly string $name, - - /** - * Link to the build - */ - public readonly string $url, - - /** - * The build number. Some CI servers use non-numeric build numbers, which is why this is a string - */ - public readonly string $buildNumber, - - public readonly Git $git, - - ){} -} - - -/** - * Represents the Git message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * Information about Git, provided by the Build/CI server as environment - * variables. - */ -final class Git -{ - public function __construct( - public readonly string $remote, - - public readonly string $revision, - - public readonly string $branch, - - public readonly string $tag, - - ){} -} - - -/** - * Represents the Product message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * Used to describe various properties of Meta - */ -final class Product -{ - public function __construct( - /** - * The product name - */ - public readonly string $name, - - /** - * The product version - */ - public readonly string $version, - - ){} -} - - -/** - * Represents the ParameterType message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class ParameterType -{ - public function __construct( - /** - * The name is unique, so we don't need an id. - */ - public readonly string $name, - - public readonly array $regularExpressions, - - public readonly bool $preferForRegularExpressionMatch, - - public readonly bool $useForSnippets, - - public readonly string $id, - - ){} -} - - -/** - * Represents the ParseError message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class ParseError -{ - public function __construct( - public readonly SourceReference $source, - - public readonly string $message, - - ){} -} - - -/** - * Represents the Pickle message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * //// Pickles - * - * * - * A `Pickle` represents a template for a `TestCase`. It is typically derived - * from another format, such as [GherkinDocument](#io.cucumber.messages.GherkinDocument). - * In the future a `Pickle` may be derived from other formats such as Markdown or - * Excel files. - * - * By making `Pickle` the main data structure Cucumber uses for execution, the - * implementation of Cucumber itself becomes simpler, as it doesn't have to deal - * with the complex structure of a [GherkinDocument](#io.cucumber.messages.GherkinDocument). - * - * Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` - */ -final class Pickle -{ - public function __construct( - /** - * * - * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash - * from the source data and the `locations` of the pickle. - * This ID will change if source the file is modified. - */ - public readonly string $id, - - /** - * The uri of the source file - */ - public readonly string $uri, - - /** - * The name of the pickle - */ - public readonly string $name, - - /** - * The language of the pickle - */ - public readonly string $language, - - /** - * One or more steps - */ - public readonly array $steps, - - /** - * * - * One or more tags. If this pickle is constructed from a Gherkin document, - * It includes inherited tags from the `Feature` as well. - */ - public readonly array $tags, - - /** - * * - * Points to the AST node locations of the pickle. The last one represents the unique - * id of the pickle. A pickle constructed from `Examples` will have the first - * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. - */ - public readonly array $astNodeIds, - - ){} -} - - -/** - * Represents the PickleDocString message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class PickleDocString -{ - public function __construct( - public readonly string $mediaType, - - public readonly string $content, - - ){} -} - - -/** - * Represents the PickleStep message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * An executable step - */ -final class PickleStep -{ - public function __construct( - public readonly PickleStepArgument $argument, - - /** - * References the IDs of the source of the step. For Gherkin, this can be - * the ID of a Step, and possibly also the ID of a TableRow - */ - public readonly array $astNodeIds, - - /** - * A unique ID for the PickleStep - */ - public readonly string $id, - - public readonly string $text, - - ){} -} - - -/** - * Represents the PickleStepArgument message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * An optional argument - */ -final class PickleStepArgument -{ - public function __construct( - public readonly PickleDocString $docString, - - public readonly PickleTable $dataTable, - - ){} -} - - -/** - * Represents the PickleTable message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class PickleTable -{ - public function __construct( - public readonly array $rows, - - ){} -} - - -/** - * Represents the PickleTableCell message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class PickleTableCell -{ - public function __construct( - public readonly string $value, - - ){} -} - - -/** - * Represents the PickleTableRow message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class PickleTableRow -{ - public function __construct( - public readonly array $cells, - - ){} -} - - -/** - * Represents the PickleTag message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * A tag - */ -final class PickleTag -{ - public function __construct( - public readonly string $name, - - /** - * Points to the AST node this was created from - */ - public readonly string $astNodeId, - - ){} -} - - -/** - * Represents the Source message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * //// Source - * - * * - * A source file, typically a Gherkin document or Java/Ruby/JavaScript source code - */ -final class Source -{ - public function __construct( - /** - * * - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) - * of the source, typically a file path relative to the root directory - */ - public readonly string $uri, - - /** - * The contents of the file - */ - public readonly string $data, - - /** - * The media type of the file. Can be used to specify custom types, such as - * text/x.cucumber.gherkin+plain - */ - public readonly Source\MediaType $mediaType, - - ){} -} - - -/** - * Represents the SourceReference message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * Points to a [Source](#io.cucumber.messages.Source) identified by `uri` and a - * [Location](#io.cucumber.messages.Location) within that file. - */ -final class SourceReference -{ - public function __construct( - public readonly string $uri, - - public readonly JavaMethod $javaMethod, - - public readonly JavaStackTraceElement $javaStackTraceElement, - - public readonly Location $location, - - ){} -} - - -/** - * Represents the JavaMethod message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class JavaMethod -{ - public function __construct( - public readonly string $className, - - public readonly string $methodName, - - public readonly array $methodParameterTypes, - - ){} -} - - -/** - * Represents the JavaStackTraceElement message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class JavaStackTraceElement -{ - public function __construct( - public readonly string $className, - - public readonly string $fileName, - - public readonly string $methodName, - - ){} -} - - -/** - * Represents the StepDefinition message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class StepDefinition -{ - public function __construct( - public readonly string $id, - - public readonly StepDefinitionPattern $pattern, - - public readonly SourceReference $sourceReference, - - ){} -} - - -/** - * Represents the StepDefinitionPattern message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class StepDefinitionPattern -{ - public function __construct( - public readonly string $source, - - public readonly StepDefinitionPattern\Type $type, - - ){} -} - - -/** - * Represents the TestCase message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * //// TestCases - * - * * - * A `TestCase` contains a sequence of `TestStep`s. - */ -final class TestCase -{ - public function __construct( - public readonly string $id, - - /** - * The ID of the `Pickle` this `TestCase` is derived from. - */ - public readonly string $pickleId, - - public readonly array $testSteps, - - ){} -} - - -/** - * Represents the Group message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Group -{ - public function __construct( - public readonly array $children, - - public readonly int $start, - - public readonly string $value, - - ){} -} - - -/** - * Represents the StepMatchArgument message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * Represents a single argument extracted from a step match and passed to a step definition. - * This is used for the following purposes: - * - Construct an argument to pass to a step definition (possibly through a parameter type transform) - * - Highlight the matched parameter in rich formatters such as the HTML formatter - * - * This message closely matches the `Argument` class in the `cucumber-expressions` library. - */ -final class StepMatchArgument -{ - public function __construct( - /** - * * - * Represents the outermost capture group of an argument. This message closely matches the - * `Group` class in the `cucumber-expressions` library. - */ - public readonly Group $group, - - public readonly string $parameterTypeName, - - ){} -} - - -/** - * Represents the StepMatchArgumentsList message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class StepMatchArgumentsList -{ - public function __construct( - public readonly array $stepMatchArguments, - - ){} -} - - -/** - * Represents the TestStep message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - * * - * A `TestStep` is derived from either a `PickleStep` - * combined with a `StepDefinition`, or from a `Hook`. - */ -final class TestStep -{ - public function __construct( - /** - * Pointer to the `Hook` (if derived from a Hook) - */ - public readonly string $hookId, - - public readonly string $id, - - /** - * Pointer to the `PickleStep` (if derived from a `PickleStep`) - */ - public readonly string $pickleStepId, - - /** - * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) - */ - public readonly array $stepDefinitionIds, - - /** - * A list of list of StepMatchArgument (if derived from a `PickleStep`). - * Each element represents a matching step definition. A size of 0 means `UNDEFINED`, - * and a size of 2+ means `AMBIGUOUS` - */ - public readonly array $stepMatchArgumentsLists, - - ){} -} - - -/** - * Represents the TestCaseFinished message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestCaseFinished -{ - public function __construct( - public readonly string $testCaseStartedId, - - public readonly Timestamp $timestamp, - - public readonly bool $willBeRetried, - - ){} -} - - -/** - * Represents the TestCaseStarted message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestCaseStarted -{ - public function __construct( - /** - * * - * The first attempt should have value 0, and for each retry the value - * should increase by 1. - */ - public readonly int $attempt, - - /** - * * - * Because a `TestCase` can be run multiple times (in case of a retry), - * we use this field to group messages relating to the same attempt. - */ - public readonly string $id, - - public readonly string $testCaseId, - - public readonly Timestamp $timestamp, - - ){} -} - - -/** - * Represents the TestRunFinished message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestRunFinished -{ - public function __construct( - /** - * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. - * If there are undefined parameter types, the message is simply - * "The following parameter type(s() are not defined: xxx, yyy". - * The independent `UndefinedParameterType` messages can be used to generate - * snippets for those parameter types. - */ - public readonly string $message, - - /** - * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) - */ - public readonly bool $success, - - /** - * Timestamp when the TestRun is finished - */ - public readonly Timestamp $timestamp, - - ){} -} - - -/** - * Represents the TestRunStarted message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestRunStarted -{ - public function __construct( - public readonly Timestamp $timestamp, - - ){} -} - - -/** - * Represents the TestStepFinished message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestStepFinished -{ - public function __construct( - public readonly string $testCaseStartedId, - - public readonly string $testStepId, - - public readonly TestStepResult $testStepResult, - - public readonly Timestamp $timestamp, - - ){} -} - - -/** - * Represents the TestStepResult message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestStepResult -{ - public function __construct( - public readonly Duration $duration, - - public readonly string $message, - - public readonly TestStepResult\Status $status, - - ){} -} - - -/** - * Represents the TestStepStarted message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class TestStepStarted -{ - public function __construct( - public readonly string $testCaseStartedId, - - public readonly string $testStepId, - - public readonly Timestamp $timestamp, - - ){} -} - - -/** - * Represents the Timestamp message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class Timestamp -{ - public function __construct( - /** - * Represents seconds of UTC time since Unix epoch - * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - * 9999-12-31T23:59:59Z inclusive. - */ - public readonly int $seconds, - - /** - * Non-negative fractions of a second at nanosecond resolution. Negative - * second values with fractions must still have non-negative nanos values - * that count forward in time. Must be from 0 to 999,999,999 - * inclusive. - */ - public readonly int $nanos, - - ){} -} - - -/** - * Represents the UndefinedParameterType message in Cucumber's {message protocol}[https://github.com/cucumber/common/tree/main/messages#readme]. - * - - */ -final class UndefinedParameterType -{ - public function __construct( - public readonly string $expression, - - public readonly string $name, - - ){} -} - - diff --git a/messages/php/tests/AcceptanceTest.php b/messages/php/tests/AcceptanceTest.php new file mode 100644 index 00000000000..e9c475f357f --- /dev/null +++ b/messages/php/tests/AcceptanceTest.php @@ -0,0 +1,31 @@ +asJson(); + + self::assertJsonStringEqualsJsonString($json, $newJson); + } + + /** + * @return Generator> + */ + public function provideJson() : Generator + { + $filePattern = __DIR__ . '/../../../compatibility-kit/javascript/features/**/*.ndjson'; + foreach (glob($filePattern) as $filename) { + foreach(file($filename) ?: [] as $lineNumber => $line) { + // key is provided for better error messages + $key = realpath($filename) . ':' . $lineNumber; + yield $key => [$line]; + } + } + } +} From eeb51f2875672ba3e532abd1372c4da6baa88f4d Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Fri, 21 Jan 2022 21:09:17 +0000 Subject: [PATCH 03/37] Additional validation and stricter type checks This raises Psalm's inspection level to 1, which requires many further type checks. These checks will be useful when accepting untrusted content --- messages/jsonschema/scripts/codegen.rb | 3 +- .../jsonschema/scripts/templates/php.php.erb | 42 + messages/php/psalm.xml | 5 +- messages/php/src/DecodingException.php | 10 + messages/php/src/JsonEncodingTrait.php | 19 +- messages/php/src/SchemaViolationException.php | 11 + messages/php/src/UnknownDecodingException.php | 11 + messages/php/src/generated/messages.php | 2813 ++++++++++++++++- 8 files changed, 2887 insertions(+), 27 deletions(-) create mode 100644 messages/php/src/DecodingException.php create mode 100644 messages/php/src/SchemaViolationException.php create mode 100644 messages/php/src/UnknownDecodingException.php diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 131baac13c3..6cef38e4f78 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -370,7 +370,8 @@ def non_nullable_constructor_for(parent_type, property, property_name, schema, a type = type_for(parent_type, property_name, property) if type == 'array' constructor = non_nullable_constructor_for(parent_type, property['items'], nil, schema, "member") - "array_map(fn(mixed $member) => #{constructor} , $#{source})" + member_type = (property['items']['type'] ? 'mixed' : 'array') + "array_map(fn(#{member_type} $member) => #{constructor} , $#{source})" else "#{type_for(parent_type, property_name, property)}::fromArray($#{source})" end diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index fb70636e8d2..adf9fef81b3 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -36,14 +36,56 @@ final class <%= class_name(key) %> implements JsonSerializable ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + <%- schema['properties'].each do |property_name, property| -%> + <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> + self::ensure<%= capitalize(property_name)%>($arr); + <%- end -%> + <%- end -%> + return new self( <%- schema['properties'].each do |property_name, property| -%> <%= constructor_for(key, property, property_name, schema, 'arr') %>, <%- end -%> ); } + <%- schema['properties'].each do |property_name, property| -%> + <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> + + /** + * Check that the type of '<%= property_name %>' matches expectations + * + <%- if (!is_nullable(property_name, schema)) -%> + <%- if is_scalar(property) -%> + * @psalm-assert array{<%= property_name %>: mixed} $arr + <%- else -%> + * @psalm-assert array{<%= property_name %>: array} $arr + <%- end -%> + <%- else -%> + * @psalm-assert array{<%= property_name %>?: array} $arr + <%- end -%> + */ + private static function ensure<%= capitalize(property_name)%>(array $arr): void + { + <%- if (!is_nullable(property_name, schema)) -%> + if (!array_key_exists('<%= property_name %>', $arr)) { + throw new SchemaViolationException('Property \'<%= property_name %>\' is required but was not found'); + } + <%- end -%> + <%- if(!is_scalar(property)) -%> + if (array_key_exists('<%= property_name %>', $arr) && !is_array($arr['<%= property_name %>'])) { + throw new SchemaViolationException('Property \'<%= property_name %>\' was not array'); + } + <%- end -%> + } + <%- end -%> + <%- end -%> } <% end %> diff --git a/messages/php/psalm.xml b/messages/php/psalm.xml index 23ea9bbbd7e..63e8582975f 100644 --- a/messages/php/psalm.xml +++ b/messages/php/psalm.xml @@ -1,6 +1,6 @@ - + + diff --git a/messages/php/src/DecodingException.php b/messages/php/src/DecodingException.php new file mode 100644 index 00000000000..e7a8f6d6632 --- /dev/null +++ b/messages/php/src/DecodingException.php @@ -0,0 +1,10 @@ + Comment::fromArray($member) , $arr['comments']), + array_map(fn(array $member) => Comment::fromArray($member) , $arr['comments']), ); } + + /** + * Check that the type of 'feature' matches expectations + * + * @psalm-assert array{feature?: array} $arr + */ + private static function ensureFeature(array $arr): void + { + if (array_key_exists('feature', $arr) && !is_array($arr['feature'])) { + throw new SchemaViolationException('Property \'feature\' was not array'); + } + } + + /** + * Check that the type of 'comments' matches expectations + * + * @psalm-assert array{comments: array} $arr + */ + private static function ensureComments(array $arr): void + { + if (!array_key_exists('comments', $arr)) { + throw new SchemaViolationException('Property \'comments\' is required but was not found'); + } + if (array_key_exists('comments', $arr) && !is_array($arr['comments'])) { + throw new SchemaViolationException('Property \'comments\' was not array'); + } + } } @@ -285,17 +637,107 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureSteps($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(mixed $member) => Step::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -322,13 +764,48 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureText($arr); + return new self( Location::fromArray($arr['location']), (string) $arr['text'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } } @@ -352,13 +829,51 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureRows($arr); + return new self( Location::fromArray($arr['location']), - array_map(fn(mixed $member) => TableRow::fromArray($member) , $arr['rows']), + array_map(fn(array $member) => TableRow::fromArray($member) , $arr['rows']), ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'rows' matches expectations + * + * @psalm-assert array{rows: array} $arr + */ + private static function ensureRows(array $arr): void + { + if (!array_key_exists('rows', $arr)) { + throw new SchemaViolationException('Property \'rows\' is required but was not found'); + } + if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { + throw new SchemaViolationException('Property \'rows\' was not array'); + } + } } @@ -383,8 +898,17 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureContent($arr); + self::ensureDelimiter($arr); + return new self( Location::fromArray($arr['location']), isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, @@ -392,6 +916,45 @@ public static function fromArray(array $arr) : self (string) $arr['delimiter'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'content' matches expectations + * + * @psalm-assert array{content: mixed} $arr + */ + private static function ensureContent(array $arr): void + { + if (!array_key_exists('content', $arr)) { + throw new SchemaViolationException('Property \'content\' is required but was not found'); + } + } + + /** + * Check that the type of 'delimiter' matches expectations + * + * @psalm-assert array{delimiter: mixed} $arr + */ + private static function ensureDelimiter(array $arr): void + { + if (!array_key_exists('delimiter', $arr)) { + throw new SchemaViolationException('Property \'delimiter\' is required but was not found'); + } + } } @@ -433,19 +996,138 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureTableHeader($arr); + self::ensureTableBody($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), - array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, - array_map(fn(mixed $member) => TableRow::fromArray($member) , $arr['tableBody']), + array_map(fn(array $member) => TableRow::fromArray($member) , $arr['tableBody']), (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'tableHeader' matches expectations + * + * @psalm-assert array{tableHeader?: array} $arr + */ + private static function ensureTableHeader(array $arr): void + { + if (array_key_exists('tableHeader', $arr) && !is_array($arr['tableHeader'])) { + throw new SchemaViolationException('Property \'tableHeader\' was not array'); + } + } + + /** + * Check that the type of 'tableBody' matches expectations + * + * @psalm-assert array{tableBody: array} $arr + */ + private static function ensureTableBody(array $arr): void + { + if (!array_key_exists('tableBody', $arr)) { + throw new SchemaViolationException('Property \'tableBody\' is required but was not found'); + } + if (array_key_exists('tableBody', $arr) && !is_array($arr['tableBody'])) { + throw new SchemaViolationException('Property \'tableBody\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -499,18 +1181,124 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureLanguage($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureChildren($arr); + return new self( Location::fromArray($arr['location']), - array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), (string) $arr['language'], (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(mixed $member) => FeatureChild::fromArray($member) , $arr['children']), + array_map(fn(array $member) => FeatureChild::fromArray($member) , $arr['children']), ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'language' matches expectations + * + * @psalm-assert array{language: mixed} $arr + */ + private static function ensureLanguage(array $arr): void + { + if (!array_key_exists('language', $arr)) { + throw new SchemaViolationException('Property \'language\' is required but was not found'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } } @@ -533,14 +1321,59 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureRule($arr); + self::ensureBackground($arr); + self::ensureScenario($arr); + return new self( isset($arr['rule']) ? Rule::fromArray($arr['rule']) : null, isset($arr['background']) ? Background::fromArray($arr['background']) : null, isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, ); } + + /** + * Check that the type of 'rule' matches expectations + * + * @psalm-assert array{rule?: array} $arr + */ + private static function ensureRule(array $arr): void + { + if (array_key_exists('rule', $arr) && !is_array($arr['rule'])) { + throw new SchemaViolationException('Property \'rule\' was not array'); + } + } + + /** + * Check that the type of 'background' matches expectations + * + * @psalm-assert array{background?: array} $arr + */ + private static function ensureBackground(array $arr): void + { + if (array_key_exists('background', $arr) && !is_array($arr['background'])) { + throw new SchemaViolationException('Property \'background\' was not array'); + } + } + + /** + * Check that the type of 'scenario' matches expectations + * + * @psalm-assert array{scenario?: array} $arr + */ + private static function ensureScenario(array $arr): void + { + if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { + throw new SchemaViolationException('Property \'scenario\' was not array'); + } + } } @@ -581,18 +1414,124 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureChildren($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), - array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(mixed $member) => RuleChild::fromArray($member) , $arr['children']), + array_map(fn(array $member) => RuleChild::fromArray($member) , $arr['children']), (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -613,13 +1552,45 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureBackground($arr); + self::ensureScenario($arr); + return new self( isset($arr['background']) ? Background::fromArray($arr['background']) : null, isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, ); } + + /** + * Check that the type of 'background' matches expectations + * + * @psalm-assert array{background?: array} $arr + */ + private static function ensureBackground(array $arr): void + { + if (array_key_exists('background', $arr) && !is_array($arr['background'])) { + throw new SchemaViolationException('Property \'background\' was not array'); + } + } + + /** + * Check that the type of 'scenario' matches expectations + * + * @psalm-assert array{scenario?: array} $arr + */ + private static function ensureScenario(array $arr): void + { + if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { + throw new SchemaViolationException('Property \'scenario\' was not array'); + } + } } @@ -664,19 +1635,141 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureSteps($arr); + self::ensureExamples($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), - array_map(fn(mixed $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(mixed $member) => Step::fromArray($member) , $arr['steps']), - array_map(fn(mixed $member) => Examples::fromArray($member) , $arr['examples']), + array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => Examples::fromArray($member) , $arr['examples']), (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'examples' matches expectations + * + * @psalm-assert array{examples: array} $arr + */ + private static function ensureExamples(array $arr): void + { + if (!array_key_exists('examples', $arr)) { + throw new SchemaViolationException('Property \'examples\' is required but was not found'); + } + if (array_key_exists('examples', $arr) && !is_array($arr['examples'])) { + throw new SchemaViolationException('Property \'examples\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -711,8 +1804,20 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureKeyword($arr); + self::ensureText($arr); + self::ensureDocString($arr); + self::ensureDataTable($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), (string) $arr['keyword'], @@ -722,6 +1827,81 @@ public static function fromArray(array $arr) : self (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } + + /** + * Check that the type of 'docString' matches expectations + * + * @psalm-assert array{docString?: array} $arr + */ + private static function ensureDocString(array $arr): void + { + if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { + throw new SchemaViolationException('Property \'docString\' was not array'); + } + } + + /** + * Check that the type of 'dataTable' matches expectations + * + * @psalm-assert array{dataTable?: array} $arr + */ + private static function ensureDataTable(array $arr): void + { + if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { + throw new SchemaViolationException('Property \'dataTable\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -748,13 +1928,48 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureValue($arr); + return new self( Location::fromArray($arr['location']), (string) $arr['value'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'value' matches expectations + * + * @psalm-assert array{value: mixed} $arr + */ + private static function ensureValue(array $arr): void + { + if (!array_key_exists('value', $arr)) { + throw new SchemaViolationException('Property \'value\' is required but was not found'); + } + } } @@ -784,14 +1999,65 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureCells($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), - array_map(fn(mixed $member) => TableCell::fromArray($member) , $arr['cells']), + array_map(fn(array $member) => TableCell::fromArray($member) , $arr['cells']), (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'cells' matches expectations + * + * @psalm-assert array{cells: array} $arr + */ + private static function ensureCells(array $arr): void + { + if (!array_key_exists('cells', $arr)) { + throw new SchemaViolationException('Property \'cells\' is required but was not found'); + } + if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { + throw new SchemaViolationException('Property \'cells\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -823,14 +2089,62 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLocation($arr); + self::ensureName($arr); + self::ensureId($arr); + return new self( Location::fromArray($arr['location']), (string) $arr['name'], (string) $arr['id'], ); } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -853,14 +2167,49 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureId($arr); + self::ensureSourceReference($arr); + return new self( (string) $arr['id'], SourceReference::fromArray($arr['sourceReference']), isset($arr['tagExpression']) ? (string) $arr['tagExpression'] : null, ); } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'sourceReference' matches expectations + * + * @psalm-assert array{sourceReference: array} $arr + */ + private static function ensureSourceReference(array $arr): void + { + if (!array_key_exists('sourceReference', $arr)) { + throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); + } + if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { + throw new SchemaViolationException('Property \'sourceReference\' was not array'); + } + } } @@ -881,13 +2230,32 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureLine($arr); + return new self( (int) $arr['line'], isset($arr['column']) ? (int) $arr['column'] : null, ); } + + /** + * Check that the type of 'line' matches expectations + * + * @psalm-assert array{line: mixed} $arr + */ + private static function ensureLine(array $arr): void + { + if (!array_key_exists('line', $arr)) { + throw new SchemaViolationException('Property \'line\' is required but was not found'); + } + } } @@ -932,8 +2300,20 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureProtocolVersion($arr); + self::ensureImplementation($arr); + self::ensureRuntime($arr); + self::ensureOs($arr); + self::ensureCpu($arr); + self::ensureCi($arr); + return new self( (string) $arr['protocolVersion'], Product::fromArray($arr['implementation']), @@ -943,6 +2323,90 @@ public static function fromArray(array $arr) : self isset($arr['ci']) ? Ci::fromArray($arr['ci']) : null, ); } + + /** + * Check that the type of 'protocolVersion' matches expectations + * + * @psalm-assert array{protocolVersion: mixed} $arr + */ + private static function ensureProtocolVersion(array $arr): void + { + if (!array_key_exists('protocolVersion', $arr)) { + throw new SchemaViolationException('Property \'protocolVersion\' is required but was not found'); + } + } + + /** + * Check that the type of 'implementation' matches expectations + * + * @psalm-assert array{implementation: array} $arr + */ + private static function ensureImplementation(array $arr): void + { + if (!array_key_exists('implementation', $arr)) { + throw new SchemaViolationException('Property \'implementation\' is required but was not found'); + } + if (array_key_exists('implementation', $arr) && !is_array($arr['implementation'])) { + throw new SchemaViolationException('Property \'implementation\' was not array'); + } + } + + /** + * Check that the type of 'runtime' matches expectations + * + * @psalm-assert array{runtime: array} $arr + */ + private static function ensureRuntime(array $arr): void + { + if (!array_key_exists('runtime', $arr)) { + throw new SchemaViolationException('Property \'runtime\' is required but was not found'); + } + if (array_key_exists('runtime', $arr) && !is_array($arr['runtime'])) { + throw new SchemaViolationException('Property \'runtime\' was not array'); + } + } + + /** + * Check that the type of 'os' matches expectations + * + * @psalm-assert array{os: array} $arr + */ + private static function ensureOs(array $arr): void + { + if (!array_key_exists('os', $arr)) { + throw new SchemaViolationException('Property \'os\' is required but was not found'); + } + if (array_key_exists('os', $arr) && !is_array($arr['os'])) { + throw new SchemaViolationException('Property \'os\' was not array'); + } + } + + /** + * Check that the type of 'cpu' matches expectations + * + * @psalm-assert array{cpu: array} $arr + */ + private static function ensureCpu(array $arr): void + { + if (!array_key_exists('cpu', $arr)) { + throw new SchemaViolationException('Property \'cpu\' is required but was not found'); + } + if (array_key_exists('cpu', $arr) && !is_array($arr['cpu'])) { + throw new SchemaViolationException('Property \'cpu\' was not array'); + } + } + + /** + * Check that the type of 'ci' matches expectations + * + * @psalm-assert array{ci?: array} $arr + */ + private static function ensureCi(array $arr): void + { + if (array_key_exists('ci', $arr) && !is_array($arr['ci'])) { + throw new SchemaViolationException('Property \'ci\' was not array'); + } + } } @@ -976,8 +2440,16 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureName($arr); + self::ensureGit($arr); + return new self( (string) $arr['name'], isset($arr['url']) ? (string) $arr['url'] : null, @@ -985,6 +2457,30 @@ public static function fromArray(array $arr) : self isset($arr['git']) ? Git::fromArray($arr['git']) : null, ); } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'git' matches expectations + * + * @psalm-assert array{git?: array} $arr + */ + private static function ensureGit(array $arr): void + { + if (array_key_exists('git', $arr) && !is_array($arr['git'])) { + throw new SchemaViolationException('Property \'git\' was not array'); + } + } } @@ -1010,8 +2506,16 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureRemote($arr); + self::ensureRevision($arr); + return new self( (string) $arr['remote'], (string) $arr['revision'], @@ -1019,6 +2523,30 @@ public static function fromArray(array $arr) : self isset($arr['tag']) ? (string) $arr['tag'] : null, ); } + + /** + * Check that the type of 'remote' matches expectations + * + * @psalm-assert array{remote: mixed} $arr + */ + private static function ensureRemote(array $arr): void + { + if (!array_key_exists('remote', $arr)) { + throw new SchemaViolationException('Property \'remote\' is required but was not found'); + } + } + + /** + * Check that the type of 'revision' matches expectations + * + * @psalm-assert array{revision: mixed} $arr + */ + private static function ensureRevision(array $arr): void + { + if (!array_key_exists('revision', $arr)) { + throw new SchemaViolationException('Property \'revision\' is required but was not found'); + } + } } @@ -1045,13 +2573,32 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureName($arr); + return new self( (string) $arr['name'], isset($arr['version']) ? (string) $arr['version'] : null, ); } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } } @@ -1084,8 +2631,19 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureName($arr); + self::ensureRegularExpressions($arr); + self::ensurePreferForRegularExpressionMatch($arr); + self::ensureUseForSnippets($arr); + self::ensureId($arr); + return new self( (string) $arr['name'], array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), @@ -1094,6 +2652,69 @@ public static function fromArray(array $arr) : self (string) $arr['id'], ); } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'regularExpressions' matches expectations + * + * @psalm-assert array{regularExpressions: array} $arr + */ + private static function ensureRegularExpressions(array $arr): void + { + if (!array_key_exists('regularExpressions', $arr)) { + throw new SchemaViolationException('Property \'regularExpressions\' is required but was not found'); + } + if (array_key_exists('regularExpressions', $arr) && !is_array($arr['regularExpressions'])) { + throw new SchemaViolationException('Property \'regularExpressions\' was not array'); + } + } + + /** + * Check that the type of 'preferForRegularExpressionMatch' matches expectations + * + * @psalm-assert array{preferForRegularExpressionMatch: mixed} $arr + */ + private static function ensurePreferForRegularExpressionMatch(array $arr): void + { + if (!array_key_exists('preferForRegularExpressionMatch', $arr)) { + throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' is required but was not found'); + } + } + + /** + * Check that the type of 'useForSnippets' matches expectations + * + * @psalm-assert array{useForSnippets: mixed} $arr + */ + private static function ensureUseForSnippets(array $arr): void + { + if (!array_key_exists('useForSnippets', $arr)) { + throw new SchemaViolationException('Property \'useForSnippets\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -1114,13 +2735,48 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureSource($arr); + self::ensureMessage($arr); + return new self( SourceReference::fromArray($arr['source']), (string) $arr['message'], ); } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source: array} $arr + */ + private static function ensureSource(array $arr): void + { + if (!array_key_exists('source', $arr)) { + throw new SchemaViolationException('Property \'source\' is required but was not found'); + } + if (array_key_exists('source', $arr) && !is_array($arr['source'])) { + throw new SchemaViolationException('Property \'source\' was not array'); + } + } + + /** + * Check that the type of 'message' matches expectations + * + * @psalm-assert array{message: mixed} $arr + */ + private static function ensureMessage(array $arr): void + { + if (!array_key_exists('message', $arr)) { + throw new SchemaViolationException('Property \'message\' is required but was not found'); + } + } } @@ -1191,18 +2847,124 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureId($arr); + self::ensureUri($arr); + self::ensureName($arr); + self::ensureLanguage($arr); + self::ensureSteps($arr); + self::ensureTags($arr); + self::ensureAstNodeIds($arr); + return new self( (string) $arr['id'], (string) $arr['uri'], (string) $arr['name'], (string) $arr['language'], - array_map(fn(mixed $member) => PickleStep::fromArray($member) , $arr['steps']), - array_map(fn(mixed $member) => PickleTag::fromArray($member) , $arr['tags']), + array_map(fn(array $member) => PickleStep::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => PickleTag::fromArray($member) , $arr['tags']), array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), ); } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'uri' matches expectations + * + * @psalm-assert array{uri: mixed} $arr + */ + private static function ensureUri(array $arr): void + { + if (!array_key_exists('uri', $arr)) { + throw new SchemaViolationException('Property \'uri\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'language' matches expectations + * + * @psalm-assert array{language: mixed} $arr + */ + private static function ensureLanguage(array $arr): void + { + if (!array_key_exists('language', $arr)) { + throw new SchemaViolationException('Property \'language\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'astNodeIds' matches expectations + * + * @psalm-assert array{astNodeIds: array} $arr + */ + private static function ensureAstNodeIds(array $arr): void + { + if (!array_key_exists('astNodeIds', $arr)) { + throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); + } + if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { + throw new SchemaViolationException('Property \'astNodeIds\' was not array'); + } + } } @@ -1223,13 +2985,32 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureContent($arr); + return new self( isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, (string) $arr['content'], ); } + + /** + * Check that the type of 'content' matches expectations + * + * @psalm-assert array{content: mixed} $arr + */ + private static function ensureContent(array $arr): void + { + if (!array_key_exists('content', $arr)) { + throw new SchemaViolationException('Property \'content\' is required but was not found'); + } + } } @@ -1262,8 +3043,18 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureArgument($arr); + self::ensureAstNodeIds($arr); + self::ensureId($arr); + self::ensureText($arr); + return new self( isset($arr['argument']) ? PickleStepArgument::fromArray($arr['argument']) : null, array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), @@ -1271,6 +3062,57 @@ public static function fromArray(array $arr) : self (string) $arr['text'], ); } + + /** + * Check that the type of 'argument' matches expectations + * + * @psalm-assert array{argument?: array} $arr + */ + private static function ensureArgument(array $arr): void + { + if (array_key_exists('argument', $arr) && !is_array($arr['argument'])) { + throw new SchemaViolationException('Property \'argument\' was not array'); + } + } + + /** + * Check that the type of 'astNodeIds' matches expectations + * + * @psalm-assert array{astNodeIds: array} $arr + */ + private static function ensureAstNodeIds(array $arr): void + { + if (!array_key_exists('astNodeIds', $arr)) { + throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); + } + if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { + throw new SchemaViolationException('Property \'astNodeIds\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } } @@ -1291,13 +3133,45 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureDocString($arr); + self::ensureDataTable($arr); + return new self( isset($arr['docString']) ? PickleDocString::fromArray($arr['docString']) : null, isset($arr['dataTable']) ? PickleTable::fromArray($arr['dataTable']) : null, ); } + + /** + * Check that the type of 'docString' matches expectations + * + * @psalm-assert array{docString?: array} $arr + */ + private static function ensureDocString(array $arr): void + { + if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { + throw new SchemaViolationException('Property \'docString\' was not array'); + } + } + + /** + * Check that the type of 'dataTable' matches expectations + * + * @psalm-assert array{dataTable?: array} $arr + */ + private static function ensureDataTable(array $arr): void + { + if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { + throw new SchemaViolationException('Property \'dataTable\' was not array'); + } + } } @@ -1319,12 +3193,34 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureRows($arr); + return new self( - array_map(fn(mixed $member) => PickleTableRow::fromArray($member) , $arr['rows']), + array_map(fn(array $member) => PickleTableRow::fromArray($member) , $arr['rows']), ); } + + /** + * Check that the type of 'rows' matches expectations + * + * @psalm-assert array{rows: array} $arr + */ + private static function ensureRows(array $arr): void + { + if (!array_key_exists('rows', $arr)) { + throw new SchemaViolationException('Property \'rows\' is required but was not found'); + } + if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { + throw new SchemaViolationException('Property \'rows\' was not array'); + } + } } @@ -1343,12 +3239,31 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureValue($arr); + return new self( (string) $arr['value'], ); } + + /** + * Check that the type of 'value' matches expectations + * + * @psalm-assert array{value: mixed} $arr + */ + private static function ensureValue(array $arr): void + { + if (!array_key_exists('value', $arr)) { + throw new SchemaViolationException('Property \'value\' is required but was not found'); + } + } } @@ -1370,12 +3285,34 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureCells($arr); + return new self( - array_map(fn(mixed $member) => PickleTableCell::fromArray($member) , $arr['cells']), + array_map(fn(array $member) => PickleTableCell::fromArray($member) , $arr['cells']), ); } + + /** + * Check that the type of 'cells' matches expectations + * + * @psalm-assert array{cells: array} $arr + */ + private static function ensureCells(array $arr): void + { + if (!array_key_exists('cells', $arr)) { + throw new SchemaViolationException('Property \'cells\' is required but was not found'); + } + if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { + throw new SchemaViolationException('Property \'cells\' was not array'); + } + } } @@ -1399,13 +3336,45 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureName($arr); + self::ensureAstNodeId($arr); + return new self( (string) $arr['name'], (string) $arr['astNodeId'], ); } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'astNodeId' matches expectations + * + * @psalm-assert array{astNodeId: mixed} $arr + */ + private static function ensureAstNodeId(array $arr): void + { + if (!array_key_exists('astNodeId', $arr)) { + throw new SchemaViolationException('Property \'astNodeId\' is required but was not found'); + } + } } @@ -1441,14 +3410,59 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureUri($arr); + self::ensureData($arr); + self::ensureMediaType($arr); + return new self( (string) $arr['uri'], (string) $arr['data'], Source\MediaType::from((string) $arr['mediaType']), ); } + + /** + * Check that the type of 'uri' matches expectations + * + * @psalm-assert array{uri: mixed} $arr + */ + private static function ensureUri(array $arr): void + { + if (!array_key_exists('uri', $arr)) { + throw new SchemaViolationException('Property \'uri\' is required but was not found'); + } + } + + /** + * Check that the type of 'data' matches expectations + * + * @psalm-assert array{data: mixed} $arr + */ + private static function ensureData(array $arr): void + { + if (!array_key_exists('data', $arr)) { + throw new SchemaViolationException('Property \'data\' is required but was not found'); + } + } + + /** + * Check that the type of 'mediaType' matches expectations + * + * @psalm-assert array{mediaType: mixed} $arr + */ + private static function ensureMediaType(array $arr): void + { + if (!array_key_exists('mediaType', $arr)) { + throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); + } + } } @@ -1474,8 +3488,17 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureJavaMethod($arr); + self::ensureJavaStackTraceElement($arr); + self::ensureLocation($arr); + return new self( isset($arr['uri']) ? (string) $arr['uri'] : null, isset($arr['javaMethod']) ? JavaMethod::fromArray($arr['javaMethod']) : null, @@ -1483,6 +3506,42 @@ public static function fromArray(array $arr) : self isset($arr['location']) ? Location::fromArray($arr['location']) : null, ); } + + /** + * Check that the type of 'javaMethod' matches expectations + * + * @psalm-assert array{javaMethod?: array} $arr + */ + private static function ensureJavaMethod(array $arr): void + { + if (array_key_exists('javaMethod', $arr) && !is_array($arr['javaMethod'])) { + throw new SchemaViolationException('Property \'javaMethod\' was not array'); + } + } + + /** + * Check that the type of 'javaStackTraceElement' matches expectations + * + * @psalm-assert array{javaStackTraceElement?: array} $arr + */ + private static function ensureJavaStackTraceElement(array $arr): void + { + if (array_key_exists('javaStackTraceElement', $arr) && !is_array($arr['javaStackTraceElement'])) { + throw new SchemaViolationException('Property \'javaStackTraceElement\' was not array'); + } + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location?: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } } @@ -1508,14 +3567,62 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureClassName($arr); + self::ensureMethodName($arr); + self::ensureMethodParameterTypes($arr); + return new self( (string) $arr['className'], (string) $arr['methodName'], array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), ); } + + /** + * Check that the type of 'className' matches expectations + * + * @psalm-assert array{className: mixed} $arr + */ + private static function ensureClassName(array $arr): void + { + if (!array_key_exists('className', $arr)) { + throw new SchemaViolationException('Property \'className\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodName' matches expectations + * + * @psalm-assert array{methodName: mixed} $arr + */ + private static function ensureMethodName(array $arr): void + { + if (!array_key_exists('methodName', $arr)) { + throw new SchemaViolationException('Property \'methodName\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodParameterTypes' matches expectations + * + * @psalm-assert array{methodParameterTypes: array} $arr + */ + private static function ensureMethodParameterTypes(array $arr): void + { + if (!array_key_exists('methodParameterTypes', $arr)) { + throw new SchemaViolationException('Property \'methodParameterTypes\' is required but was not found'); + } + if (array_key_exists('methodParameterTypes', $arr) && !is_array($arr['methodParameterTypes'])) { + throw new SchemaViolationException('Property \'methodParameterTypes\' was not array'); + } + } } @@ -1538,14 +3645,59 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureClassName($arr); + self::ensureFileName($arr); + self::ensureMethodName($arr); + return new self( (string) $arr['className'], (string) $arr['fileName'], (string) $arr['methodName'], ); } + + /** + * Check that the type of 'className' matches expectations + * + * @psalm-assert array{className: mixed} $arr + */ + private static function ensureClassName(array $arr): void + { + if (!array_key_exists('className', $arr)) { + throw new SchemaViolationException('Property \'className\' is required but was not found'); + } + } + + /** + * Check that the type of 'fileName' matches expectations + * + * @psalm-assert array{fileName: mixed} $arr + */ + private static function ensureFileName(array $arr): void + { + if (!array_key_exists('fileName', $arr)) { + throw new SchemaViolationException('Property \'fileName\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodName' matches expectations + * + * @psalm-assert array{methodName: mixed} $arr + */ + private static function ensureMethodName(array $arr): void + { + if (!array_key_exists('methodName', $arr)) { + throw new SchemaViolationException('Property \'methodName\' is required but was not found'); + } + } } @@ -1568,14 +3720,65 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureId($arr); + self::ensurePattern($arr); + self::ensureSourceReference($arr); + return new self( (string) $arr['id'], StepDefinitionPattern::fromArray($arr['pattern']), SourceReference::fromArray($arr['sourceReference']), ); } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'pattern' matches expectations + * + * @psalm-assert array{pattern: array} $arr + */ + private static function ensurePattern(array $arr): void + { + if (!array_key_exists('pattern', $arr)) { + throw new SchemaViolationException('Property \'pattern\' is required but was not found'); + } + if (array_key_exists('pattern', $arr) && !is_array($arr['pattern'])) { + throw new SchemaViolationException('Property \'pattern\' was not array'); + } + } + + /** + * Check that the type of 'sourceReference' matches expectations + * + * @psalm-assert array{sourceReference: array} $arr + */ + private static function ensureSourceReference(array $arr): void + { + if (!array_key_exists('sourceReference', $arr)) { + throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); + } + if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { + throw new SchemaViolationException('Property \'sourceReference\' was not array'); + } + } } @@ -1596,13 +3799,45 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureSource($arr); + self::ensureType($arr); + return new self( (string) $arr['source'], StepDefinitionPattern\Type::from((string) $arr['type']), ); } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source: mixed} $arr + */ + private static function ensureSource(array $arr): void + { + if (!array_key_exists('source', $arr)) { + throw new SchemaViolationException('Property \'source\' is required but was not found'); + } + } + + /** + * Check that the type of 'type' matches expectations + * + * @psalm-assert array{type: mixed} $arr + */ + private static function ensureType(array $arr): void + { + if (!array_key_exists('type', $arr)) { + throw new SchemaViolationException('Property \'type\' is required but was not found'); + } + } } @@ -1633,14 +3868,62 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureId($arr); + self::ensurePickleId($arr); + self::ensureTestSteps($arr); + return new self( (string) $arr['id'], (string) $arr['pickleId'], - array_map(fn(mixed $member) => TestStep::fromArray($member) , $arr['testSteps']), + array_map(fn(array $member) => TestStep::fromArray($member) , $arr['testSteps']), ); } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'pickleId' matches expectations + * + * @psalm-assert array{pickleId: mixed} $arr + */ + private static function ensurePickleId(array $arr): void + { + if (!array_key_exists('pickleId', $arr)) { + throw new SchemaViolationException('Property \'pickleId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testSteps' matches expectations + * + * @psalm-assert array{testSteps: array} $arr + */ + private static function ensureTestSteps(array $arr): void + { + if (!array_key_exists('testSteps', $arr)) { + throw new SchemaViolationException('Property \'testSteps\' is required but was not found'); + } + if (array_key_exists('testSteps', $arr) && !is_array($arr['testSteps'])) { + throw new SchemaViolationException('Property \'testSteps\' was not array'); + } + } } @@ -1666,14 +3949,36 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureChildren($arr); + return new self( - array_map(fn(mixed $member) => Group::fromArray($member) , $arr['children']), + array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), isset($arr['start']) ? (int) $arr['start'] : null, isset($arr['value']) ? (string) $arr['value'] : null, ); } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } } @@ -1703,13 +4008,35 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureGroup($arr); + return new self( Group::fromArray($arr['group']), isset($arr['parameterTypeName']) ? (string) $arr['parameterTypeName'] : null, ); } + + /** + * Check that the type of 'group' matches expectations + * + * @psalm-assert array{group: array} $arr + */ + private static function ensureGroup(array $arr): void + { + if (!array_key_exists('group', $arr)) { + throw new SchemaViolationException('Property \'group\' is required but was not found'); + } + if (array_key_exists('group', $arr) && !is_array($arr['group'])) { + throw new SchemaViolationException('Property \'group\' was not array'); + } + } } @@ -1731,12 +4058,34 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureStepMatchArguments($arr); + return new self( - array_map(fn(mixed $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), + array_map(fn(array $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), ); } + + /** + * Check that the type of 'stepMatchArguments' matches expectations + * + * @psalm-assert array{stepMatchArguments: array} $arr + */ + private static function ensureStepMatchArguments(array $arr): void + { + if (!array_key_exists('stepMatchArguments', $arr)) { + throw new SchemaViolationException('Property \'stepMatchArguments\' is required but was not found'); + } + if (array_key_exists('stepMatchArguments', $arr) && !is_array($arr['stepMatchArguments'])) { + throw new SchemaViolationException('Property \'stepMatchArguments\' was not array'); + } + } } @@ -1780,16 +4129,61 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureId($arr); + self::ensureStepDefinitionIds($arr); + self::ensureStepMatchArgumentsLists($arr); + return new self( isset($arr['hookId']) ? (string) $arr['hookId'] : null, (string) $arr['id'], isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, - isset($arr['stepMatchArgumentsLists']) ? array_map(fn(mixed $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, + isset($arr['stepMatchArgumentsLists']) ? array_map(fn(array $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, ); } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'stepDefinitionIds' matches expectations + * + * @psalm-assert array{stepDefinitionIds?: array} $arr + */ + private static function ensureStepDefinitionIds(array $arr): void + { + if (array_key_exists('stepDefinitionIds', $arr) && !is_array($arr['stepDefinitionIds'])) { + throw new SchemaViolationException('Property \'stepDefinitionIds\' was not array'); + } + } + + /** + * Check that the type of 'stepMatchArgumentsLists' matches expectations + * + * @psalm-assert array{stepMatchArgumentsLists?: array} $arr + */ + private static function ensureStepMatchArgumentsLists(array $arr): void + { + if (array_key_exists('stepMatchArgumentsLists', $arr) && !is_array($arr['stepMatchArgumentsLists'])) { + throw new SchemaViolationException('Property \'stepMatchArgumentsLists\' was not array'); + } + } } @@ -1812,14 +4206,62 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureTestCaseStartedId($arr); + self::ensureTimestamp($arr); + self::ensureWillBeRetried($arr); + return new self( (string) $arr['testCaseStartedId'], Timestamp::fromArray($arr['timestamp']), (bool) $arr['willBeRetried'], ); } + + /** + * Check that the type of 'testCaseStartedId' matches expectations + * + * @psalm-assert array{testCaseStartedId: mixed} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (!array_key_exists('testCaseStartedId', $arr)) { + throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } + + /** + * Check that the type of 'willBeRetried' matches expectations + * + * @psalm-assert array{willBeRetried: mixed} $arr + */ + private static function ensureWillBeRetried(array $arr): void + { + if (!array_key_exists('willBeRetried', $arr)) { + throw new SchemaViolationException('Property \'willBeRetried\' is required but was not found'); + } + } } @@ -1852,8 +4294,18 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureAttempt($arr); + self::ensureId($arr); + self::ensureTestCaseId($arr); + self::ensureTimestamp($arr); + return new self( (int) $arr['attempt'], (string) $arr['id'], @@ -1861,6 +4313,57 @@ public static function fromArray(array $arr) : self Timestamp::fromArray($arr['timestamp']), ); } + + /** + * Check that the type of 'attempt' matches expectations + * + * @psalm-assert array{attempt: mixed} $arr + */ + private static function ensureAttempt(array $arr): void + { + if (!array_key_exists('attempt', $arr)) { + throw new SchemaViolationException('Property \'attempt\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'testCaseId' matches expectations + * + * @psalm-assert array{testCaseId: mixed} $arr + */ + private static function ensureTestCaseId(array $arr): void + { + if (!array_key_exists('testCaseId', $arr)) { + throw new SchemaViolationException('Property \'testCaseId\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -1896,14 +4399,49 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureSuccess($arr); + self::ensureTimestamp($arr); + return new self( isset($arr['message']) ? (string) $arr['message'] : null, (bool) $arr['success'], Timestamp::fromArray($arr['timestamp']), ); } + + /** + * Check that the type of 'success' matches expectations + * + * @psalm-assert array{success: mixed} $arr + */ + private static function ensureSuccess(array $arr): void + { + if (!array_key_exists('success', $arr)) { + throw new SchemaViolationException('Property \'success\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -1922,12 +4460,34 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureTimestamp($arr); + return new self( Timestamp::fromArray($arr['timestamp']), ); } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -1952,8 +4512,18 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureTestCaseStartedId($arr); + self::ensureTestStepId($arr); + self::ensureTestStepResult($arr); + self::ensureTimestamp($arr); + return new self( (string) $arr['testCaseStartedId'], (string) $arr['testStepId'], @@ -1961,6 +4531,60 @@ public static function fromArray(array $arr) : self Timestamp::fromArray($arr['timestamp']), ); } + + /** + * Check that the type of 'testCaseStartedId' matches expectations + * + * @psalm-assert array{testCaseStartedId: mixed} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (!array_key_exists('testCaseStartedId', $arr)) { + throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testStepId' matches expectations + * + * @psalm-assert array{testStepId: mixed} $arr + */ + private static function ensureTestStepId(array $arr): void + { + if (!array_key_exists('testStepId', $arr)) { + throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testStepResult' matches expectations + * + * @psalm-assert array{testStepResult: array} $arr + */ + private static function ensureTestStepResult(array $arr): void + { + if (!array_key_exists('testStepResult', $arr)) { + throw new SchemaViolationException('Property \'testStepResult\' is required but was not found'); + } + if (array_key_exists('testStepResult', $arr) && !is_array($arr['testStepResult'])) { + throw new SchemaViolationException('Property \'testStepResult\' was not array'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -1983,14 +4607,49 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureDuration($arr); + self::ensureStatus($arr); + return new self( Duration::fromArray($arr['duration']), isset($arr['message']) ? (string) $arr['message'] : null, TestStepResult\Status::from((string) $arr['status']), ); } + + /** + * Check that the type of 'duration' matches expectations + * + * @psalm-assert array{duration: array} $arr + */ + private static function ensureDuration(array $arr): void + { + if (!array_key_exists('duration', $arr)) { + throw new SchemaViolationException('Property \'duration\' is required but was not found'); + } + if (array_key_exists('duration', $arr) && !is_array($arr['duration'])) { + throw new SchemaViolationException('Property \'duration\' was not array'); + } + } + + /** + * Check that the type of 'status' matches expectations + * + * @psalm-assert array{status: mixed} $arr + */ + private static function ensureStatus(array $arr): void + { + if (!array_key_exists('status', $arr)) { + throw new SchemaViolationException('Property \'status\' is required but was not found'); + } + } } @@ -2013,14 +4672,62 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureTestCaseStartedId($arr); + self::ensureTestStepId($arr); + self::ensureTimestamp($arr); + return new self( (string) $arr['testCaseStartedId'], (string) $arr['testStepId'], Timestamp::fromArray($arr['timestamp']), ); } + + /** + * Check that the type of 'testCaseStartedId' matches expectations + * + * @psalm-assert array{testCaseStartedId: mixed} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (!array_key_exists('testCaseStartedId', $arr)) { + throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testStepId' matches expectations + * + * @psalm-assert array{testStepId: mixed} $arr + */ + private static function ensureTestStepId(array $arr): void + { + if (!array_key_exists('testStepId', $arr)) { + throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -2052,13 +4759,45 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureSeconds($arr); + self::ensureNanos($arr); + return new self( (int) $arr['seconds'], (int) $arr['nanos'], ); } + + /** + * Check that the type of 'seconds' matches expectations + * + * @psalm-assert array{seconds: mixed} $arr + */ + private static function ensureSeconds(array $arr): void + { + if (!array_key_exists('seconds', $arr)) { + throw new SchemaViolationException('Property \'seconds\' is required but was not found'); + } + } + + /** + * Check that the type of 'nanos' matches expectations + * + * @psalm-assert array{nanos: mixed} $arr + */ + private static function ensureNanos(array $arr): void + { + if (!array_key_exists('nanos', $arr)) { + throw new SchemaViolationException('Property \'nanos\' is required but was not found'); + } + } } @@ -2079,13 +4818,45 @@ private function __construct( ){} + /** + * @throws SchemaViolationException + * + * @internal + */ public static function fromArray(array $arr) : self { + self::ensureExpression($arr); + self::ensureName($arr); + return new self( (string) $arr['expression'], (string) $arr['name'], ); } + + /** + * Check that the type of 'expression' matches expectations + * + * @psalm-assert array{expression: mixed} $arr + */ + private static function ensureExpression(array $arr): void + { + if (!array_key_exists('expression', $arr)) { + throw new SchemaViolationException('Property \'expression\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } } From 42c48df41a9cef785f392085a9becde6e0b17e45 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Fri, 21 Jan 2022 21:45:04 +0000 Subject: [PATCH 04/37] Fix tabs->spaces --- messages/jsonschema/scripts/codegen.rb | 43 +- .../jsonschema/scripts/templates/php.php.erb | 88 +- messages/php/src/generated/messages.php | 5984 ++++++++--------- 3 files changed, 3058 insertions(+), 3057 deletions(-) diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 6cef38e4f78..951042ea255 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -340,42 +340,43 @@ def array_contents_type(parent_type_name, property_name, property) end def is_nullable(property_name, schema) - !(schema['required'] || []).index(property_name) + !(schema['required'] || []).index(property_name) end def is_scalar(property) - property.has_key?('type') && @language_type_by_schema_type.has_key?(property['type']) + property.has_key?('type') && @language_type_by_schema_type.has_key?(property['type']) end def scalar_type_for(property) - raise "No type mapping for JSONSchema type #{type}. Schema:\n#{JSON.pretty_generate(property)}" unless @language_type_by_schema_type[property['type']] - @language_type_by_schema_type[property['type']] + raise "No type mapping for JSONSchema type #{type}. Schema:\n#{JSON.pretty_generate(property)}" unless @language_type_by_schema_type[property['type']] + + @language_type_by_schema_type[property['type']] end def constructor_for(parent_type, property, property_name, schema, arr_name) - constr = non_nullable_constructor_for(parent_type, property, property_name, schema, arr_name) + constr = non_nullable_constructor_for(parent_type, property, property_name, schema, arr_name) - is_nullable(property_name, schema) ? "isset($#{arr_name}['#{property_name}']) ? #{constr} : null" : constr + is_nullable(property_name, schema) ? "isset($#{arr_name}['#{property_name}']) ? #{constr} : null" : constr end def non_nullable_constructor_for(parent_type, property, property_name, schema, arr_name) source = property_name.nil? ? "#{arr_name}" : "#{arr_name}['#{property_name}']" - if is_scalar(property) - if property['enum'] - "#{enum_name(parent_type, property_name, property['enum'])}::from((#{scalar_type_for(property)}) $#{source})" - else - "(#{scalar_type_for(property)}) $#{source}" + if is_scalar(property) + if property['enum'] + "#{enum_name(parent_type, property_name, property['enum'])}::from((#{scalar_type_for(property)}) $#{source})" + else + "(#{scalar_type_for(property)}) $#{source}" + end + else + type = type_for(parent_type, property_name, property) + if type == 'array' + constructor = non_nullable_constructor_for(parent_type, property['items'], nil, schema, "member") + member_type = (property['items']['type'] ? 'mixed' : 'array') + "array_map(fn(#{member_type} $member) => #{constructor} , $#{source})" + else + "#{type_for(parent_type, property_name, property)}::fromArray($#{source})" end - else - type = type_for(parent_type, property_name, property) - if type == 'array' - constructor = non_nullable_constructor_for(parent_type, property['items'], nil, schema, "member") - member_type = (property['items']['type'] ? 'mixed' : 'array') - "array_map(fn(#{member_type} $member) => #{constructor} , $#{source})" - else - "#{type_for(parent_type, property_name, property)}::fromArray($#{source})" - end - end + end end end diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index adf9fef81b3..03fa03c5ac1 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -15,16 +15,16 @@ use \JsonSerializable; <%=- format_description(schema['description'], indent_string: '') -%> */ final class <%= class_name(key) %> implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( <%- schema['properties'].each do |property_name, property| -%> - <%- property_type = type_for(key, property_name, property) -%> - <%- if property['description'] or property_type == 'array' -%> + <%- property_type = type_for(key, property_name, property) -%> + <%- if property['description'] or property_type == 'array' -%> /** <%- if property['description'] -%> - <%= format_description(property['description']) %> + <%= format_description(property['description']) %> <%- end -%> <%- if property_type == 'array' -%> * @param <%= is_nullable(property_name, schema) ? '?' : '' %>list<<%= array_contents_type(key, property_name, property) %>> $<%= property_name %> @@ -43,49 +43,49 @@ final class <%= class_name(key) %> implements JsonSerializable */ public static function fromArray(array $arr) : self { - <%- schema['properties'].each do |property_name, property| -%> - <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> - self::ensure<%= capitalize(property_name)%>($arr); - <%- end -%> - <%- end -%> + <%- schema['properties'].each do |property_name, property| -%> + <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> + self::ensure<%= capitalize(property_name)%>($arr); + <%- end -%> + <%- end -%> - return new self( - <%- schema['properties'].each do |property_name, property| -%> - <%= constructor_for(key, property, property_name, schema, 'arr') %>, - <%- end -%> - ); + return new self( + <%- schema['properties'].each do |property_name, property| -%> + <%= constructor_for(key, property, property_name, schema, 'arr') %>, + <%- end -%> + ); } - <%- schema['properties'].each do |property_name, property| -%> - <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> + <%- schema['properties'].each do |property_name, property| -%> + <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> - /** - * Check that the type of '<%= property_name %>' matches expectations - * - <%- if (!is_nullable(property_name, schema)) -%> - <%- if is_scalar(property) -%> - * @psalm-assert array{<%= property_name %>: mixed} $arr - <%- else -%> - * @psalm-assert array{<%= property_name %>: array} $arr - <%- end -%> - <%- else -%> - * @psalm-assert array{<%= property_name %>?: array} $arr - <%- end -%> - */ - private static function ensure<%= capitalize(property_name)%>(array $arr): void - { - <%- if (!is_nullable(property_name, schema)) -%> - if (!array_key_exists('<%= property_name %>', $arr)) { - throw new SchemaViolationException('Property \'<%= property_name %>\' is required but was not found'); - } - <%- end -%> - <%- if(!is_scalar(property)) -%> - if (array_key_exists('<%= property_name %>', $arr) && !is_array($arr['<%= property_name %>'])) { - throw new SchemaViolationException('Property \'<%= property_name %>\' was not array'); - } - <%- end -%> - } - <%- end -%> - <%- end -%> + /** + * Check that the type of '<%= property_name %>' matches expectations + * + <%- if (!is_nullable(property_name, schema)) -%> + <%- if is_scalar(property) -%> + * @psalm-assert array{<%= property_name %>: mixed} $arr + <%- else -%> + * @psalm-assert array{<%= property_name %>: array} $arr + <%- end -%> + <%- else -%> + * @psalm-assert array{<%= property_name %>?: array} $arr + <%- end -%> + */ + private static function ensure<%= capitalize(property_name)%>(array $arr): void + { + <%- if (!is_nullable(property_name, schema)) -%> + if (!array_key_exists('<%= property_name %>', $arr)) { + throw new SchemaViolationException('Property \'<%= property_name %>\' is required but was not found'); + } + <%- end -%> + <%- if(!is_scalar(property)) -%> + if (array_key_exists('<%= property_name %>', $arr) && !is_array($arr['<%= property_name %>'])) { + throw new SchemaViolationException('Property \'<%= property_name %>\' was not array'); + } + <%- end -%> + } + <%- end -%> + <%- end -%> } <% end %> diff --git a/messages/php/src/generated/messages.php b/messages/php/src/generated/messages.php index cb9b9564241..20131820ed3 100644 --- a/messages/php/src/generated/messages.php +++ b/messages/php/src/generated/messages.php @@ -25,19 +25,19 @@ * is captured in `TestResult`. */ final class Attachment implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The body of the attachment. If `contentEncoding` is `IDENTITY`, the attachment + * The body of the attachment. If `contentEncoding` is `IDENTITY`, the attachment * is simply the string. If it's `BASE64`, the string should be Base64 decoded to * obtain the attachment. */ public readonly string $body, /** - * Whether to interpret `body` "as-is" (IDENTITY) or if it needs to be Base64-decoded (BASE64). + * Whether to interpret `body` "as-is" (IDENTITY) or if it needs to be Base64-decoded (BASE64). * * Content encoding is *not* determined by the media type, but rather by the type * of the object being attached: @@ -49,12 +49,12 @@ private function __construct( public readonly Attachment\ContentEncoding $contentEncoding, /** - * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) + * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) */ public readonly ?string $fileName, /** - * The media type of the data. This can be any valid + * The media type of the data. This can be any valid * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` * and `text/x.cucumber.stacktrace+plain` @@ -68,7 +68,7 @@ private function __construct( public readonly ?string $testStepId, /** - * A URL where the attachment can be retrieved. This field should not be set by Cucumber. + * A URL where the attachment can be retrieved. This field should not be set by Cucumber. * It should be set by a program that reads a message stream and does the following for * each Attachment message: * @@ -91,70 +91,70 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureBody($arr); - self::ensureContentEncoding($arr); - self::ensureMediaType($arr); - self::ensureSource($arr); - - return new self( - (string) $arr['body'], - Attachment\ContentEncoding::from((string) $arr['contentEncoding']), - isset($arr['fileName']) ? (string) $arr['fileName'] : null, - (string) $arr['mediaType'], - isset($arr['source']) ? Source::fromArray($arr['source']) : null, - isset($arr['testCaseStartedId']) ? (string) $arr['testCaseStartedId'] : null, - isset($arr['testStepId']) ? (string) $arr['testStepId'] : null, - isset($arr['url']) ? (string) $arr['url'] : null, - ); - } - - /** - * Check that the type of 'body' matches expectations - * - * @psalm-assert array{body: mixed} $arr - */ - private static function ensureBody(array $arr): void - { - if (!array_key_exists('body', $arr)) { - throw new SchemaViolationException('Property \'body\' is required but was not found'); - } - } - - /** - * Check that the type of 'contentEncoding' matches expectations - * - * @psalm-assert array{contentEncoding: mixed} $arr - */ - private static function ensureContentEncoding(array $arr): void - { - if (!array_key_exists('contentEncoding', $arr)) { - throw new SchemaViolationException('Property \'contentEncoding\' is required but was not found'); - } - } - - /** - * Check that the type of 'mediaType' matches expectations - * - * @psalm-assert array{mediaType: mixed} $arr - */ - private static function ensureMediaType(array $arr): void - { - if (!array_key_exists('mediaType', $arr)) { - throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); - } - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source?: array} $arr - */ - private static function ensureSource(array $arr): void - { - if (array_key_exists('source', $arr) && !is_array($arr['source'])) { - throw new SchemaViolationException('Property \'source\' was not array'); - } - } + self::ensureBody($arr); + self::ensureContentEncoding($arr); + self::ensureMediaType($arr); + self::ensureSource($arr); + + return new self( + (string) $arr['body'], + Attachment\ContentEncoding::from((string) $arr['contentEncoding']), + isset($arr['fileName']) ? (string) $arr['fileName'] : null, + (string) $arr['mediaType'], + isset($arr['source']) ? Source::fromArray($arr['source']) : null, + isset($arr['testCaseStartedId']) ? (string) $arr['testCaseStartedId'] : null, + isset($arr['testStepId']) ? (string) $arr['testStepId'] : null, + isset($arr['url']) ? (string) $arr['url'] : null, + ); + } + + /** + * Check that the type of 'body' matches expectations + * + * @psalm-assert array{body: mixed} $arr + */ + private static function ensureBody(array $arr): void + { + if (!array_key_exists('body', $arr)) { + throw new SchemaViolationException('Property \'body\' is required but was not found'); + } + } + + /** + * Check that the type of 'contentEncoding' matches expectations + * + * @psalm-assert array{contentEncoding: mixed} $arr + */ + private static function ensureContentEncoding(array $arr): void + { + if (!array_key_exists('contentEncoding', $arr)) { + throw new SchemaViolationException('Property \'contentEncoding\' is required but was not found'); + } + } + + /** + * Check that the type of 'mediaType' matches expectations + * + * @psalm-assert array{mediaType: mixed} $arr + */ + private static function ensureMediaType(array $arr): void + { + if (!array_key_exists('mediaType', $arr)) { + throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); + } + } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source?: array} $arr + */ + private static function ensureSource(array $arr): void + { + if (array_key_exists('source', $arr) && !is_array($arr['source'])) { + throw new SchemaViolationException('Property \'source\' was not array'); + } + } } @@ -166,14 +166,14 @@ private static function ensureSource(array $arr): void * of message is used. */ final class Duration implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( public readonly int $seconds, /** - * Non-negative fractions of a second at nanosecond resolution. Negative + * Non-negative fractions of a second at nanosecond resolution. Negative * second values with fractions must still have non-negative nanos values * that count forward in time. Must be from 0 to 999,999,999 * inclusive. @@ -189,38 +189,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureSeconds($arr); - self::ensureNanos($arr); - - return new self( - (int) $arr['seconds'], - (int) $arr['nanos'], - ); - } - - /** - * Check that the type of 'seconds' matches expectations - * - * @psalm-assert array{seconds: mixed} $arr - */ - private static function ensureSeconds(array $arr): void - { - if (!array_key_exists('seconds', $arr)) { - throw new SchemaViolationException('Property \'seconds\' is required but was not found'); - } - } - - /** - * Check that the type of 'nanos' matches expectations - * - * @psalm-assert array{nanos: mixed} $arr - */ - private static function ensureNanos(array $arr): void - { - if (!array_key_exists('nanos', $arr)) { - throw new SchemaViolationException('Property \'nanos\' is required but was not found'); - } - } + self::ensureSeconds($arr); + self::ensureNanos($arr); + + return new self( + (int) $arr['seconds'], + (int) $arr['nanos'], + ); + } + + /** + * Check that the type of 'seconds' matches expectations + * + * @psalm-assert array{seconds: mixed} $arr + */ + private static function ensureSeconds(array $arr): void + { + if (!array_key_exists('seconds', $arr)) { + throw new SchemaViolationException('Property \'seconds\' is required but was not found'); + } + } + + /** + * Check that the type of 'nanos' matches expectations + * + * @psalm-assert array{nanos: mixed} $arr + */ + private static function ensureNanos(array $arr): void + { + if (!array_key_exists('nanos', $arr)) { + throw new SchemaViolationException('Property \'nanos\' is required but was not found'); + } + } } @@ -236,7 +236,7 @@ private static function ensureNanos(array $arr): void * messages. */ final class Envelope implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -283,248 +283,248 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureAttachment($arr); - self::ensureGherkinDocument($arr); - self::ensureHook($arr); - self::ensureMeta($arr); - self::ensureParameterType($arr); - self::ensureParseError($arr); - self::ensurePickle($arr); - self::ensureSource($arr); - self::ensureStepDefinition($arr); - self::ensureTestCase($arr); - self::ensureTestCaseFinished($arr); - self::ensureTestCaseStarted($arr); - self::ensureTestRunFinished($arr); - self::ensureTestRunStarted($arr); - self::ensureTestStepFinished($arr); - self::ensureTestStepStarted($arr); - self::ensureUndefinedParameterType($arr); - - return new self( - isset($arr['attachment']) ? Attachment::fromArray($arr['attachment']) : null, - isset($arr['gherkinDocument']) ? GherkinDocument::fromArray($arr['gherkinDocument']) : null, - isset($arr['hook']) ? Hook::fromArray($arr['hook']) : null, - isset($arr['meta']) ? Meta::fromArray($arr['meta']) : null, - isset($arr['parameterType']) ? ParameterType::fromArray($arr['parameterType']) : null, - isset($arr['parseError']) ? ParseError::fromArray($arr['parseError']) : null, - isset($arr['pickle']) ? Pickle::fromArray($arr['pickle']) : null, - isset($arr['source']) ? Source::fromArray($arr['source']) : null, - isset($arr['stepDefinition']) ? StepDefinition::fromArray($arr['stepDefinition']) : null, - isset($arr['testCase']) ? TestCase::fromArray($arr['testCase']) : null, - isset($arr['testCaseFinished']) ? TestCaseFinished::fromArray($arr['testCaseFinished']) : null, - isset($arr['testCaseStarted']) ? TestCaseStarted::fromArray($arr['testCaseStarted']) : null, - isset($arr['testRunFinished']) ? TestRunFinished::fromArray($arr['testRunFinished']) : null, - isset($arr['testRunStarted']) ? TestRunStarted::fromArray($arr['testRunStarted']) : null, - isset($arr['testStepFinished']) ? TestStepFinished::fromArray($arr['testStepFinished']) : null, - isset($arr['testStepStarted']) ? TestStepStarted::fromArray($arr['testStepStarted']) : null, - isset($arr['undefinedParameterType']) ? UndefinedParameterType::fromArray($arr['undefinedParameterType']) : null, - ); - } - - /** - * Check that the type of 'attachment' matches expectations - * - * @psalm-assert array{attachment?: array} $arr - */ - private static function ensureAttachment(array $arr): void - { - if (array_key_exists('attachment', $arr) && !is_array($arr['attachment'])) { - throw new SchemaViolationException('Property \'attachment\' was not array'); - } - } - - /** - * Check that the type of 'gherkinDocument' matches expectations - * - * @psalm-assert array{gherkinDocument?: array} $arr - */ - private static function ensureGherkinDocument(array $arr): void - { - if (array_key_exists('gherkinDocument', $arr) && !is_array($arr['gherkinDocument'])) { - throw new SchemaViolationException('Property \'gherkinDocument\' was not array'); - } - } - - /** - * Check that the type of 'hook' matches expectations - * - * @psalm-assert array{hook?: array} $arr - */ - private static function ensureHook(array $arr): void - { - if (array_key_exists('hook', $arr) && !is_array($arr['hook'])) { - throw new SchemaViolationException('Property \'hook\' was not array'); - } - } - - /** - * Check that the type of 'meta' matches expectations - * - * @psalm-assert array{meta?: array} $arr - */ - private static function ensureMeta(array $arr): void - { - if (array_key_exists('meta', $arr) && !is_array($arr['meta'])) { - throw new SchemaViolationException('Property \'meta\' was not array'); - } - } - - /** - * Check that the type of 'parameterType' matches expectations - * - * @psalm-assert array{parameterType?: array} $arr - */ - private static function ensureParameterType(array $arr): void - { - if (array_key_exists('parameterType', $arr) && !is_array($arr['parameterType'])) { - throw new SchemaViolationException('Property \'parameterType\' was not array'); - } - } - - /** - * Check that the type of 'parseError' matches expectations - * - * @psalm-assert array{parseError?: array} $arr - */ - private static function ensureParseError(array $arr): void - { - if (array_key_exists('parseError', $arr) && !is_array($arr['parseError'])) { - throw new SchemaViolationException('Property \'parseError\' was not array'); - } - } - - /** - * Check that the type of 'pickle' matches expectations - * - * @psalm-assert array{pickle?: array} $arr - */ - private static function ensurePickle(array $arr): void - { - if (array_key_exists('pickle', $arr) && !is_array($arr['pickle'])) { - throw new SchemaViolationException('Property \'pickle\' was not array'); - } - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source?: array} $arr - */ - private static function ensureSource(array $arr): void - { - if (array_key_exists('source', $arr) && !is_array($arr['source'])) { - throw new SchemaViolationException('Property \'source\' was not array'); - } - } - - /** - * Check that the type of 'stepDefinition' matches expectations - * - * @psalm-assert array{stepDefinition?: array} $arr - */ - private static function ensureStepDefinition(array $arr): void - { - if (array_key_exists('stepDefinition', $arr) && !is_array($arr['stepDefinition'])) { - throw new SchemaViolationException('Property \'stepDefinition\' was not array'); - } - } - - /** - * Check that the type of 'testCase' matches expectations - * - * @psalm-assert array{testCase?: array} $arr - */ - private static function ensureTestCase(array $arr): void - { - if (array_key_exists('testCase', $arr) && !is_array($arr['testCase'])) { - throw new SchemaViolationException('Property \'testCase\' was not array'); - } - } - - /** - * Check that the type of 'testCaseFinished' matches expectations - * - * @psalm-assert array{testCaseFinished?: array} $arr - */ - private static function ensureTestCaseFinished(array $arr): void - { - if (array_key_exists('testCaseFinished', $arr) && !is_array($arr['testCaseFinished'])) { - throw new SchemaViolationException('Property \'testCaseFinished\' was not array'); - } - } - - /** - * Check that the type of 'testCaseStarted' matches expectations - * - * @psalm-assert array{testCaseStarted?: array} $arr - */ - private static function ensureTestCaseStarted(array $arr): void - { - if (array_key_exists('testCaseStarted', $arr) && !is_array($arr['testCaseStarted'])) { - throw new SchemaViolationException('Property \'testCaseStarted\' was not array'); - } - } - - /** - * Check that the type of 'testRunFinished' matches expectations - * - * @psalm-assert array{testRunFinished?: array} $arr - */ - private static function ensureTestRunFinished(array $arr): void - { - if (array_key_exists('testRunFinished', $arr) && !is_array($arr['testRunFinished'])) { - throw new SchemaViolationException('Property \'testRunFinished\' was not array'); - } - } - - /** - * Check that the type of 'testRunStarted' matches expectations - * - * @psalm-assert array{testRunStarted?: array} $arr - */ - private static function ensureTestRunStarted(array $arr): void - { - if (array_key_exists('testRunStarted', $arr) && !is_array($arr['testRunStarted'])) { - throw new SchemaViolationException('Property \'testRunStarted\' was not array'); - } - } - - /** - * Check that the type of 'testStepFinished' matches expectations - * - * @psalm-assert array{testStepFinished?: array} $arr - */ - private static function ensureTestStepFinished(array $arr): void - { - if (array_key_exists('testStepFinished', $arr) && !is_array($arr['testStepFinished'])) { - throw new SchemaViolationException('Property \'testStepFinished\' was not array'); - } - } - - /** - * Check that the type of 'testStepStarted' matches expectations - * - * @psalm-assert array{testStepStarted?: array} $arr - */ - private static function ensureTestStepStarted(array $arr): void - { - if (array_key_exists('testStepStarted', $arr) && !is_array($arr['testStepStarted'])) { - throw new SchemaViolationException('Property \'testStepStarted\' was not array'); - } - } - - /** - * Check that the type of 'undefinedParameterType' matches expectations - * - * @psalm-assert array{undefinedParameterType?: array} $arr - */ - private static function ensureUndefinedParameterType(array $arr): void - { - if (array_key_exists('undefinedParameterType', $arr) && !is_array($arr['undefinedParameterType'])) { - throw new SchemaViolationException('Property \'undefinedParameterType\' was not array'); - } - } + self::ensureAttachment($arr); + self::ensureGherkinDocument($arr); + self::ensureHook($arr); + self::ensureMeta($arr); + self::ensureParameterType($arr); + self::ensureParseError($arr); + self::ensurePickle($arr); + self::ensureSource($arr); + self::ensureStepDefinition($arr); + self::ensureTestCase($arr); + self::ensureTestCaseFinished($arr); + self::ensureTestCaseStarted($arr); + self::ensureTestRunFinished($arr); + self::ensureTestRunStarted($arr); + self::ensureTestStepFinished($arr); + self::ensureTestStepStarted($arr); + self::ensureUndefinedParameterType($arr); + + return new self( + isset($arr['attachment']) ? Attachment::fromArray($arr['attachment']) : null, + isset($arr['gherkinDocument']) ? GherkinDocument::fromArray($arr['gherkinDocument']) : null, + isset($arr['hook']) ? Hook::fromArray($arr['hook']) : null, + isset($arr['meta']) ? Meta::fromArray($arr['meta']) : null, + isset($arr['parameterType']) ? ParameterType::fromArray($arr['parameterType']) : null, + isset($arr['parseError']) ? ParseError::fromArray($arr['parseError']) : null, + isset($arr['pickle']) ? Pickle::fromArray($arr['pickle']) : null, + isset($arr['source']) ? Source::fromArray($arr['source']) : null, + isset($arr['stepDefinition']) ? StepDefinition::fromArray($arr['stepDefinition']) : null, + isset($arr['testCase']) ? TestCase::fromArray($arr['testCase']) : null, + isset($arr['testCaseFinished']) ? TestCaseFinished::fromArray($arr['testCaseFinished']) : null, + isset($arr['testCaseStarted']) ? TestCaseStarted::fromArray($arr['testCaseStarted']) : null, + isset($arr['testRunFinished']) ? TestRunFinished::fromArray($arr['testRunFinished']) : null, + isset($arr['testRunStarted']) ? TestRunStarted::fromArray($arr['testRunStarted']) : null, + isset($arr['testStepFinished']) ? TestStepFinished::fromArray($arr['testStepFinished']) : null, + isset($arr['testStepStarted']) ? TestStepStarted::fromArray($arr['testStepStarted']) : null, + isset($arr['undefinedParameterType']) ? UndefinedParameterType::fromArray($arr['undefinedParameterType']) : null, + ); + } + + /** + * Check that the type of 'attachment' matches expectations + * + * @psalm-assert array{attachment?: array} $arr + */ + private static function ensureAttachment(array $arr): void + { + if (array_key_exists('attachment', $arr) && !is_array($arr['attachment'])) { + throw new SchemaViolationException('Property \'attachment\' was not array'); + } + } + + /** + * Check that the type of 'gherkinDocument' matches expectations + * + * @psalm-assert array{gherkinDocument?: array} $arr + */ + private static function ensureGherkinDocument(array $arr): void + { + if (array_key_exists('gherkinDocument', $arr) && !is_array($arr['gherkinDocument'])) { + throw new SchemaViolationException('Property \'gherkinDocument\' was not array'); + } + } + + /** + * Check that the type of 'hook' matches expectations + * + * @psalm-assert array{hook?: array} $arr + */ + private static function ensureHook(array $arr): void + { + if (array_key_exists('hook', $arr) && !is_array($arr['hook'])) { + throw new SchemaViolationException('Property \'hook\' was not array'); + } + } + + /** + * Check that the type of 'meta' matches expectations + * + * @psalm-assert array{meta?: array} $arr + */ + private static function ensureMeta(array $arr): void + { + if (array_key_exists('meta', $arr) && !is_array($arr['meta'])) { + throw new SchemaViolationException('Property \'meta\' was not array'); + } + } + + /** + * Check that the type of 'parameterType' matches expectations + * + * @psalm-assert array{parameterType?: array} $arr + */ + private static function ensureParameterType(array $arr): void + { + if (array_key_exists('parameterType', $arr) && !is_array($arr['parameterType'])) { + throw new SchemaViolationException('Property \'parameterType\' was not array'); + } + } + + /** + * Check that the type of 'parseError' matches expectations + * + * @psalm-assert array{parseError?: array} $arr + */ + private static function ensureParseError(array $arr): void + { + if (array_key_exists('parseError', $arr) && !is_array($arr['parseError'])) { + throw new SchemaViolationException('Property \'parseError\' was not array'); + } + } + + /** + * Check that the type of 'pickle' matches expectations + * + * @psalm-assert array{pickle?: array} $arr + */ + private static function ensurePickle(array $arr): void + { + if (array_key_exists('pickle', $arr) && !is_array($arr['pickle'])) { + throw new SchemaViolationException('Property \'pickle\' was not array'); + } + } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source?: array} $arr + */ + private static function ensureSource(array $arr): void + { + if (array_key_exists('source', $arr) && !is_array($arr['source'])) { + throw new SchemaViolationException('Property \'source\' was not array'); + } + } + + /** + * Check that the type of 'stepDefinition' matches expectations + * + * @psalm-assert array{stepDefinition?: array} $arr + */ + private static function ensureStepDefinition(array $arr): void + { + if (array_key_exists('stepDefinition', $arr) && !is_array($arr['stepDefinition'])) { + throw new SchemaViolationException('Property \'stepDefinition\' was not array'); + } + } + + /** + * Check that the type of 'testCase' matches expectations + * + * @psalm-assert array{testCase?: array} $arr + */ + private static function ensureTestCase(array $arr): void + { + if (array_key_exists('testCase', $arr) && !is_array($arr['testCase'])) { + throw new SchemaViolationException('Property \'testCase\' was not array'); + } + } + + /** + * Check that the type of 'testCaseFinished' matches expectations + * + * @psalm-assert array{testCaseFinished?: array} $arr + */ + private static function ensureTestCaseFinished(array $arr): void + { + if (array_key_exists('testCaseFinished', $arr) && !is_array($arr['testCaseFinished'])) { + throw new SchemaViolationException('Property \'testCaseFinished\' was not array'); + } + } + + /** + * Check that the type of 'testCaseStarted' matches expectations + * + * @psalm-assert array{testCaseStarted?: array} $arr + */ + private static function ensureTestCaseStarted(array $arr): void + { + if (array_key_exists('testCaseStarted', $arr) && !is_array($arr['testCaseStarted'])) { + throw new SchemaViolationException('Property \'testCaseStarted\' was not array'); + } + } + + /** + * Check that the type of 'testRunFinished' matches expectations + * + * @psalm-assert array{testRunFinished?: array} $arr + */ + private static function ensureTestRunFinished(array $arr): void + { + if (array_key_exists('testRunFinished', $arr) && !is_array($arr['testRunFinished'])) { + throw new SchemaViolationException('Property \'testRunFinished\' was not array'); + } + } + + /** + * Check that the type of 'testRunStarted' matches expectations + * + * @psalm-assert array{testRunStarted?: array} $arr + */ + private static function ensureTestRunStarted(array $arr): void + { + if (array_key_exists('testRunStarted', $arr) && !is_array($arr['testRunStarted'])) { + throw new SchemaViolationException('Property \'testRunStarted\' was not array'); + } + } + + /** + * Check that the type of 'testStepFinished' matches expectations + * + * @psalm-assert array{testStepFinished?: array} $arr + */ + private static function ensureTestStepFinished(array $arr): void + { + if (array_key_exists('testStepFinished', $arr) && !is_array($arr['testStepFinished'])) { + throw new SchemaViolationException('Property \'testStepFinished\' was not array'); + } + } + + /** + * Check that the type of 'testStepStarted' matches expectations + * + * @psalm-assert array{testStepStarted?: array} $arr + */ + private static function ensureTestStepStarted(array $arr): void + { + if (array_key_exists('testStepStarted', $arr) && !is_array($arr['testStepStarted'])) { + throw new SchemaViolationException('Property \'testStepStarted\' was not array'); + } + } + + /** + * Check that the type of 'undefinedParameterType' matches expectations + * + * @psalm-assert array{undefinedParameterType?: array} $arr + */ + private static function ensureUndefinedParameterType(array $arr): void + { + if (array_key_exists('undefinedParameterType', $arr) && !is_array($arr['undefinedParameterType'])) { + throw new SchemaViolationException('Property \'undefinedParameterType\' was not array'); + } + } } @@ -540,12 +540,12 @@ private static function ensureUndefinedParameterType(array $arr): void * "rich" output, resembling the original Gherkin document. */ final class GherkinDocument implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) * of the source, typically a file path relative to the root directory */ public readonly ?string $uri, @@ -553,7 +553,7 @@ private function __construct( public readonly ?Feature $feature, /** - * All the comments in the Gherkin document + * All the comments in the Gherkin document * @param list $comments */ public readonly array $comments, @@ -567,42 +567,42 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureFeature($arr); - self::ensureComments($arr); - - return new self( - isset($arr['uri']) ? (string) $arr['uri'] : null, - isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null, - array_map(fn(array $member) => Comment::fromArray($member) , $arr['comments']), - ); - } - - /** - * Check that the type of 'feature' matches expectations - * - * @psalm-assert array{feature?: array} $arr - */ - private static function ensureFeature(array $arr): void - { - if (array_key_exists('feature', $arr) && !is_array($arr['feature'])) { - throw new SchemaViolationException('Property \'feature\' was not array'); - } - } - - /** - * Check that the type of 'comments' matches expectations - * - * @psalm-assert array{comments: array} $arr - */ - private static function ensureComments(array $arr): void - { - if (!array_key_exists('comments', $arr)) { - throw new SchemaViolationException('Property \'comments\' is required but was not found'); - } - if (array_key_exists('comments', $arr) && !is_array($arr['comments'])) { - throw new SchemaViolationException('Property \'comments\' was not array'); - } - } + self::ensureFeature($arr); + self::ensureComments($arr); + + return new self( + isset($arr['uri']) ? (string) $arr['uri'] : null, + isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null, + array_map(fn(array $member) => Comment::fromArray($member) , $arr['comments']), + ); + } + + /** + * Check that the type of 'feature' matches expectations + * + * @psalm-assert array{feature?: array} $arr + */ + private static function ensureFeature(array $arr): void + { + if (array_key_exists('feature', $arr) && !is_array($arr['feature'])) { + throw new SchemaViolationException('Property \'feature\' was not array'); + } + } + + /** + * Check that the type of 'comments' matches expectations + * + * @psalm-assert array{comments: array} $arr + */ + private static function ensureComments(array $arr): void + { + if (!array_key_exists('comments', $arr)) { + throw new SchemaViolationException('Property \'comments\' is required but was not found'); + } + if (array_key_exists('comments', $arr) && !is_array($arr['comments'])) { + throw new SchemaViolationException('Property \'comments\' was not array'); + } + } } @@ -613,12 +613,12 @@ private static function ensureComments(array $arr): void */ final class Background implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the `Background` keyword + * The location of the `Background` keyword */ public readonly Location $location, @@ -644,100 +644,100 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureSteps($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'steps' matches expectations - * - * @psalm-assert array{steps: array} $arr - */ - private static function ensureSteps(array $arr): void - { - if (!array_key_exists('steps', $arr)) { - throw new SchemaViolationException('Property \'steps\' is required but was not found'); - } - if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { - throw new SchemaViolationException('Property \'steps\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureSteps($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -748,17 +748,17 @@ private static function ensureId(array $arr): void * A comment in a Gherkin document */ final class Comment implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the comment + * The location of the comment */ public readonly Location $location, /** - * The text of the comment + * The text of the comment */ public readonly string $text, @@ -771,41 +771,41 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureText($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['text'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr - */ - private static function ensureText(array $arr): void - { - if (!array_key_exists('text', $arr)) { - throw new SchemaViolationException('Property \'text\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureText($arr); + + return new self( + Location::fromArray($arr['location']), + (string) $arr['text'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } } @@ -816,7 +816,7 @@ private static function ensureText(array $arr): void */ final class DataTable implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -836,44 +836,44 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureRows($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => TableRow::fromArray($member) , $arr['rows']), - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'rows' matches expectations - * - * @psalm-assert array{rows: array} $arr - */ - private static function ensureRows(array $arr): void - { - if (!array_key_exists('rows', $arr)) { - throw new SchemaViolationException('Property \'rows\' is required but was not found'); - } - if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { - throw new SchemaViolationException('Property \'rows\' was not array'); - } - } + self::ensureLocation($arr); + self::ensureRows($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => TableRow::fromArray($member) , $arr['rows']), + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'rows' matches expectations + * + * @psalm-assert array{rows: array} $arr + */ + private static function ensureRows(array $arr): void + { + if (!array_key_exists('rows', $arr)) { + throw new SchemaViolationException('Property \'rows\' is required but was not found'); + } + if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { + throw new SchemaViolationException('Property \'rows\' was not array'); + } + } } @@ -884,7 +884,7 @@ private static function ensureRows(array $arr): void */ final class DocString implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -905,56 +905,56 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureContent($arr); - self::ensureDelimiter($arr); - - return new self( - Location::fromArray($arr['location']), - isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, - (string) $arr['content'], - (string) $arr['delimiter'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'content' matches expectations - * - * @psalm-assert array{content: mixed} $arr - */ - private static function ensureContent(array $arr): void - { - if (!array_key_exists('content', $arr)) { - throw new SchemaViolationException('Property \'content\' is required but was not found'); - } - } - - /** - * Check that the type of 'delimiter' matches expectations - * - * @psalm-assert array{delimiter: mixed} $arr - */ - private static function ensureDelimiter(array $arr): void - { - if (!array_key_exists('delimiter', $arr)) { - throw new SchemaViolationException('Property \'delimiter\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureContent($arr); + self::ensureDelimiter($arr); + + return new self( + Location::fromArray($arr['location']), + isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, + (string) $arr['content'], + (string) $arr['delimiter'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'content' matches expectations + * + * @psalm-assert array{content: mixed} $arr + */ + private static function ensureContent(array $arr): void + { + if (!array_key_exists('content', $arr)) { + throw new SchemaViolationException('Property \'content\' is required but was not found'); + } + } + + /** + * Check that the type of 'delimiter' matches expectations + * + * @psalm-assert array{delimiter: mixed} $arr + */ + private static function ensureDelimiter(array $arr): void + { + if (!array_key_exists('delimiter', $arr)) { + throw new SchemaViolationException('Property \'delimiter\' is required but was not found'); + } + } } @@ -965,12 +965,12 @@ private static function ensureDelimiter(array $arr): void */ final class Examples implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the `Examples` keyword + * The location of the `Examples` keyword */ public readonly Location $location, @@ -1003,131 +1003,131 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureTableHeader($arr); - self::ensureTableBody($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, - array_map(fn(array $member) => TableRow::fromArray($member) , $arr['tableBody']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'tableHeader' matches expectations - * - * @psalm-assert array{tableHeader?: array} $arr - */ - private static function ensureTableHeader(array $arr): void - { - if (array_key_exists('tableHeader', $arr) && !is_array($arr['tableHeader'])) { - throw new SchemaViolationException('Property \'tableHeader\' was not array'); - } - } - - /** - * Check that the type of 'tableBody' matches expectations - * - * @psalm-assert array{tableBody: array} $arr - */ - private static function ensureTableBody(array $arr): void - { - if (!array_key_exists('tableBody', $arr)) { - throw new SchemaViolationException('Property \'tableBody\' is required but was not found'); - } - if (array_key_exists('tableBody', $arr) && !is_array($arr['tableBody'])) { - throw new SchemaViolationException('Property \'tableBody\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureTableHeader($arr); + self::ensureTableBody($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, + array_map(fn(array $member) => TableRow::fromArray($member) , $arr['tableBody']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'tableHeader' matches expectations + * + * @psalm-assert array{tableHeader?: array} $arr + */ + private static function ensureTableHeader(array $arr): void + { + if (array_key_exists('tableHeader', $arr) && !is_array($arr['tableHeader'])) { + throw new SchemaViolationException('Property \'tableHeader\' was not array'); + } + } + + /** + * Check that the type of 'tableBody' matches expectations + * + * @psalm-assert array{tableBody: array} $arr + */ + private static function ensureTableBody(array $arr): void + { + if (!array_key_exists('tableBody', $arr)) { + throw new SchemaViolationException('Property \'tableBody\' is required but was not found'); + } + if (array_key_exists('tableBody', $arr) && !is_array($arr['tableBody'])) { + throw new SchemaViolationException('Property \'tableBody\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -1138,43 +1138,43 @@ private static function ensureId(array $arr): void */ final class Feature implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the `Feature` keyword + * The location of the `Feature` keyword */ public readonly Location $location, /** - * All the tags placed above the `Feature` keyword + * All the tags placed above the `Feature` keyword * @param list $tags */ public readonly array $tags, /** - * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document + * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document */ public readonly string $language, /** - * The text of the `Feature` keyword (in the language specified by `language`) + * The text of the `Feature` keyword (in the language specified by `language`) */ public readonly string $keyword, /** - * The name of the feature (the text following the `keyword`) + * The name of the feature (the text following the `keyword`) */ public readonly string $name, /** - * The line(s) underneath the line with the `keyword` that are used as description + * The line(s) underneath the line with the `keyword` that are used as description */ public readonly string $description, /** - * Zero or more children + * Zero or more children * @param list $children */ public readonly array $children, @@ -1188,117 +1188,117 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureLanguage($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureChildren($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['language'], - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => FeatureChild::fromArray($member) , $arr['children']), - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'language' matches expectations - * - * @psalm-assert array{language: mixed} $arr - */ - private static function ensureLanguage(array $arr): void - { - if (!array_key_exists('language', $arr)) { - throw new SchemaViolationException('Property \'language\' is required but was not found'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'children' matches expectations - * - * @psalm-assert array{children: array} $arr - */ - private static function ensureChildren(array $arr): void - { - if (!array_key_exists('children', $arr)) { - throw new SchemaViolationException('Property \'children\' is required but was not found'); - } - if (array_key_exists('children', $arr) && !is_array($arr['children'])) { - throw new SchemaViolationException('Property \'children\' was not array'); - } - } + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureLanguage($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureChildren($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['language'], + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => FeatureChild::fromArray($member) , $arr['children']), + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'language' matches expectations + * + * @psalm-assert array{language: mixed} $arr + */ + private static function ensureLanguage(array $arr): void + { + if (!array_key_exists('language', $arr)) { + throw new SchemaViolationException('Property \'language\' is required but was not found'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } } @@ -1309,7 +1309,7 @@ private static function ensureChildren(array $arr): void * A child node of a `Feature` node */ final class FeatureChild implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -1328,52 +1328,52 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureRule($arr); - self::ensureBackground($arr); - self::ensureScenario($arr); - - return new self( - isset($arr['rule']) ? Rule::fromArray($arr['rule']) : null, - isset($arr['background']) ? Background::fromArray($arr['background']) : null, - isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, - ); - } - - /** - * Check that the type of 'rule' matches expectations - * - * @psalm-assert array{rule?: array} $arr - */ - private static function ensureRule(array $arr): void - { - if (array_key_exists('rule', $arr) && !is_array($arr['rule'])) { - throw new SchemaViolationException('Property \'rule\' was not array'); - } - } - - /** - * Check that the type of 'background' matches expectations - * - * @psalm-assert array{background?: array} $arr - */ - private static function ensureBackground(array $arr): void - { - if (array_key_exists('background', $arr) && !is_array($arr['background'])) { - throw new SchemaViolationException('Property \'background\' was not array'); - } - } - - /** - * Check that the type of 'scenario' matches expectations - * - * @psalm-assert array{scenario?: array} $arr - */ - private static function ensureScenario(array $arr): void - { - if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { - throw new SchemaViolationException('Property \'scenario\' was not array'); - } - } + self::ensureRule($arr); + self::ensureBackground($arr); + self::ensureScenario($arr); + + return new self( + isset($arr['rule']) ? Rule::fromArray($arr['rule']) : null, + isset($arr['background']) ? Background::fromArray($arr['background']) : null, + isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, + ); + } + + /** + * Check that the type of 'rule' matches expectations + * + * @psalm-assert array{rule?: array} $arr + */ + private static function ensureRule(array $arr): void + { + if (array_key_exists('rule', $arr) && !is_array($arr['rule'])) { + throw new SchemaViolationException('Property \'rule\' was not array'); + } + } + + /** + * Check that the type of 'background' matches expectations + * + * @psalm-assert array{background?: array} $arr + */ + private static function ensureBackground(array $arr): void + { + if (array_key_exists('background', $arr) && !is_array($arr['background'])) { + throw new SchemaViolationException('Property \'background\' was not array'); + } + } + + /** + * Check that the type of 'scenario' matches expectations + * + * @psalm-assert array{scenario?: array} $arr + */ + private static function ensureScenario(array $arr): void + { + if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { + throw new SchemaViolationException('Property \'scenario\' was not array'); + } + } } @@ -1384,17 +1384,17 @@ private static function ensureScenario(array $arr): void */ final class Rule implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the `Rule` keyword + * The location of the `Rule` keyword */ public readonly Location $location, /** - * All the tags placed above the `Rule` keyword + * All the tags placed above the `Rule` keyword * @param list $tags */ public readonly array $tags, @@ -1410,128 +1410,128 @@ private function __construct( */ public readonly array $children, - public readonly string $id, + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureChildren($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => RuleChild::fromArray($member) , $arr['children']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } - ){} + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } /** - * @throws SchemaViolationException + * Check that the type of 'id' matches expectations * - * @internal + * @psalm-assert array{id: mixed} $arr */ - public static function fromArray(array $arr) : self + private static function ensureId(array $arr): void { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureChildren($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => RuleChild::fromArray($member) , $arr['children']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'children' matches expectations - * - * @psalm-assert array{children: array} $arr - */ - private static function ensureChildren(array $arr): void - { - if (!array_key_exists('children', $arr)) { - throw new SchemaViolationException('Property \'children\' is required but was not found'); - } - if (array_key_exists('children', $arr) && !is_array($arr['children'])) { - throw new SchemaViolationException('Property \'children\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -1542,7 +1542,7 @@ private static function ensureId(array $arr): void * A child node of a `Rule` node */ final class RuleChild implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -1559,38 +1559,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureBackground($arr); - self::ensureScenario($arr); - - return new self( - isset($arr['background']) ? Background::fromArray($arr['background']) : null, - isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, - ); - } - - /** - * Check that the type of 'background' matches expectations - * - * @psalm-assert array{background?: array} $arr - */ - private static function ensureBackground(array $arr): void - { - if (array_key_exists('background', $arr) && !is_array($arr['background'])) { - throw new SchemaViolationException('Property \'background\' was not array'); - } - } - - /** - * Check that the type of 'scenario' matches expectations - * - * @psalm-assert array{scenario?: array} $arr - */ - private static function ensureScenario(array $arr): void - { - if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { - throw new SchemaViolationException('Property \'scenario\' was not array'); - } - } + self::ensureBackground($arr); + self::ensureScenario($arr); + + return new self( + isset($arr['background']) ? Background::fromArray($arr['background']) : null, + isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, + ); + } + + /** + * Check that the type of 'background' matches expectations + * + * @psalm-assert array{background?: array} $arr + */ + private static function ensureBackground(array $arr): void + { + if (array_key_exists('background', $arr) && !is_array($arr['background'])) { + throw new SchemaViolationException('Property \'background\' was not array'); + } + } + + /** + * Check that the type of 'scenario' matches expectations + * + * @psalm-assert array{scenario?: array} $arr + */ + private static function ensureScenario(array $arr): void + { + if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { + throw new SchemaViolationException('Property \'scenario\' was not array'); + } + } } @@ -1601,12 +1601,12 @@ private static function ensureScenario(array $arr): void */ final class Scenario implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the `Scenario` keyword + * The location of the `Scenario` keyword */ public readonly Location $location, @@ -1642,134 +1642,134 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureSteps($arr); - self::ensureExamples($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), - array_map(fn(array $member) => Examples::fromArray($member) , $arr['examples']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'steps' matches expectations - * - * @psalm-assert array{steps: array} $arr - */ - private static function ensureSteps(array $arr): void - { - if (!array_key_exists('steps', $arr)) { - throw new SchemaViolationException('Property \'steps\' is required but was not found'); - } - if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { - throw new SchemaViolationException('Property \'steps\' was not array'); - } - } - - /** - * Check that the type of 'examples' matches expectations - * - * @psalm-assert array{examples: array} $arr - */ - private static function ensureExamples(array $arr): void - { - if (!array_key_exists('examples', $arr)) { - throw new SchemaViolationException('Property \'examples\' is required but was not found'); - } - if (array_key_exists('examples', $arr) && !is_array($arr['examples'])) { - throw new SchemaViolationException('Property \'examples\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureSteps($arr); + self::ensureExamples($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => Examples::fromArray($member) , $arr['examples']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'examples' matches expectations + * + * @psalm-assert array{examples: array} $arr + */ + private static function ensureExamples(array $arr): void + { + if (!array_key_exists('examples', $arr)) { + throw new SchemaViolationException('Property \'examples\' is required but was not found'); + } + if (array_key_exists('examples', $arr) && !is_array($arr['examples'])) { + throw new SchemaViolationException('Property \'examples\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -1780,12 +1780,12 @@ private static function ensureId(array $arr): void * A step */ final class Step implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the steps' `keyword` + * The location of the steps' `keyword` */ public readonly Location $location, @@ -1798,7 +1798,7 @@ private function __construct( public readonly ?DataTable $dataTable, /** - * Unique ID to be able to reference the Step from PickleStep + * Unique ID to be able to reference the Step from PickleStep */ public readonly string $id, @@ -1811,97 +1811,97 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureKeyword($arr); - self::ensureText($arr); - self::ensureDocString($arr); - self::ensureDataTable($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['keyword'], - (string) $arr['text'], - isset($arr['docString']) ? DocString::fromArray($arr['docString']) : null, - isset($arr['dataTable']) ? DataTable::fromArray($arr['dataTable']) : null, - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr - */ - private static function ensureText(array $arr): void - { - if (!array_key_exists('text', $arr)) { - throw new SchemaViolationException('Property \'text\' is required but was not found'); - } - } - - /** - * Check that the type of 'docString' matches expectations - * - * @psalm-assert array{docString?: array} $arr - */ - private static function ensureDocString(array $arr): void - { - if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { - throw new SchemaViolationException('Property \'docString\' was not array'); - } - } - - /** - * Check that the type of 'dataTable' matches expectations - * - * @psalm-assert array{dataTable?: array} $arr - */ - private static function ensureDataTable(array $arr): void - { - if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { - throw new SchemaViolationException('Property \'dataTable\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureKeyword($arr); + self::ensureText($arr); + self::ensureDocString($arr); + self::ensureDataTable($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + (string) $arr['keyword'], + (string) $arr['text'], + isset($arr['docString']) ? DocString::fromArray($arr['docString']) : null, + isset($arr['dataTable']) ? DataTable::fromArray($arr['dataTable']) : null, + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } + + /** + * Check that the type of 'docString' matches expectations + * + * @psalm-assert array{docString?: array} $arr + */ + private static function ensureDocString(array $arr): void + { + if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { + throw new SchemaViolationException('Property \'docString\' was not array'); + } + } + + /** + * Check that the type of 'dataTable' matches expectations + * + * @psalm-assert array{dataTable?: array} $arr + */ + private static function ensureDataTable(array $arr): void + { + if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { + throw new SchemaViolationException('Property \'dataTable\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -1912,17 +1912,17 @@ private static function ensureId(array $arr): void * A cell in a `TableRow` */ final class TableCell implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the cell + * The location of the cell */ public readonly Location $location, /** - * The value of the cell + * The value of the cell */ public readonly string $value, @@ -1935,41 +1935,41 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureValue($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['value'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'value' matches expectations - * - * @psalm-assert array{value: mixed} $arr - */ - private static function ensureValue(array $arr): void - { - if (!array_key_exists('value', $arr)) { - throw new SchemaViolationException('Property \'value\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureValue($arr); + + return new self( + Location::fromArray($arr['location']), + (string) $arr['value'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'value' matches expectations + * + * @psalm-assert array{value: mixed} $arr + */ + private static function ensureValue(array $arr): void + { + if (!array_key_exists('value', $arr)) { + throw new SchemaViolationException('Property \'value\' is required but was not found'); + } + } } @@ -1980,17 +1980,17 @@ private static function ensureValue(array $arr): void * A row in a table */ final class TableRow implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The location of the first cell in the row + * The location of the first cell in the row */ public readonly Location $location, /** - * Cells in the row + * Cells in the row * @param list $cells */ public readonly array $cells, @@ -2006,58 +2006,58 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureCells($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => TableCell::fromArray($member) , $arr['cells']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'cells' matches expectations - * - * @psalm-assert array{cells: array} $arr - */ - private static function ensureCells(array $arr): void - { - if (!array_key_exists('cells', $arr)) { - throw new SchemaViolationException('Property \'cells\' is required but was not found'); - } - if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { - throw new SchemaViolationException('Property \'cells\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureCells($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => TableCell::fromArray($member) , $arr['cells']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'cells' matches expectations + * + * @psalm-assert array{cells: array} $arr + */ + private static function ensureCells(array $arr): void + { + if (!array_key_exists('cells', $arr)) { + throw new SchemaViolationException('Property \'cells\' is required but was not found'); + } + if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { + throw new SchemaViolationException('Property \'cells\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -2068,22 +2068,22 @@ private static function ensureId(array $arr): void * A tag */ final class Tag implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * Location of the tag + * Location of the tag */ public readonly Location $location, /** - * The name of the tag (including the leading `@`) + * The name of the tag (including the leading `@`) */ public readonly string $name, /** - * Unique ID to be able to reference the Tag from PickleTag + * Unique ID to be able to reference the Tag from PickleTag */ public readonly string $id, @@ -2096,55 +2096,55 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLocation($arr); - self::ensureName($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['name'], - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureLocation($arr); + self::ensureName($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + (string) $arr['name'], + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -2155,7 +2155,7 @@ private static function ensureId(array $arr): void */ final class Hook implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -2174,42 +2174,42 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureId($arr); - self::ensureSourceReference($arr); - - return new self( - (string) $arr['id'], - SourceReference::fromArray($arr['sourceReference']), - isset($arr['tagExpression']) ? (string) $arr['tagExpression'] : null, - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'sourceReference' matches expectations - * - * @psalm-assert array{sourceReference: array} $arr - */ - private static function ensureSourceReference(array $arr): void - { - if (!array_key_exists('sourceReference', $arr)) { - throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); - } - if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { - throw new SchemaViolationException('Property \'sourceReference\' was not array'); - } - } + self::ensureId($arr); + self::ensureSourceReference($arr); + + return new self( + (string) $arr['id'], + SourceReference::fromArray($arr['sourceReference']), + isset($arr['tagExpression']) ? (string) $arr['tagExpression'] : null, + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'sourceReference' matches expectations + * + * @psalm-assert array{sourceReference: array} $arr + */ + private static function ensureSourceReference(array $arr): void + { + if (!array_key_exists('sourceReference', $arr)) { + throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); + } + if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { + throw new SchemaViolationException('Property \'sourceReference\' was not array'); + } + } } @@ -2220,7 +2220,7 @@ private static function ensureSourceReference(array $arr): void * Points to a line and a column in a text file */ final class Location implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -2237,25 +2237,25 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureLine($arr); - - return new self( - (int) $arr['line'], - isset($arr['column']) ? (int) $arr['column'] : null, - ); - } - - /** - * Check that the type of 'line' matches expectations - * - * @psalm-assert array{line: mixed} $arr - */ - private static function ensureLine(array $arr): void - { - if (!array_key_exists('line', $arr)) { - throw new SchemaViolationException('Property \'line\' is required but was not found'); - } - } + self::ensureLine($arr); + + return new self( + (int) $arr['line'], + isset($arr['column']) ? (int) $arr['column'] : null, + ); + } + + /** + * Check that the type of 'line' matches expectations + * + * @psalm-assert array{line: mixed} $arr + */ + private static function ensureLine(array $arr): void + { + if (!array_key_exists('line', $arr)) { + throw new SchemaViolationException('Property \'line\' is required but was not found'); + } + } } @@ -2267,32 +2267,32 @@ private static function ensureLine(array $arr): void * this for various purposes. */ final class Meta implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The [SEMVER](https://semver.org/) version number of the protocol + * The [SEMVER](https://semver.org/) version number of the protocol */ public readonly string $protocolVersion, /** - * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. + * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. */ public readonly Product $implementation, /** - * Java, Ruby, Node.js etc + * Java, Ruby, Node.js etc */ public readonly Product $runtime, /** - * Windows, Linux, MacOS etc + * Windows, Linux, MacOS etc */ public readonly Product $os, /** - * 386, arm, amd64 etc + * 386, arm, amd64 etc */ public readonly Product $cpu, @@ -2307,106 +2307,106 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureProtocolVersion($arr); - self::ensureImplementation($arr); - self::ensureRuntime($arr); - self::ensureOs($arr); - self::ensureCpu($arr); - self::ensureCi($arr); - - return new self( - (string) $arr['protocolVersion'], - Product::fromArray($arr['implementation']), - Product::fromArray($arr['runtime']), - Product::fromArray($arr['os']), - Product::fromArray($arr['cpu']), - isset($arr['ci']) ? Ci::fromArray($arr['ci']) : null, - ); - } - - /** - * Check that the type of 'protocolVersion' matches expectations - * - * @psalm-assert array{protocolVersion: mixed} $arr - */ - private static function ensureProtocolVersion(array $arr): void - { - if (!array_key_exists('protocolVersion', $arr)) { - throw new SchemaViolationException('Property \'protocolVersion\' is required but was not found'); - } - } - - /** - * Check that the type of 'implementation' matches expectations - * - * @psalm-assert array{implementation: array} $arr - */ - private static function ensureImplementation(array $arr): void - { - if (!array_key_exists('implementation', $arr)) { - throw new SchemaViolationException('Property \'implementation\' is required but was not found'); - } - if (array_key_exists('implementation', $arr) && !is_array($arr['implementation'])) { - throw new SchemaViolationException('Property \'implementation\' was not array'); - } - } - - /** - * Check that the type of 'runtime' matches expectations - * - * @psalm-assert array{runtime: array} $arr - */ - private static function ensureRuntime(array $arr): void - { - if (!array_key_exists('runtime', $arr)) { - throw new SchemaViolationException('Property \'runtime\' is required but was not found'); - } - if (array_key_exists('runtime', $arr) && !is_array($arr['runtime'])) { - throw new SchemaViolationException('Property \'runtime\' was not array'); - } - } - - /** - * Check that the type of 'os' matches expectations - * - * @psalm-assert array{os: array} $arr - */ - private static function ensureOs(array $arr): void - { - if (!array_key_exists('os', $arr)) { - throw new SchemaViolationException('Property \'os\' is required but was not found'); - } - if (array_key_exists('os', $arr) && !is_array($arr['os'])) { - throw new SchemaViolationException('Property \'os\' was not array'); - } - } - - /** - * Check that the type of 'cpu' matches expectations - * - * @psalm-assert array{cpu: array} $arr - */ - private static function ensureCpu(array $arr): void - { - if (!array_key_exists('cpu', $arr)) { - throw new SchemaViolationException('Property \'cpu\' is required but was not found'); - } - if (array_key_exists('cpu', $arr) && !is_array($arr['cpu'])) { - throw new SchemaViolationException('Property \'cpu\' was not array'); - } - } - - /** - * Check that the type of 'ci' matches expectations - * - * @psalm-assert array{ci?: array} $arr - */ - private static function ensureCi(array $arr): void - { - if (array_key_exists('ci', $arr) && !is_array($arr['ci'])) { - throw new SchemaViolationException('Property \'ci\' was not array'); - } - } + self::ensureProtocolVersion($arr); + self::ensureImplementation($arr); + self::ensureRuntime($arr); + self::ensureOs($arr); + self::ensureCpu($arr); + self::ensureCi($arr); + + return new self( + (string) $arr['protocolVersion'], + Product::fromArray($arr['implementation']), + Product::fromArray($arr['runtime']), + Product::fromArray($arr['os']), + Product::fromArray($arr['cpu']), + isset($arr['ci']) ? Ci::fromArray($arr['ci']) : null, + ); + } + + /** + * Check that the type of 'protocolVersion' matches expectations + * + * @psalm-assert array{protocolVersion: mixed} $arr + */ + private static function ensureProtocolVersion(array $arr): void + { + if (!array_key_exists('protocolVersion', $arr)) { + throw new SchemaViolationException('Property \'protocolVersion\' is required but was not found'); + } + } + + /** + * Check that the type of 'implementation' matches expectations + * + * @psalm-assert array{implementation: array} $arr + */ + private static function ensureImplementation(array $arr): void + { + if (!array_key_exists('implementation', $arr)) { + throw new SchemaViolationException('Property \'implementation\' is required but was not found'); + } + if (array_key_exists('implementation', $arr) && !is_array($arr['implementation'])) { + throw new SchemaViolationException('Property \'implementation\' was not array'); + } + } + + /** + * Check that the type of 'runtime' matches expectations + * + * @psalm-assert array{runtime: array} $arr + */ + private static function ensureRuntime(array $arr): void + { + if (!array_key_exists('runtime', $arr)) { + throw new SchemaViolationException('Property \'runtime\' is required but was not found'); + } + if (array_key_exists('runtime', $arr) && !is_array($arr['runtime'])) { + throw new SchemaViolationException('Property \'runtime\' was not array'); + } + } + + /** + * Check that the type of 'os' matches expectations + * + * @psalm-assert array{os: array} $arr + */ + private static function ensureOs(array $arr): void + { + if (!array_key_exists('os', $arr)) { + throw new SchemaViolationException('Property \'os\' is required but was not found'); + } + if (array_key_exists('os', $arr) && !is_array($arr['os'])) { + throw new SchemaViolationException('Property \'os\' was not array'); + } + } + + /** + * Check that the type of 'cpu' matches expectations + * + * @psalm-assert array{cpu: array} $arr + */ + private static function ensureCpu(array $arr): void + { + if (!array_key_exists('cpu', $arr)) { + throw new SchemaViolationException('Property \'cpu\' is required but was not found'); + } + if (array_key_exists('cpu', $arr) && !is_array($arr['cpu'])) { + throw new SchemaViolationException('Property \'cpu\' was not array'); + } + } + + /** + * Check that the type of 'ci' matches expectations + * + * @psalm-assert array{ci?: array} $arr + */ + private static function ensureCi(array $arr): void + { + if (array_key_exists('ci', $arr) && !is_array($arr['ci'])) { + throw new SchemaViolationException('Property \'ci\' was not array'); + } + } } @@ -2417,22 +2417,22 @@ private static function ensureCi(array $arr): void * CI environment */ final class Ci implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. + * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. */ public readonly string $name, /** - * Link to the build + * Link to the build */ public readonly ?string $url, /** - * The build number. Some CI servers use non-numeric build numbers, which is why this is a string + * The build number. Some CI servers use non-numeric build numbers, which is why this is a string */ public readonly ?string $buildNumber, @@ -2447,40 +2447,40 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureName($arr); - self::ensureGit($arr); - - return new self( - (string) $arr['name'], - isset($arr['url']) ? (string) $arr['url'] : null, - isset($arr['buildNumber']) ? (string) $arr['buildNumber'] : null, - isset($arr['git']) ? Git::fromArray($arr['git']) : null, - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'git' matches expectations - * - * @psalm-assert array{git?: array} $arr - */ - private static function ensureGit(array $arr): void - { - if (array_key_exists('git', $arr) && !is_array($arr['git'])) { - throw new SchemaViolationException('Property \'git\' was not array'); - } - } + self::ensureName($arr); + self::ensureGit($arr); + + return new self( + (string) $arr['name'], + isset($arr['url']) ? (string) $arr['url'] : null, + isset($arr['buildNumber']) ? (string) $arr['buildNumber'] : null, + isset($arr['git']) ? Git::fromArray($arr['git']) : null, + ); + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'git' matches expectations + * + * @psalm-assert array{git?: array} $arr + */ + private static function ensureGit(array $arr): void + { + if (array_key_exists('git', $arr) && !is_array($arr['git'])) { + throw new SchemaViolationException('Property \'git\' was not array'); + } + } } @@ -2492,7 +2492,7 @@ private static function ensureGit(array $arr): void * variables. */ final class Git implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -2507,46 +2507,46 @@ private function __construct( ){} /** - * @throws SchemaViolationException + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureRemote($arr); + self::ensureRevision($arr); + + return new self( + (string) $arr['remote'], + (string) $arr['revision'], + isset($arr['branch']) ? (string) $arr['branch'] : null, + isset($arr['tag']) ? (string) $arr['tag'] : null, + ); + } + + /** + * Check that the type of 'remote' matches expectations + * + * @psalm-assert array{remote: mixed} $arr + */ + private static function ensureRemote(array $arr): void + { + if (!array_key_exists('remote', $arr)) { + throw new SchemaViolationException('Property \'remote\' is required but was not found'); + } + } + + /** + * Check that the type of 'revision' matches expectations * - * @internal + * @psalm-assert array{revision: mixed} $arr */ - public static function fromArray(array $arr) : self + private static function ensureRevision(array $arr): void { - self::ensureRemote($arr); - self::ensureRevision($arr); - - return new self( - (string) $arr['remote'], - (string) $arr['revision'], - isset($arr['branch']) ? (string) $arr['branch'] : null, - isset($arr['tag']) ? (string) $arr['tag'] : null, - ); - } - - /** - * Check that the type of 'remote' matches expectations - * - * @psalm-assert array{remote: mixed} $arr - */ - private static function ensureRemote(array $arr): void - { - if (!array_key_exists('remote', $arr)) { - throw new SchemaViolationException('Property \'remote\' is required but was not found'); - } - } - - /** - * Check that the type of 'revision' matches expectations - * - * @psalm-assert array{revision: mixed} $arr - */ - private static function ensureRevision(array $arr): void - { - if (!array_key_exists('revision', $arr)) { - throw new SchemaViolationException('Property \'revision\' is required but was not found'); - } - } + if (!array_key_exists('revision', $arr)) { + throw new SchemaViolationException('Property \'revision\' is required but was not found'); + } + } } @@ -2557,17 +2557,17 @@ private static function ensureRevision(array $arr): void * Used to describe various properties of Meta */ final class Product implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The product name + * The product name */ public readonly string $name, /** - * The product version + * The product version */ public readonly ?string $version, @@ -2580,25 +2580,25 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureName($arr); - - return new self( - (string) $arr['name'], - isset($arr['version']) ? (string) $arr['version'] : null, - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } + self::ensureName($arr); + + return new self( + (string) $arr['name'], + isset($arr['version']) ? (string) $arr['version'] : null, + ); + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } } @@ -2609,12 +2609,12 @@ private static function ensureName(array $arr): void */ final class ParameterType implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The name is unique, so we don't need an id. + * The name is unique, so we don't need an id. */ public readonly string $name, @@ -2638,83 +2638,83 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureName($arr); - self::ensureRegularExpressions($arr); - self::ensurePreferForRegularExpressionMatch($arr); - self::ensureUseForSnippets($arr); - self::ensureId($arr); - - return new self( - (string) $arr['name'], - array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), - (bool) $arr['preferForRegularExpressionMatch'], - (bool) $arr['useForSnippets'], - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'regularExpressions' matches expectations - * - * @psalm-assert array{regularExpressions: array} $arr - */ - private static function ensureRegularExpressions(array $arr): void - { - if (!array_key_exists('regularExpressions', $arr)) { - throw new SchemaViolationException('Property \'regularExpressions\' is required but was not found'); - } - if (array_key_exists('regularExpressions', $arr) && !is_array($arr['regularExpressions'])) { - throw new SchemaViolationException('Property \'regularExpressions\' was not array'); - } - } - - /** - * Check that the type of 'preferForRegularExpressionMatch' matches expectations - * - * @psalm-assert array{preferForRegularExpressionMatch: mixed} $arr - */ - private static function ensurePreferForRegularExpressionMatch(array $arr): void - { - if (!array_key_exists('preferForRegularExpressionMatch', $arr)) { - throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' is required but was not found'); - } - } - - /** - * Check that the type of 'useForSnippets' matches expectations - * - * @psalm-assert array{useForSnippets: mixed} $arr - */ - private static function ensureUseForSnippets(array $arr): void - { - if (!array_key_exists('useForSnippets', $arr)) { - throw new SchemaViolationException('Property \'useForSnippets\' is required but was not found'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } + self::ensureName($arr); + self::ensureRegularExpressions($arr); + self::ensurePreferForRegularExpressionMatch($arr); + self::ensureUseForSnippets($arr); + self::ensureId($arr); + + return new self( + (string) $arr['name'], + array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), + (bool) $arr['preferForRegularExpressionMatch'], + (bool) $arr['useForSnippets'], + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'regularExpressions' matches expectations + * + * @psalm-assert array{regularExpressions: array} $arr + */ + private static function ensureRegularExpressions(array $arr): void + { + if (!array_key_exists('regularExpressions', $arr)) { + throw new SchemaViolationException('Property \'regularExpressions\' is required but was not found'); + } + if (array_key_exists('regularExpressions', $arr) && !is_array($arr['regularExpressions'])) { + throw new SchemaViolationException('Property \'regularExpressions\' was not array'); + } + } + + /** + * Check that the type of 'preferForRegularExpressionMatch' matches expectations + * + * @psalm-assert array{preferForRegularExpressionMatch: mixed} $arr + */ + private static function ensurePreferForRegularExpressionMatch(array $arr): void + { + if (!array_key_exists('preferForRegularExpressionMatch', $arr)) { + throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' is required but was not found'); + } + } + + /** + * Check that the type of 'useForSnippets' matches expectations + * + * @psalm-assert array{useForSnippets: mixed} $arr + */ + private static function ensureUseForSnippets(array $arr): void + { + if (!array_key_exists('useForSnippets', $arr)) { + throw new SchemaViolationException('Property \'useForSnippets\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } } @@ -2725,7 +2725,7 @@ private static function ensureId(array $arr): void */ final class ParseError implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -2742,41 +2742,41 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureSource($arr); - self::ensureMessage($arr); - - return new self( - SourceReference::fromArray($arr['source']), - (string) $arr['message'], - ); - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source: array} $arr - */ - private static function ensureSource(array $arr): void - { - if (!array_key_exists('source', $arr)) { - throw new SchemaViolationException('Property \'source\' is required but was not found'); - } - if (array_key_exists('source', $arr) && !is_array($arr['source'])) { - throw new SchemaViolationException('Property \'source\' was not array'); - } - } - - /** - * Check that the type of 'message' matches expectations - * - * @psalm-assert array{message: mixed} $arr - */ - private static function ensureMessage(array $arr): void - { - if (!array_key_exists('message', $arr)) { - throw new SchemaViolationException('Property \'message\' is required but was not found'); - } - } + self::ensureSource($arr); + self::ensureMessage($arr); + + return new self( + SourceReference::fromArray($arr['source']), + (string) $arr['message'], + ); + } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source: array} $arr + */ + private static function ensureSource(array $arr): void + { + if (!array_key_exists('source', $arr)) { + throw new SchemaViolationException('Property \'source\' is required but was not found'); + } + if (array_key_exists('source', $arr) && !is_array($arr['source'])) { + throw new SchemaViolationException('Property \'source\' was not array'); + } + } + + /** + * Check that the type of 'message' matches expectations + * + * @psalm-assert array{message: mixed} $arr + */ + private static function ensureMessage(array $arr): void + { + if (!array_key_exists('message', $arr)) { + throw new SchemaViolationException('Property \'message\' is required but was not found'); + } + } } @@ -2798,47 +2798,47 @@ private static function ensureMessage(array $arr): void * Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` */ final class Pickle implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash + * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash * from the source data and the `locations` of the pickle. * This ID will change if source the file is modified. */ public readonly string $id, /** - * The uri of the source file + * The uri of the source file */ public readonly string $uri, /** - * The name of the pickle + * The name of the pickle */ public readonly string $name, /** - * The language of the pickle + * The language of the pickle */ public readonly string $language, /** - * One or more steps + * One or more steps * @param list $steps */ public readonly array $steps, /** - * One or more tags. If this pickle is constructed from a Gherkin document, + * One or more tags. If this pickle is constructed from a Gherkin document, * It includes inherited tags from the `Feature` as well. * @param list $tags */ public readonly array $tags, /** - * Points to the AST node locations of the pickle. The last one represents the unique + * Points to the AST node locations of the pickle. The last one represents the unique * id of the pickle. A pickle constructed from `Examples` will have the first * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. * @param list $astNodeIds @@ -2854,117 +2854,117 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureId($arr); - self::ensureUri($arr); - self::ensureName($arr); - self::ensureLanguage($arr); - self::ensureSteps($arr); - self::ensureTags($arr); - self::ensureAstNodeIds($arr); - - return new self( - (string) $arr['id'], - (string) $arr['uri'], - (string) $arr['name'], - (string) $arr['language'], - array_map(fn(array $member) => PickleStep::fromArray($member) , $arr['steps']), - array_map(fn(array $member) => PickleTag::fromArray($member) , $arr['tags']), - array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'uri' matches expectations - * - * @psalm-assert array{uri: mixed} $arr - */ - private static function ensureUri(array $arr): void - { - if (!array_key_exists('uri', $arr)) { - throw new SchemaViolationException('Property \'uri\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'language' matches expectations - * - * @psalm-assert array{language: mixed} $arr - */ - private static function ensureLanguage(array $arr): void - { - if (!array_key_exists('language', $arr)) { - throw new SchemaViolationException('Property \'language\' is required but was not found'); - } - } - - /** - * Check that the type of 'steps' matches expectations - * - * @psalm-assert array{steps: array} $arr - */ - private static function ensureSteps(array $arr): void - { - if (!array_key_exists('steps', $arr)) { - throw new SchemaViolationException('Property \'steps\' is required but was not found'); - } - if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { - throw new SchemaViolationException('Property \'steps\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'astNodeIds' matches expectations - * - * @psalm-assert array{astNodeIds: array} $arr - */ - private static function ensureAstNodeIds(array $arr): void - { - if (!array_key_exists('astNodeIds', $arr)) { - throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); - } - if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { - throw new SchemaViolationException('Property \'astNodeIds\' was not array'); - } - } + self::ensureId($arr); + self::ensureUri($arr); + self::ensureName($arr); + self::ensureLanguage($arr); + self::ensureSteps($arr); + self::ensureTags($arr); + self::ensureAstNodeIds($arr); + + return new self( + (string) $arr['id'], + (string) $arr['uri'], + (string) $arr['name'], + (string) $arr['language'], + array_map(fn(array $member) => PickleStep::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => PickleTag::fromArray($member) , $arr['tags']), + array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'uri' matches expectations + * + * @psalm-assert array{uri: mixed} $arr + */ + private static function ensureUri(array $arr): void + { + if (!array_key_exists('uri', $arr)) { + throw new SchemaViolationException('Property \'uri\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'language' matches expectations + * + * @psalm-assert array{language: mixed} $arr + */ + private static function ensureLanguage(array $arr): void + { + if (!array_key_exists('language', $arr)) { + throw new SchemaViolationException('Property \'language\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'astNodeIds' matches expectations + * + * @psalm-assert array{astNodeIds: array} $arr + */ + private static function ensureAstNodeIds(array $arr): void + { + if (!array_key_exists('astNodeIds', $arr)) { + throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); + } + if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { + throw new SchemaViolationException('Property \'astNodeIds\' was not array'); + } + } } @@ -2975,7 +2975,7 @@ private static function ensureAstNodeIds(array $arr): void */ final class PickleDocString implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -2992,25 +2992,25 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureContent($arr); - - return new self( - isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, - (string) $arr['content'], - ); - } - - /** - * Check that the type of 'content' matches expectations - * - * @psalm-assert array{content: mixed} $arr - */ - private static function ensureContent(array $arr): void - { - if (!array_key_exists('content', $arr)) { - throw new SchemaViolationException('Property \'content\' is required but was not found'); - } - } + self::ensureContent($arr); + + return new self( + isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, + (string) $arr['content'], + ); + } + + /** + * Check that the type of 'content' matches expectations + * + * @psalm-assert array{content: mixed} $arr + */ + private static function ensureContent(array $arr): void + { + if (!array_key_exists('content', $arr)) { + throw new SchemaViolationException('Property \'content\' is required but was not found'); + } + } } @@ -3021,21 +3021,21 @@ private static function ensureContent(array $arr): void * An executable step */ final class PickleStep implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( public readonly ?PickleStepArgument $argument, /** - * References the IDs of the source of the step. For Gherkin, this can be + * References the IDs of the source of the step. For Gherkin, this can be * the ID of a Step, and possibly also the ID of a TableRow * @param list $astNodeIds */ public readonly array $astNodeIds, /** - * A unique ID for the PickleStep + * A unique ID for the PickleStep */ public readonly string $id, @@ -3050,69 +3050,69 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureArgument($arr); - self::ensureAstNodeIds($arr); - self::ensureId($arr); - self::ensureText($arr); - - return new self( - isset($arr['argument']) ? PickleStepArgument::fromArray($arr['argument']) : null, - array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), - (string) $arr['id'], - (string) $arr['text'], - ); - } - - /** - * Check that the type of 'argument' matches expectations - * - * @psalm-assert array{argument?: array} $arr - */ - private static function ensureArgument(array $arr): void - { - if (array_key_exists('argument', $arr) && !is_array($arr['argument'])) { - throw new SchemaViolationException('Property \'argument\' was not array'); - } - } - - /** - * Check that the type of 'astNodeIds' matches expectations - * - * @psalm-assert array{astNodeIds: array} $arr - */ - private static function ensureAstNodeIds(array $arr): void - { - if (!array_key_exists('astNodeIds', $arr)) { - throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); - } - if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { - throw new SchemaViolationException('Property \'astNodeIds\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr - */ - private static function ensureText(array $arr): void - { - if (!array_key_exists('text', $arr)) { - throw new SchemaViolationException('Property \'text\' is required but was not found'); - } - } + self::ensureArgument($arr); + self::ensureAstNodeIds($arr); + self::ensureId($arr); + self::ensureText($arr); + + return new self( + isset($arr['argument']) ? PickleStepArgument::fromArray($arr['argument']) : null, + array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + (string) $arr['id'], + (string) $arr['text'], + ); + } + + /** + * Check that the type of 'argument' matches expectations + * + * @psalm-assert array{argument?: array} $arr + */ + private static function ensureArgument(array $arr): void + { + if (array_key_exists('argument', $arr) && !is_array($arr['argument'])) { + throw new SchemaViolationException('Property \'argument\' was not array'); + } + } + + /** + * Check that the type of 'astNodeIds' matches expectations + * + * @psalm-assert array{astNodeIds: array} $arr + */ + private static function ensureAstNodeIds(array $arr): void + { + if (!array_key_exists('astNodeIds', $arr)) { + throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); + } + if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { + throw new SchemaViolationException('Property \'astNodeIds\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } } @@ -3123,7 +3123,7 @@ private static function ensureText(array $arr): void * An optional argument */ final class PickleStepArgument implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3140,38 +3140,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureDocString($arr); - self::ensureDataTable($arr); - - return new self( - isset($arr['docString']) ? PickleDocString::fromArray($arr['docString']) : null, - isset($arr['dataTable']) ? PickleTable::fromArray($arr['dataTable']) : null, - ); - } - - /** - * Check that the type of 'docString' matches expectations - * - * @psalm-assert array{docString?: array} $arr - */ - private static function ensureDocString(array $arr): void - { - if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { - throw new SchemaViolationException('Property \'docString\' was not array'); - } - } - - /** - * Check that the type of 'dataTable' matches expectations - * - * @psalm-assert array{dataTable?: array} $arr - */ - private static function ensureDataTable(array $arr): void - { - if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { - throw new SchemaViolationException('Property \'dataTable\' was not array'); - } - } + self::ensureDocString($arr); + self::ensureDataTable($arr); + + return new self( + isset($arr['docString']) ? PickleDocString::fromArray($arr['docString']) : null, + isset($arr['dataTable']) ? PickleTable::fromArray($arr['dataTable']) : null, + ); + } + + /** + * Check that the type of 'docString' matches expectations + * + * @psalm-assert array{docString?: array} $arr + */ + private static function ensureDocString(array $arr): void + { + if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { + throw new SchemaViolationException('Property \'docString\' was not array'); + } + } + + /** + * Check that the type of 'dataTable' matches expectations + * + * @psalm-assert array{dataTable?: array} $arr + */ + private static function ensureDataTable(array $arr): void + { + if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { + throw new SchemaViolationException('Property \'dataTable\' was not array'); + } + } } @@ -3182,7 +3182,7 @@ private static function ensureDataTable(array $arr): void */ final class PickleTable implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3200,27 +3200,27 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureRows($arr); - - return new self( - array_map(fn(array $member) => PickleTableRow::fromArray($member) , $arr['rows']), - ); - } - - /** - * Check that the type of 'rows' matches expectations - * - * @psalm-assert array{rows: array} $arr - */ - private static function ensureRows(array $arr): void - { - if (!array_key_exists('rows', $arr)) { - throw new SchemaViolationException('Property \'rows\' is required but was not found'); - } - if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { - throw new SchemaViolationException('Property \'rows\' was not array'); - } - } + self::ensureRows($arr); + + return new self( + array_map(fn(array $member) => PickleTableRow::fromArray($member) , $arr['rows']), + ); + } + + /** + * Check that the type of 'rows' matches expectations + * + * @psalm-assert array{rows: array} $arr + */ + private static function ensureRows(array $arr): void + { + if (!array_key_exists('rows', $arr)) { + throw new SchemaViolationException('Property \'rows\' is required but was not found'); + } + if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { + throw new SchemaViolationException('Property \'rows\' was not array'); + } + } } @@ -3231,7 +3231,7 @@ private static function ensureRows(array $arr): void */ final class PickleTableCell implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3246,24 +3246,24 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureValue($arr); + self::ensureValue($arr); - return new self( - (string) $arr['value'], - ); + return new self( + (string) $arr['value'], + ); } - /** - * Check that the type of 'value' matches expectations - * - * @psalm-assert array{value: mixed} $arr - */ - private static function ensureValue(array $arr): void - { - if (!array_key_exists('value', $arr)) { - throw new SchemaViolationException('Property \'value\' is required but was not found'); - } - } + /** + * Check that the type of 'value' matches expectations + * + * @psalm-assert array{value: mixed} $arr + */ + private static function ensureValue(array $arr): void + { + if (!array_key_exists('value', $arr)) { + throw new SchemaViolationException('Property \'value\' is required but was not found'); + } + } } @@ -3274,7 +3274,7 @@ private static function ensureValue(array $arr): void */ final class PickleTableRow implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3292,27 +3292,27 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureCells($arr); - - return new self( - array_map(fn(array $member) => PickleTableCell::fromArray($member) , $arr['cells']), - ); - } - - /** - * Check that the type of 'cells' matches expectations - * - * @psalm-assert array{cells: array} $arr - */ - private static function ensureCells(array $arr): void - { - if (!array_key_exists('cells', $arr)) { - throw new SchemaViolationException('Property \'cells\' is required but was not found'); - } - if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { - throw new SchemaViolationException('Property \'cells\' was not array'); - } - } + self::ensureCells($arr); + + return new self( + array_map(fn(array $member) => PickleTableCell::fromArray($member) , $arr['cells']), + ); + } + + /** + * Check that the type of 'cells' matches expectations + * + * @psalm-assert array{cells: array} $arr + */ + private static function ensureCells(array $arr): void + { + if (!array_key_exists('cells', $arr)) { + throw new SchemaViolationException('Property \'cells\' is required but was not found'); + } + if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { + throw new SchemaViolationException('Property \'cells\' was not array'); + } + } } @@ -3323,14 +3323,14 @@ private static function ensureCells(array $arr): void * A tag */ final class PickleTag implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( public readonly string $name, /** - * Points to the AST node this was created from + * Points to the AST node this was created from */ public readonly string $astNodeId, @@ -3343,38 +3343,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureName($arr); - self::ensureAstNodeId($arr); - - return new self( - (string) $arr['name'], - (string) $arr['astNodeId'], - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'astNodeId' matches expectations - * - * @psalm-assert array{astNodeId: mixed} $arr - */ - private static function ensureAstNodeId(array $arr): void - { - if (!array_key_exists('astNodeId', $arr)) { - throw new SchemaViolationException('Property \'astNodeId\' is required but was not found'); - } - } + self::ensureName($arr); + self::ensureAstNodeId($arr); + + return new self( + (string) $arr['name'], + (string) $arr['astNodeId'], + ); + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'astNodeId' matches expectations + * + * @psalm-assert array{astNodeId: mixed} $arr + */ + private static function ensureAstNodeId(array $arr): void + { + if (!array_key_exists('astNodeId', $arr)) { + throw new SchemaViolationException('Property \'astNodeId\' is required but was not found'); + } + } } @@ -3387,23 +3387,23 @@ private static function ensureAstNodeId(array $arr): void * A source file, typically a Gherkin document or Java/Ruby/JavaScript source code */ final class Source implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) * of the source, typically a file path relative to the root directory */ public readonly string $uri, /** - * The contents of the file + * The contents of the file */ public readonly string $data, /** - * The media type of the file. Can be used to specify custom types, such as + * The media type of the file. Can be used to specify custom types, such as * text/x.cucumber.gherkin+plain */ public readonly Source\MediaType $mediaType, @@ -3417,52 +3417,52 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureUri($arr); - self::ensureData($arr); - self::ensureMediaType($arr); - - return new self( - (string) $arr['uri'], - (string) $arr['data'], - Source\MediaType::from((string) $arr['mediaType']), - ); - } - - /** - * Check that the type of 'uri' matches expectations - * - * @psalm-assert array{uri: mixed} $arr - */ - private static function ensureUri(array $arr): void - { - if (!array_key_exists('uri', $arr)) { - throw new SchemaViolationException('Property \'uri\' is required but was not found'); - } - } - - /** - * Check that the type of 'data' matches expectations - * - * @psalm-assert array{data: mixed} $arr - */ - private static function ensureData(array $arr): void - { - if (!array_key_exists('data', $arr)) { - throw new SchemaViolationException('Property \'data\' is required but was not found'); - } - } - - /** - * Check that the type of 'mediaType' matches expectations - * - * @psalm-assert array{mediaType: mixed} $arr - */ - private static function ensureMediaType(array $arr): void - { - if (!array_key_exists('mediaType', $arr)) { - throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); - } - } + self::ensureUri($arr); + self::ensureData($arr); + self::ensureMediaType($arr); + + return new self( + (string) $arr['uri'], + (string) $arr['data'], + Source\MediaType::from((string) $arr['mediaType']), + ); + } + + /** + * Check that the type of 'uri' matches expectations + * + * @psalm-assert array{uri: mixed} $arr + */ + private static function ensureUri(array $arr): void + { + if (!array_key_exists('uri', $arr)) { + throw new SchemaViolationException('Property \'uri\' is required but was not found'); + } + } + + /** + * Check that the type of 'data' matches expectations + * + * @psalm-assert array{data: mixed} $arr + */ + private static function ensureData(array $arr): void + { + if (!array_key_exists('data', $arr)) { + throw new SchemaViolationException('Property \'data\' is required but was not found'); + } + } + + /** + * Check that the type of 'mediaType' matches expectations + * + * @psalm-assert array{mediaType: mixed} $arr + */ + private static function ensureMediaType(array $arr): void + { + if (!array_key_exists('mediaType', $arr)) { + throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); + } + } } @@ -3474,7 +3474,7 @@ private static function ensureMediaType(array $arr): void * [Location](#io.cucumber.messages.Location) within that file. */ final class SourceReference implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3495,53 +3495,53 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureJavaMethod($arr); - self::ensureJavaStackTraceElement($arr); - self::ensureLocation($arr); - - return new self( - isset($arr['uri']) ? (string) $arr['uri'] : null, - isset($arr['javaMethod']) ? JavaMethod::fromArray($arr['javaMethod']) : null, - isset($arr['javaStackTraceElement']) ? JavaStackTraceElement::fromArray($arr['javaStackTraceElement']) : null, - isset($arr['location']) ? Location::fromArray($arr['location']) : null, - ); - } - - /** - * Check that the type of 'javaMethod' matches expectations - * - * @psalm-assert array{javaMethod?: array} $arr - */ - private static function ensureJavaMethod(array $arr): void - { - if (array_key_exists('javaMethod', $arr) && !is_array($arr['javaMethod'])) { - throw new SchemaViolationException('Property \'javaMethod\' was not array'); - } - } - - /** - * Check that the type of 'javaStackTraceElement' matches expectations - * - * @psalm-assert array{javaStackTraceElement?: array} $arr - */ - private static function ensureJavaStackTraceElement(array $arr): void - { - if (array_key_exists('javaStackTraceElement', $arr) && !is_array($arr['javaStackTraceElement'])) { - throw new SchemaViolationException('Property \'javaStackTraceElement\' was not array'); - } - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location?: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } + self::ensureJavaMethod($arr); + self::ensureJavaStackTraceElement($arr); + self::ensureLocation($arr); + + return new self( + isset($arr['uri']) ? (string) $arr['uri'] : null, + isset($arr['javaMethod']) ? JavaMethod::fromArray($arr['javaMethod']) : null, + isset($arr['javaStackTraceElement']) ? JavaStackTraceElement::fromArray($arr['javaStackTraceElement']) : null, + isset($arr['location']) ? Location::fromArray($arr['location']) : null, + ); + } + + /** + * Check that the type of 'javaMethod' matches expectations + * + * @psalm-assert array{javaMethod?: array} $arr + */ + private static function ensureJavaMethod(array $arr): void + { + if (array_key_exists('javaMethod', $arr) && !is_array($arr['javaMethod'])) { + throw new SchemaViolationException('Property \'javaMethod\' was not array'); + } + } + + /** + * Check that the type of 'javaStackTraceElement' matches expectations + * + * @psalm-assert array{javaStackTraceElement?: array} $arr + */ + private static function ensureJavaStackTraceElement(array $arr): void + { + if (array_key_exists('javaStackTraceElement', $arr) && !is_array($arr['javaStackTraceElement'])) { + throw new SchemaViolationException('Property \'javaStackTraceElement\' was not array'); + } + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location?: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } } @@ -3552,7 +3552,7 @@ private static function ensureLocation(array $arr): void */ final class JavaMethod implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3574,55 +3574,55 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureClassName($arr); - self::ensureMethodName($arr); - self::ensureMethodParameterTypes($arr); - - return new self( - (string) $arr['className'], - (string) $arr['methodName'], - array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), - ); - } - - /** - * Check that the type of 'className' matches expectations - * - * @psalm-assert array{className: mixed} $arr - */ - private static function ensureClassName(array $arr): void - { - if (!array_key_exists('className', $arr)) { - throw new SchemaViolationException('Property \'className\' is required but was not found'); - } - } - - /** - * Check that the type of 'methodName' matches expectations - * - * @psalm-assert array{methodName: mixed} $arr - */ - private static function ensureMethodName(array $arr): void - { - if (!array_key_exists('methodName', $arr)) { - throw new SchemaViolationException('Property \'methodName\' is required but was not found'); - } - } - - /** - * Check that the type of 'methodParameterTypes' matches expectations - * - * @psalm-assert array{methodParameterTypes: array} $arr - */ - private static function ensureMethodParameterTypes(array $arr): void - { - if (!array_key_exists('methodParameterTypes', $arr)) { - throw new SchemaViolationException('Property \'methodParameterTypes\' is required but was not found'); - } - if (array_key_exists('methodParameterTypes', $arr) && !is_array($arr['methodParameterTypes'])) { - throw new SchemaViolationException('Property \'methodParameterTypes\' was not array'); - } - } + self::ensureClassName($arr); + self::ensureMethodName($arr); + self::ensureMethodParameterTypes($arr); + + return new self( + (string) $arr['className'], + (string) $arr['methodName'], + array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), + ); + } + + /** + * Check that the type of 'className' matches expectations + * + * @psalm-assert array{className: mixed} $arr + */ + private static function ensureClassName(array $arr): void + { + if (!array_key_exists('className', $arr)) { + throw new SchemaViolationException('Property \'className\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodName' matches expectations + * + * @psalm-assert array{methodName: mixed} $arr + */ + private static function ensureMethodName(array $arr): void + { + if (!array_key_exists('methodName', $arr)) { + throw new SchemaViolationException('Property \'methodName\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodParameterTypes' matches expectations + * + * @psalm-assert array{methodParameterTypes: array} $arr + */ + private static function ensureMethodParameterTypes(array $arr): void + { + if (!array_key_exists('methodParameterTypes', $arr)) { + throw new SchemaViolationException('Property \'methodParameterTypes\' is required but was not found'); + } + if (array_key_exists('methodParameterTypes', $arr) && !is_array($arr['methodParameterTypes'])) { + throw new SchemaViolationException('Property \'methodParameterTypes\' was not array'); + } + } } @@ -3633,7 +3633,7 @@ private static function ensureMethodParameterTypes(array $arr): void */ final class JavaStackTraceElement implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3643,61 +3643,61 @@ private function __construct( public readonly string $methodName, - ){} + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureClassName($arr); + self::ensureFileName($arr); + self::ensureMethodName($arr); + + return new self( + (string) $arr['className'], + (string) $arr['fileName'], + (string) $arr['methodName'], + ); + } + + /** + * Check that the type of 'className' matches expectations + * + * @psalm-assert array{className: mixed} $arr + */ + private static function ensureClassName(array $arr): void + { + if (!array_key_exists('className', $arr)) { + throw new SchemaViolationException('Property \'className\' is required but was not found'); + } + } + + /** + * Check that the type of 'fileName' matches expectations + * + * @psalm-assert array{fileName: mixed} $arr + */ + private static function ensureFileName(array $arr): void + { + if (!array_key_exists('fileName', $arr)) { + throw new SchemaViolationException('Property \'fileName\' is required but was not found'); + } + } /** - * @throws SchemaViolationException + * Check that the type of 'methodName' matches expectations * - * @internal + * @psalm-assert array{methodName: mixed} $arr */ - public static function fromArray(array $arr) : self + private static function ensureMethodName(array $arr): void { - self::ensureClassName($arr); - self::ensureFileName($arr); - self::ensureMethodName($arr); - - return new self( - (string) $arr['className'], - (string) $arr['fileName'], - (string) $arr['methodName'], - ); - } - - /** - * Check that the type of 'className' matches expectations - * - * @psalm-assert array{className: mixed} $arr - */ - private static function ensureClassName(array $arr): void - { - if (!array_key_exists('className', $arr)) { - throw new SchemaViolationException('Property \'className\' is required but was not found'); - } - } - - /** - * Check that the type of 'fileName' matches expectations - * - * @psalm-assert array{fileName: mixed} $arr - */ - private static function ensureFileName(array $arr): void - { - if (!array_key_exists('fileName', $arr)) { - throw new SchemaViolationException('Property \'fileName\' is required but was not found'); - } - } - - /** - * Check that the type of 'methodName' matches expectations - * - * @psalm-assert array{methodName: mixed} $arr - */ - private static function ensureMethodName(array $arr): void - { - if (!array_key_exists('methodName', $arr)) { - throw new SchemaViolationException('Property \'methodName\' is required but was not found'); - } - } + if (!array_key_exists('methodName', $arr)) { + throw new SchemaViolationException('Property \'methodName\' is required but was not found'); + } + } } @@ -3708,7 +3708,7 @@ private static function ensureMethodName(array $arr): void */ final class StepDefinition implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3727,58 +3727,58 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureId($arr); - self::ensurePattern($arr); - self::ensureSourceReference($arr); - - return new self( - (string) $arr['id'], - StepDefinitionPattern::fromArray($arr['pattern']), - SourceReference::fromArray($arr['sourceReference']), - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'pattern' matches expectations - * - * @psalm-assert array{pattern: array} $arr - */ - private static function ensurePattern(array $arr): void - { - if (!array_key_exists('pattern', $arr)) { - throw new SchemaViolationException('Property \'pattern\' is required but was not found'); - } - if (array_key_exists('pattern', $arr) && !is_array($arr['pattern'])) { - throw new SchemaViolationException('Property \'pattern\' was not array'); - } - } - - /** - * Check that the type of 'sourceReference' matches expectations - * - * @psalm-assert array{sourceReference: array} $arr - */ - private static function ensureSourceReference(array $arr): void - { - if (!array_key_exists('sourceReference', $arr)) { - throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); - } - if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { - throw new SchemaViolationException('Property \'sourceReference\' was not array'); - } - } + self::ensureId($arr); + self::ensurePattern($arr); + self::ensureSourceReference($arr); + + return new self( + (string) $arr['id'], + StepDefinitionPattern::fromArray($arr['pattern']), + SourceReference::fromArray($arr['sourceReference']), + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'pattern' matches expectations + * + * @psalm-assert array{pattern: array} $arr + */ + private static function ensurePattern(array $arr): void + { + if (!array_key_exists('pattern', $arr)) { + throw new SchemaViolationException('Property \'pattern\' is required but was not found'); + } + if (array_key_exists('pattern', $arr) && !is_array($arr['pattern'])) { + throw new SchemaViolationException('Property \'pattern\' was not array'); + } + } + + /** + * Check that the type of 'sourceReference' matches expectations + * + * @psalm-assert array{sourceReference: array} $arr + */ + private static function ensureSourceReference(array $arr): void + { + if (!array_key_exists('sourceReference', $arr)) { + throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); + } + if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { + throw new SchemaViolationException('Property \'sourceReference\' was not array'); + } + } } @@ -3789,7 +3789,7 @@ private static function ensureSourceReference(array $arr): void */ final class StepDefinitionPattern implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3806,38 +3806,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureSource($arr); - self::ensureType($arr); - - return new self( - (string) $arr['source'], - StepDefinitionPattern\Type::from((string) $arr['type']), - ); - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source: mixed} $arr - */ - private static function ensureSource(array $arr): void - { - if (!array_key_exists('source', $arr)) { - throw new SchemaViolationException('Property \'source\' is required but was not found'); - } - } - - /** - * Check that the type of 'type' matches expectations - * - * @psalm-assert array{type: mixed} $arr - */ - private static function ensureType(array $arr): void - { - if (!array_key_exists('type', $arr)) { - throw new SchemaViolationException('Property \'type\' is required but was not found'); - } - } + self::ensureSource($arr); + self::ensureType($arr); + + return new self( + (string) $arr['source'], + StepDefinitionPattern\Type::from((string) $arr['type']), + ); + } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source: mixed} $arr + */ + private static function ensureSource(array $arr): void + { + if (!array_key_exists('source', $arr)) { + throw new SchemaViolationException('Property \'source\' is required but was not found'); + } + } + + /** + * Check that the type of 'type' matches expectations + * + * @psalm-assert array{type: mixed} $arr + */ + private static function ensureType(array $arr): void + { + if (!array_key_exists('type', $arr)) { + throw new SchemaViolationException('Property \'type\' is required but was not found'); + } + } } @@ -3850,14 +3850,14 @@ private static function ensureType(array $arr): void * A `TestCase` contains a sequence of `TestStep`s. */ final class TestCase implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( public readonly string $id, /** - * The ID of the `Pickle` this `TestCase` is derived from. + * The ID of the `Pickle` this `TestCase` is derived from. */ public readonly string $pickleId, @@ -3875,55 +3875,55 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureId($arr); - self::ensurePickleId($arr); - self::ensureTestSteps($arr); - - return new self( - (string) $arr['id'], - (string) $arr['pickleId'], - array_map(fn(array $member) => TestStep::fromArray($member) , $arr['testSteps']), - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'pickleId' matches expectations - * - * @psalm-assert array{pickleId: mixed} $arr - */ - private static function ensurePickleId(array $arr): void - { - if (!array_key_exists('pickleId', $arr)) { - throw new SchemaViolationException('Property \'pickleId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testSteps' matches expectations - * - * @psalm-assert array{testSteps: array} $arr - */ - private static function ensureTestSteps(array $arr): void - { - if (!array_key_exists('testSteps', $arr)) { - throw new SchemaViolationException('Property \'testSteps\' is required but was not found'); - } - if (array_key_exists('testSteps', $arr) && !is_array($arr['testSteps'])) { - throw new SchemaViolationException('Property \'testSteps\' was not array'); - } - } + self::ensureId($arr); + self::ensurePickleId($arr); + self::ensureTestSteps($arr); + + return new self( + (string) $arr['id'], + (string) $arr['pickleId'], + array_map(fn(array $member) => TestStep::fromArray($member) , $arr['testSteps']), + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'pickleId' matches expectations + * + * @psalm-assert array{pickleId: mixed} $arr + */ + private static function ensurePickleId(array $arr): void + { + if (!array_key_exists('pickleId', $arr)) { + throw new SchemaViolationException('Property \'pickleId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testSteps' matches expectations + * + * @psalm-assert array{testSteps: array} $arr + */ + private static function ensureTestSteps(array $arr): void + { + if (!array_key_exists('testSteps', $arr)) { + throw new SchemaViolationException('Property \'testSteps\' is required but was not found'); + } + if (array_key_exists('testSteps', $arr) && !is_array($arr['testSteps'])) { + throw new SchemaViolationException('Property \'testSteps\' was not array'); + } + } } @@ -3934,7 +3934,7 @@ private static function ensureTestSteps(array $arr): void */ final class Group implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -3956,29 +3956,29 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureChildren($arr); - - return new self( - array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), - isset($arr['start']) ? (int) $arr['start'] : null, - isset($arr['value']) ? (string) $arr['value'] : null, - ); - } - - /** - * Check that the type of 'children' matches expectations - * - * @psalm-assert array{children: array} $arr - */ - private static function ensureChildren(array $arr): void - { - if (!array_key_exists('children', $arr)) { - throw new SchemaViolationException('Property \'children\' is required but was not found'); - } - if (array_key_exists('children', $arr) && !is_array($arr['children'])) { - throw new SchemaViolationException('Property \'children\' was not array'); - } - } + self::ensureChildren($arr); + + return new self( + array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), + isset($arr['start']) ? (int) $arr['start'] : null, + isset($arr['value']) ? (string) $arr['value'] : null, + ); + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } } @@ -3994,12 +3994,12 @@ private static function ensureChildren(array $arr): void * This message closely matches the `Argument` class in the `cucumber-expressions` library. */ final class StepMatchArgument implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * Represents the outermost capture group of an argument. This message closely matches the + * Represents the outermost capture group of an argument. This message closely matches the * `Group` class in the `cucumber-expressions` library. */ public readonly Group $group, @@ -4015,28 +4015,28 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureGroup($arr); - - return new self( - Group::fromArray($arr['group']), - isset($arr['parameterTypeName']) ? (string) $arr['parameterTypeName'] : null, - ); - } - - /** - * Check that the type of 'group' matches expectations - * - * @psalm-assert array{group: array} $arr - */ - private static function ensureGroup(array $arr): void - { - if (!array_key_exists('group', $arr)) { - throw new SchemaViolationException('Property \'group\' is required but was not found'); - } - if (array_key_exists('group', $arr) && !is_array($arr['group'])) { - throw new SchemaViolationException('Property \'group\' was not array'); - } - } + self::ensureGroup($arr); + + return new self( + Group::fromArray($arr['group']), + isset($arr['parameterTypeName']) ? (string) $arr['parameterTypeName'] : null, + ); + } + + /** + * Check that the type of 'group' matches expectations + * + * @psalm-assert array{group: array} $arr + */ + private static function ensureGroup(array $arr): void + { + if (!array_key_exists('group', $arr)) { + throw new SchemaViolationException('Property \'group\' is required but was not found'); + } + if (array_key_exists('group', $arr) && !is_array($arr['group'])) { + throw new SchemaViolationException('Property \'group\' was not array'); + } + } } @@ -4047,7 +4047,7 @@ private static function ensureGroup(array $arr): void */ final class StepMatchArgumentsList implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4065,27 +4065,27 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureStepMatchArguments($arr); - - return new self( - array_map(fn(array $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), - ); - } - - /** - * Check that the type of 'stepMatchArguments' matches expectations - * - * @psalm-assert array{stepMatchArguments: array} $arr - */ - private static function ensureStepMatchArguments(array $arr): void - { - if (!array_key_exists('stepMatchArguments', $arr)) { - throw new SchemaViolationException('Property \'stepMatchArguments\' is required but was not found'); - } - if (array_key_exists('stepMatchArguments', $arr) && !is_array($arr['stepMatchArguments'])) { - throw new SchemaViolationException('Property \'stepMatchArguments\' was not array'); - } - } + self::ensureStepMatchArguments($arr); + + return new self( + array_map(fn(array $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), + ); + } + + /** + * Check that the type of 'stepMatchArguments' matches expectations + * + * @psalm-assert array{stepMatchArguments: array} $arr + */ + private static function ensureStepMatchArguments(array $arr): void + { + if (!array_key_exists('stepMatchArguments', $arr)) { + throw new SchemaViolationException('Property \'stepMatchArguments\' is required but was not found'); + } + if (array_key_exists('stepMatchArguments', $arr) && !is_array($arr['stepMatchArguments'])) { + throw new SchemaViolationException('Property \'stepMatchArguments\' was not array'); + } + } } @@ -4097,30 +4097,30 @@ private static function ensureStepMatchArguments(array $arr): void * combined with a `StepDefinition`, or from a `Hook`. */ final class TestStep implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * Pointer to the `Hook` (if derived from a Hook) + * Pointer to the `Hook` (if derived from a Hook) */ public readonly ?string $hookId, public readonly string $id, /** - * Pointer to the `PickleStep` (if derived from a `PickleStep`) + * Pointer to the `PickleStep` (if derived from a `PickleStep`) */ public readonly ?string $pickleStepId, /** - * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) + * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) * @param ?list $stepDefinitionIds */ public readonly ?array $stepDefinitionIds, /** - * A list of list of StepMatchArgument (if derived from a `PickleStep`). + * A list of list of StepMatchArgument (if derived from a `PickleStep`). * Each element represents a matching step definition. A size of 0 means `UNDEFINED`, * and a size of 2+ means `AMBIGUOUS` * @param ?list $stepMatchArgumentsLists @@ -4136,54 +4136,54 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureId($arr); - self::ensureStepDefinitionIds($arr); - self::ensureStepMatchArgumentsLists($arr); - - return new self( - isset($arr['hookId']) ? (string) $arr['hookId'] : null, - (string) $arr['id'], - isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, - isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, - isset($arr['stepMatchArgumentsLists']) ? array_map(fn(array $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'stepDefinitionIds' matches expectations - * - * @psalm-assert array{stepDefinitionIds?: array} $arr - */ - private static function ensureStepDefinitionIds(array $arr): void - { - if (array_key_exists('stepDefinitionIds', $arr) && !is_array($arr['stepDefinitionIds'])) { - throw new SchemaViolationException('Property \'stepDefinitionIds\' was not array'); - } - } - - /** - * Check that the type of 'stepMatchArgumentsLists' matches expectations - * - * @psalm-assert array{stepMatchArgumentsLists?: array} $arr - */ - private static function ensureStepMatchArgumentsLists(array $arr): void - { - if (array_key_exists('stepMatchArgumentsLists', $arr) && !is_array($arr['stepMatchArgumentsLists'])) { - throw new SchemaViolationException('Property \'stepMatchArgumentsLists\' was not array'); - } - } + self::ensureId($arr); + self::ensureStepDefinitionIds($arr); + self::ensureStepMatchArgumentsLists($arr); + + return new self( + isset($arr['hookId']) ? (string) $arr['hookId'] : null, + (string) $arr['id'], + isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, + isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, + isset($arr['stepMatchArgumentsLists']) ? array_map(fn(array $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'stepDefinitionIds' matches expectations + * + * @psalm-assert array{stepDefinitionIds?: array} $arr + */ + private static function ensureStepDefinitionIds(array $arr): void + { + if (array_key_exists('stepDefinitionIds', $arr) && !is_array($arr['stepDefinitionIds'])) { + throw new SchemaViolationException('Property \'stepDefinitionIds\' was not array'); + } + } + + /** + * Check that the type of 'stepMatchArgumentsLists' matches expectations + * + * @psalm-assert array{stepMatchArgumentsLists?: array} $arr + */ + private static function ensureStepMatchArgumentsLists(array $arr): void + { + if (array_key_exists('stepMatchArgumentsLists', $arr) && !is_array($arr['stepMatchArgumentsLists'])) { + throw new SchemaViolationException('Property \'stepMatchArgumentsLists\' was not array'); + } + } } @@ -4194,7 +4194,7 @@ private static function ensureStepMatchArgumentsLists(array $arr): void */ final class TestCaseFinished implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4213,55 +4213,55 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureTestCaseStartedId($arr); - self::ensureTimestamp($arr); - self::ensureWillBeRetried($arr); - - return new self( - (string) $arr['testCaseStartedId'], - Timestamp::fromArray($arr['timestamp']), - (bool) $arr['willBeRetried'], - ); - } - - /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr - */ - private static function ensureTestCaseStartedId(array $arr): void - { - if (!array_key_exists('testCaseStartedId', $arr)) { - throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } - - /** - * Check that the type of 'willBeRetried' matches expectations - * - * @psalm-assert array{willBeRetried: mixed} $arr - */ - private static function ensureWillBeRetried(array $arr): void - { - if (!array_key_exists('willBeRetried', $arr)) { - throw new SchemaViolationException('Property \'willBeRetried\' is required but was not found'); - } - } + self::ensureTestCaseStartedId($arr); + self::ensureTimestamp($arr); + self::ensureWillBeRetried($arr); + + return new self( + (string) $arr['testCaseStartedId'], + Timestamp::fromArray($arr['timestamp']), + (bool) $arr['willBeRetried'], + ); + } + + /** + * Check that the type of 'testCaseStartedId' matches expectations + * + * @psalm-assert array{testCaseStartedId: mixed} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (!array_key_exists('testCaseStartedId', $arr)) { + throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } + + /** + * Check that the type of 'willBeRetried' matches expectations + * + * @psalm-assert array{willBeRetried: mixed} $arr + */ + private static function ensureWillBeRetried(array $arr): void + { + if (!array_key_exists('willBeRetried', $arr)) { + throw new SchemaViolationException('Property \'willBeRetried\' is required but was not found'); + } + } } @@ -4272,18 +4272,18 @@ private static function ensureWillBeRetried(array $arr): void */ final class TestCaseStarted implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * The first attempt should have value 0, and for each retry the value + * The first attempt should have value 0, and for each retry the value * should increase by 1. */ public readonly int $attempt, /** - * Because a `TestCase` can be run multiple times (in case of a retry), + * Because a `TestCase` can be run multiple times (in case of a retry), * we use this field to group messages relating to the same attempt. */ public readonly string $id, @@ -4301,69 +4301,69 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureAttempt($arr); - self::ensureId($arr); - self::ensureTestCaseId($arr); - self::ensureTimestamp($arr); - - return new self( - (int) $arr['attempt'], - (string) $arr['id'], - (string) $arr['testCaseId'], - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'attempt' matches expectations - * - * @psalm-assert array{attempt: mixed} $arr - */ - private static function ensureAttempt(array $arr): void - { - if (!array_key_exists('attempt', $arr)) { - throw new SchemaViolationException('Property \'attempt\' is required but was not found'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'testCaseId' matches expectations - * - * @psalm-assert array{testCaseId: mixed} $arr - */ - private static function ensureTestCaseId(array $arr): void - { - if (!array_key_exists('testCaseId', $arr)) { - throw new SchemaViolationException('Property \'testCaseId\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } + self::ensureAttempt($arr); + self::ensureId($arr); + self::ensureTestCaseId($arr); + self::ensureTimestamp($arr); + + return new self( + (int) $arr['attempt'], + (string) $arr['id'], + (string) $arr['testCaseId'], + Timestamp::fromArray($arr['timestamp']), + ); + } + + /** + * Check that the type of 'attempt' matches expectations + * + * @psalm-assert array{attempt: mixed} $arr + */ + private static function ensureAttempt(array $arr): void + { + if (!array_key_exists('attempt', $arr)) { + throw new SchemaViolationException('Property \'attempt\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'testCaseId' matches expectations + * + * @psalm-assert array{testCaseId: mixed} $arr + */ + private static function ensureTestCaseId(array $arr): void + { + if (!array_key_exists('testCaseId', $arr)) { + throw new SchemaViolationException('Property \'testCaseId\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -4374,12 +4374,12 @@ private static function ensureTimestamp(array $arr): void */ final class TestRunFinished implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. + * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. * If there are undefined parameter types, the message is simply * "The following parameter type(s() are not defined: xxx, yyy". * The independent `UndefinedParameterType` messages can be used to generate @@ -4388,12 +4388,12 @@ private function __construct( public readonly ?string $message, /** - * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) + * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) */ public readonly bool $success, /** - * Timestamp when the TestRun is finished + * Timestamp when the TestRun is finished */ public readonly Timestamp $timestamp, @@ -4406,42 +4406,42 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureSuccess($arr); - self::ensureTimestamp($arr); - - return new self( - isset($arr['message']) ? (string) $arr['message'] : null, - (bool) $arr['success'], - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'success' matches expectations - * - * @psalm-assert array{success: mixed} $arr - */ - private static function ensureSuccess(array $arr): void - { - if (!array_key_exists('success', $arr)) { - throw new SchemaViolationException('Property \'success\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } + self::ensureSuccess($arr); + self::ensureTimestamp($arr); + + return new self( + isset($arr['message']) ? (string) $arr['message'] : null, + (bool) $arr['success'], + Timestamp::fromArray($arr['timestamp']), + ); + } + + /** + * Check that the type of 'success' matches expectations + * + * @psalm-assert array{success: mixed} $arr + */ + private static function ensureSuccess(array $arr): void + { + if (!array_key_exists('success', $arr)) { + throw new SchemaViolationException('Property \'success\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -4452,7 +4452,7 @@ private static function ensureTimestamp(array $arr): void */ final class TestRunStarted implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4467,27 +4467,27 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureTimestamp($arr); - - return new self( - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } + self::ensureTimestamp($arr); + + return new self( + Timestamp::fromArray($arr['timestamp']), + ); + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -4498,7 +4498,7 @@ private static function ensureTimestamp(array $arr): void */ final class TestStepFinished implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4519,72 +4519,72 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureTestCaseStartedId($arr); - self::ensureTestStepId($arr); - self::ensureTestStepResult($arr); - self::ensureTimestamp($arr); - - return new self( - (string) $arr['testCaseStartedId'], - (string) $arr['testStepId'], - TestStepResult::fromArray($arr['testStepResult']), - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr - */ - private static function ensureTestCaseStartedId(array $arr): void - { - if (!array_key_exists('testCaseStartedId', $arr)) { - throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testStepId' matches expectations - * - * @psalm-assert array{testStepId: mixed} $arr - */ - private static function ensureTestStepId(array $arr): void - { - if (!array_key_exists('testStepId', $arr)) { - throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testStepResult' matches expectations - * - * @psalm-assert array{testStepResult: array} $arr - */ - private static function ensureTestStepResult(array $arr): void - { - if (!array_key_exists('testStepResult', $arr)) { - throw new SchemaViolationException('Property \'testStepResult\' is required but was not found'); - } - if (array_key_exists('testStepResult', $arr) && !is_array($arr['testStepResult'])) { - throw new SchemaViolationException('Property \'testStepResult\' was not array'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } + self::ensureTestCaseStartedId($arr); + self::ensureTestStepId($arr); + self::ensureTestStepResult($arr); + self::ensureTimestamp($arr); + + return new self( + (string) $arr['testCaseStartedId'], + (string) $arr['testStepId'], + TestStepResult::fromArray($arr['testStepResult']), + Timestamp::fromArray($arr['timestamp']), + ); + } + + /** + * Check that the type of 'testCaseStartedId' matches expectations + * + * @psalm-assert array{testCaseStartedId: mixed} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (!array_key_exists('testCaseStartedId', $arr)) { + throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testStepId' matches expectations + * + * @psalm-assert array{testStepId: mixed} $arr + */ + private static function ensureTestStepId(array $arr): void + { + if (!array_key_exists('testStepId', $arr)) { + throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testStepResult' matches expectations + * + * @psalm-assert array{testStepResult: array} $arr + */ + private static function ensureTestStepResult(array $arr): void + { + if (!array_key_exists('testStepResult', $arr)) { + throw new SchemaViolationException('Property \'testStepResult\' is required but was not found'); + } + if (array_key_exists('testStepResult', $arr) && !is_array($arr['testStepResult'])) { + throw new SchemaViolationException('Property \'testStepResult\' was not array'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -4595,7 +4595,7 @@ private static function ensureTimestamp(array $arr): void */ final class TestStepResult implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4614,42 +4614,42 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureDuration($arr); - self::ensureStatus($arr); - - return new self( - Duration::fromArray($arr['duration']), - isset($arr['message']) ? (string) $arr['message'] : null, - TestStepResult\Status::from((string) $arr['status']), - ); - } - - /** - * Check that the type of 'duration' matches expectations - * - * @psalm-assert array{duration: array} $arr - */ - private static function ensureDuration(array $arr): void - { - if (!array_key_exists('duration', $arr)) { - throw new SchemaViolationException('Property \'duration\' is required but was not found'); - } - if (array_key_exists('duration', $arr) && !is_array($arr['duration'])) { - throw new SchemaViolationException('Property \'duration\' was not array'); - } - } - - /** - * Check that the type of 'status' matches expectations - * - * @psalm-assert array{status: mixed} $arr - */ - private static function ensureStatus(array $arr): void - { - if (!array_key_exists('status', $arr)) { - throw new SchemaViolationException('Property \'status\' is required but was not found'); - } - } + self::ensureDuration($arr); + self::ensureStatus($arr); + + return new self( + Duration::fromArray($arr['duration']), + isset($arr['message']) ? (string) $arr['message'] : null, + TestStepResult\Status::from((string) $arr['status']), + ); + } + + /** + * Check that the type of 'duration' matches expectations + * + * @psalm-assert array{duration: array} $arr + */ + private static function ensureDuration(array $arr): void + { + if (!array_key_exists('duration', $arr)) { + throw new SchemaViolationException('Property \'duration\' is required but was not found'); + } + if (array_key_exists('duration', $arr) && !is_array($arr['duration'])) { + throw new SchemaViolationException('Property \'duration\' was not array'); + } + } + + /** + * Check that the type of 'status' matches expectations + * + * @psalm-assert array{status: mixed} $arr + */ + private static function ensureStatus(array $arr): void + { + if (!array_key_exists('status', $arr)) { + throw new SchemaViolationException('Property \'status\' is required but was not found'); + } + } } @@ -4660,7 +4660,7 @@ private static function ensureStatus(array $arr): void */ final class TestStepStarted implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4679,55 +4679,55 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureTestCaseStartedId($arr); - self::ensureTestStepId($arr); - self::ensureTimestamp($arr); - - return new self( - (string) $arr['testCaseStartedId'], - (string) $arr['testStepId'], - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr - */ - private static function ensureTestCaseStartedId(array $arr): void - { - if (!array_key_exists('testCaseStartedId', $arr)) { - throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testStepId' matches expectations - * - * @psalm-assert array{testStepId: mixed} $arr - */ - private static function ensureTestStepId(array $arr): void - { - if (!array_key_exists('testStepId', $arr)) { - throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } + self::ensureTestCaseStartedId($arr); + self::ensureTestStepId($arr); + self::ensureTimestamp($arr); + + return new self( + (string) $arr['testCaseStartedId'], + (string) $arr['testStepId'], + Timestamp::fromArray($arr['timestamp']), + ); + } + + /** + * Check that the type of 'testCaseStartedId' matches expectations + * + * @psalm-assert array{testCaseStartedId: mixed} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (!array_key_exists('testCaseStartedId', $arr)) { + throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testStepId' matches expectations + * + * @psalm-assert array{testStepId: mixed} $arr + */ + private static function ensureTestStepId(array $arr): void + { + if (!array_key_exists('testStepId', $arr)) { + throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); + } + } + + /** + * Check that the type of 'timestamp' matches expectations + * + * @psalm-assert array{timestamp: array} $arr + */ + private static function ensureTimestamp(array $arr): void + { + if (!array_key_exists('timestamp', $arr)) { + throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); + } + if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { + throw new SchemaViolationException('Property \'timestamp\' was not array'); + } + } } @@ -4738,19 +4738,19 @@ private static function ensureTimestamp(array $arr): void */ final class Timestamp implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( /** - * Represents seconds of UTC time since Unix epoch + * Represents seconds of UTC time since Unix epoch * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to * 9999-12-31T23:59:59Z inclusive. */ public readonly int $seconds, /** - * Non-negative fractions of a second at nanosecond resolution. Negative + * Non-negative fractions of a second at nanosecond resolution. Negative * second values with fractions must still have non-negative nanos values * that count forward in time. Must be from 0 to 999,999,999 * inclusive. @@ -4766,38 +4766,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureSeconds($arr); - self::ensureNanos($arr); - - return new self( - (int) $arr['seconds'], - (int) $arr['nanos'], - ); - } - - /** - * Check that the type of 'seconds' matches expectations - * - * @psalm-assert array{seconds: mixed} $arr - */ - private static function ensureSeconds(array $arr): void - { - if (!array_key_exists('seconds', $arr)) { - throw new SchemaViolationException('Property \'seconds\' is required but was not found'); - } - } - - /** - * Check that the type of 'nanos' matches expectations - * - * @psalm-assert array{nanos: mixed} $arr - */ - private static function ensureNanos(array $arr): void - { - if (!array_key_exists('nanos', $arr)) { - throw new SchemaViolationException('Property \'nanos\' is required but was not found'); - } - } + self::ensureSeconds($arr); + self::ensureNanos($arr); + + return new self( + (int) $arr['seconds'], + (int) $arr['nanos'], + ); + } + + /** + * Check that the type of 'seconds' matches expectations + * + * @psalm-assert array{seconds: mixed} $arr + */ + private static function ensureSeconds(array $arr): void + { + if (!array_key_exists('seconds', $arr)) { + throw new SchemaViolationException('Property \'seconds\' is required but was not found'); + } + } + + /** + * Check that the type of 'nanos' matches expectations + * + * @psalm-assert array{nanos: mixed} $arr + */ + private static function ensureNanos(array $arr): void + { + if (!array_key_exists('nanos', $arr)) { + throw new SchemaViolationException('Property \'nanos\' is required but was not found'); + } + } } @@ -4808,7 +4808,7 @@ private static function ensureNanos(array $arr): void */ final class UndefinedParameterType implements JsonSerializable { - use JsonEncodingTrait; + use JsonEncodingTrait; private function __construct( @@ -4825,38 +4825,38 @@ private function __construct( */ public static function fromArray(array $arr) : self { - self::ensureExpression($arr); - self::ensureName($arr); - - return new self( - (string) $arr['expression'], - (string) $arr['name'], - ); - } - - /** - * Check that the type of 'expression' matches expectations - * - * @psalm-assert array{expression: mixed} $arr - */ - private static function ensureExpression(array $arr): void - { - if (!array_key_exists('expression', $arr)) { - throw new SchemaViolationException('Property \'expression\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } + self::ensureExpression($arr); + self::ensureName($arr); + + return new self( + (string) $arr['expression'], + (string) $arr['name'], + ); + } + + /** + * Check that the type of 'expression' matches expectations + * + * @psalm-assert array{expression: mixed} $arr + */ + private static function ensureExpression(array $arr): void + { + if (!array_key_exists('expression', $arr)) { + throw new SchemaViolationException('Property \'expression\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } } From 9b90facb23941e525953e58348c6fce82f5dc538 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Sat, 22 Jan 2022 21:20:03 +0000 Subject: [PATCH 05/37] Fix JSON decoding flags --- messages/php/src/JsonEncodingTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/php/src/JsonEncodingTrait.php b/messages/php/src/JsonEncodingTrait.php index 7b55b3c708a..f68bd2b29f5 100644 --- a/messages/php/src/JsonEncodingTrait.php +++ b/messages/php/src/JsonEncodingTrait.php @@ -10,7 +10,7 @@ trait JsonEncodingTrait public static function fromJson(string $json) : self { try { - $data = json_decode($json, true, JSON_THROW_ON_ERROR); + $data = json_decode($json, true, flags: JSON_THROW_ON_ERROR); } catch(\Throwable $t) { throw new UnknownDecodingException('Unknown decoding error', previous: $t); From 605a645fb165bb4887f00b61dc82ccc53e7e7396 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Sat, 22 Jan 2022 21:21:19 +0000 Subject: [PATCH 06/37] Ensure composer dependencies are installed before build --- messages/php/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/messages/php/Makefile b/messages/php/Makefile index 1a807e42bef..d2d034c3d45 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -1,7 +1,10 @@ JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") -.deps: src/generated/messages.php +.deps: composer.lock src/generated/messages.php + +composer.lock: + composer install src/generated/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ From c5142c73ceece544861297d17871b8b1cf6b5928 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Sat, 22 Jan 2022 23:22:04 +0000 Subject: [PATCH 07/37] Split generated classes into separate files --- .../scripts/templates/php.enum.php.erb | 1 + .../jsonschema/scripts/templates/php.php.erb | 3 +- messages/php/Makefile | 6 +- messages/php/build/.gitignore | 1 + messages/php/composer.json | 7 +- messages/php/psalm.xml | 1 + messages/php/split_classes.php | 40 + messages/php/src-generated/Attachment.php | 158 + .../Attachment/ContentEncoding.php | 14 + messages/php/src-generated/Background.php | 143 + messages/php/src-generated/Ci.php | 82 + messages/php/src-generated/Comment.php | 76 + messages/php/src-generated/DataTable.php | 76 + messages/php/src-generated/DocString.php | 89 + messages/php/src-generated/Duration.php | 74 + messages/php/src-generated/Envelope.php | 312 ++ messages/php/src-generated/Examples.php | 181 + messages/php/src-generated/Feature.php | 179 + messages/php/src-generated/FeatureChild.php | 83 + .../php/src-generated/GherkinDocument.php | 86 + messages/php/src-generated/Git.php | 74 + messages/php/src-generated/Group.php | 63 + messages/php/src-generated/Hook.php | 73 + messages/php/src-generated/JavaMethod.php | 89 + .../src-generated/JavaStackTraceElement.php | 83 + messages/php/src-generated/Location.php | 54 + messages/php/src-generated/Meta.php | 159 + messages/php/src-generated/ParameterType.php | 124 + messages/php/src-generated/ParseError.php | 70 + messages/php/src-generated/Pickle.php | 196 + .../php/src-generated/PickleDocString.php | 54 + messages/php/src-generated/PickleStep.php | 110 + .../php/src-generated/PickleStepArgument.php | 67 + messages/php/src-generated/PickleTable.php | 57 + .../php/src-generated/PickleTableCell.php | 51 + messages/php/src-generated/PickleTableRow.php | 57 + messages/php/src-generated/PickleTag.php | 70 + messages/php/src-generated/Product.php | 60 + messages/php/src-generated/Rule.php | 166 + messages/php/src-generated/RuleChild.php | 67 + messages/php/src-generated/Scenario.php | 187 + messages/php/src-generated/Source.php | 96 + .../php/src-generated/Source/MediaType.php | 14 + .../php/src-generated/SourceReference.php | 87 + messages/php/src-generated/Step.php | 140 + messages/php/src-generated/StepDefinition.php | 89 + .../src-generated/StepDefinitionPattern.php | 67 + .../StepDefinitionPattern/Type.php | 14 + .../php/src-generated/StepMatchArgument.php | 66 + .../src-generated/StepMatchArgumentsList.php | 57 + messages/php/src-generated/TableCell.php | 76 + messages/php/src-generated/TableRow.php | 96 + messages/php/src-generated/Tag.php | 95 + messages/php/src-generated/TestCase.php | 94 + .../php/src-generated/TestCaseFinished.php | 86 + .../php/src-generated/TestCaseStarted.php | 110 + .../php/src-generated/TestRunFinished.php | 86 + messages/php/src-generated/TestRunStarted.php | 54 + messages/php/src-generated/TestStep.php | 106 + .../php/src-generated/TestStepFinished.php | 105 + messages/php/src-generated/TestStepResult.php | 73 + .../src-generated/TestStepResult/Status.php | 19 + .../php/src-generated/TestStepStarted.php | 86 + messages/php/src-generated/Timestamp.php | 78 + .../src-generated/UndefinedParameterType.php | 67 + messages/php/src/generated/messages.php | 4903 ----------------- 66 files changed, 5396 insertions(+), 4911 deletions(-) create mode 100644 messages/php/build/.gitignore create mode 100644 messages/php/split_classes.php create mode 100644 messages/php/src-generated/Attachment.php create mode 100644 messages/php/src-generated/Attachment/ContentEncoding.php create mode 100644 messages/php/src-generated/Background.php create mode 100644 messages/php/src-generated/Ci.php create mode 100644 messages/php/src-generated/Comment.php create mode 100644 messages/php/src-generated/DataTable.php create mode 100644 messages/php/src-generated/DocString.php create mode 100644 messages/php/src-generated/Duration.php create mode 100644 messages/php/src-generated/Envelope.php create mode 100644 messages/php/src-generated/Examples.php create mode 100644 messages/php/src-generated/Feature.php create mode 100644 messages/php/src-generated/FeatureChild.php create mode 100644 messages/php/src-generated/GherkinDocument.php create mode 100644 messages/php/src-generated/Git.php create mode 100644 messages/php/src-generated/Group.php create mode 100644 messages/php/src-generated/Hook.php create mode 100644 messages/php/src-generated/JavaMethod.php create mode 100644 messages/php/src-generated/JavaStackTraceElement.php create mode 100644 messages/php/src-generated/Location.php create mode 100644 messages/php/src-generated/Meta.php create mode 100644 messages/php/src-generated/ParameterType.php create mode 100644 messages/php/src-generated/ParseError.php create mode 100644 messages/php/src-generated/Pickle.php create mode 100644 messages/php/src-generated/PickleDocString.php create mode 100644 messages/php/src-generated/PickleStep.php create mode 100644 messages/php/src-generated/PickleStepArgument.php create mode 100644 messages/php/src-generated/PickleTable.php create mode 100644 messages/php/src-generated/PickleTableCell.php create mode 100644 messages/php/src-generated/PickleTableRow.php create mode 100644 messages/php/src-generated/PickleTag.php create mode 100644 messages/php/src-generated/Product.php create mode 100644 messages/php/src-generated/Rule.php create mode 100644 messages/php/src-generated/RuleChild.php create mode 100644 messages/php/src-generated/Scenario.php create mode 100644 messages/php/src-generated/Source.php create mode 100644 messages/php/src-generated/Source/MediaType.php create mode 100644 messages/php/src-generated/SourceReference.php create mode 100644 messages/php/src-generated/Step.php create mode 100644 messages/php/src-generated/StepDefinition.php create mode 100644 messages/php/src-generated/StepDefinitionPattern.php create mode 100644 messages/php/src-generated/StepDefinitionPattern/Type.php create mode 100644 messages/php/src-generated/StepMatchArgument.php create mode 100644 messages/php/src-generated/StepMatchArgumentsList.php create mode 100644 messages/php/src-generated/TableCell.php create mode 100644 messages/php/src-generated/TableRow.php create mode 100644 messages/php/src-generated/Tag.php create mode 100644 messages/php/src-generated/TestCase.php create mode 100644 messages/php/src-generated/TestCaseFinished.php create mode 100644 messages/php/src-generated/TestCaseStarted.php create mode 100644 messages/php/src-generated/TestRunFinished.php create mode 100644 messages/php/src-generated/TestRunStarted.php create mode 100644 messages/php/src-generated/TestStep.php create mode 100644 messages/php/src-generated/TestStepFinished.php create mode 100644 messages/php/src-generated/TestStepResult.php create mode 100644 messages/php/src-generated/TestStepResult/Status.php create mode 100644 messages/php/src-generated/TestStepStarted.php create mode 100644 messages/php/src-generated/Timestamp.php create mode 100644 messages/php/src-generated/UndefinedParameterType.php delete mode 100644 messages/php/src/generated/messages.php diff --git a/messages/jsonschema/scripts/templates/php.enum.php.erb b/messages/jsonschema/scripts/templates/php.enum.php.erb index 321bde05f72..47cc0cd90f7 100644 --- a/messages/jsonschema/scripts/templates/php.enum.php.erb +++ b/messages/jsonschema/scripts/templates/php.enum.php.erb @@ -1,6 +1,7 @@ <%- namespaces = enum[:name].split('\\') -%> namespace Cucumber\Messages\<%= namespaces.slice(0,1)[0] %>; +// CLASS_START <%= enum[:name].split('\\').join('/') %>.php enum <%= namespaces[-1] %> : string { <%- enum[:values].each_with_index do |value, index| -%> diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index 03fa03c5ac1..b7489159348 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -7,7 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; -<% @schemas.sort.each do |key, schema| %> +<%- @schemas.sort.each do |key, schema| -%> +// CLASS_START <%= class_name(key) %>.php /** * Represents the <%= class_name(key) %> message in Cucumber's message protocol * @see https://github.com/cucumber/common/tree/main/messages#readme diff --git a/messages/php/Makefile b/messages/php/Makefile index d2d034c3d45..79db1f9efc5 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -1,13 +1,15 @@ JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") -.deps: composer.lock src/generated/messages.php +.deps: composer.lock build/messages.php composer.lock: composer install -src/generated/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb +build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ + rm -rf src-generated/* + php split_classes.php test: .deps vendor/bin/psalm diff --git a/messages/php/build/.gitignore b/messages/php/build/.gitignore new file mode 100644 index 00000000000..2ff0a6e5325 --- /dev/null +++ b/messages/php/build/.gitignore @@ -0,0 +1 @@ +messages.php diff --git a/messages/php/composer.json b/messages/php/composer.json index 0a6708f74b3..d57a60be320 100644 --- a/messages/php/composer.json +++ b/messages/php/composer.json @@ -15,10 +15,7 @@ }, "autoload": { "psr-4": { - "Cucumber\\Messages\\": "src" - }, - "files": [ - "src/generated/messages.php" - ] + "Cucumber\\Messages\\": ["src","src-generated"] + } } } diff --git a/messages/php/psalm.xml b/messages/php/psalm.xml index 63e8582975f..b1c0c4c7611 100644 --- a/messages/php/psalm.xml +++ b/messages/php/psalm.xml @@ -8,6 +8,7 @@ > + diff --git a/messages/php/split_classes.php b/messages/php/split_classes.php new file mode 100644 index 00000000000..ebc7d5fad21 --- /dev/null +++ b/messages/php/split_classes.php @@ -0,0 +1,40 @@ +^.*)/.*#', $filename, $matches)) { + mkdir(__DIR__.'/src-generated/'.$matches['dir'], recursive: true); + } + + file_put_contents(__DIR__ .'/src-generated/'.$filename, $fileContents); + + echo "Written ".__DIR__ .'/src-generated/'.$filename."\n"; + } +} diff --git a/messages/php/src-generated/Attachment.php b/messages/php/src-generated/Attachment.php new file mode 100644 index 00000000000..08f44b8a59b --- /dev/null +++ b/messages/php/src-generated/Attachment.php @@ -0,0 +1,158 @@ + IDENTITY + * - byte array => BASE64 + * - stream => BASE64 + */ + public readonly Attachment\ContentEncoding $contentEncoding, + + /** + * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) + */ + public readonly ?string $fileName, + + /** + * The media type of the data. This can be any valid + * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) + * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` + * and `text/x.cucumber.stacktrace+plain` + */ + public readonly string $mediaType, + + public readonly ?Source $source, + + public readonly ?string $testCaseStartedId, + + public readonly ?string $testStepId, + + /** + * A URL where the attachment can be retrieved. This field should not be set by Cucumber. + * It should be set by a program that reads a message stream and does the following for + * each Attachment message: + * + * - Writes the body (after base64 decoding if necessary) to a new file. + * - Sets `body` and `contentEncoding` to `null` + * - Writes out the new attachment message + * + * This will result in a smaller message stream, which can improve performance and + * reduce bandwidth of message consumers. It also makes it easier to process and download attachments + * separately from reports. + */ + public readonly ?string $url, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureBody($arr); + self::ensureContentEncoding($arr); + self::ensureMediaType($arr); + self::ensureSource($arr); + + return new self( + (string) $arr['body'], + Attachment\ContentEncoding::from((string) $arr['contentEncoding']), + isset($arr['fileName']) ? (string) $arr['fileName'] : null, + (string) $arr['mediaType'], + isset($arr['source']) ? Source::fromArray($arr['source']) : null, + isset($arr['testCaseStartedId']) ? (string) $arr['testCaseStartedId'] : null, + isset($arr['testStepId']) ? (string) $arr['testStepId'] : null, + isset($arr['url']) ? (string) $arr['url'] : null, + ); + } + + /** + * Check that the type of 'body' matches expectations + * + * @psalm-assert array{body: mixed} $arr + */ + private static function ensureBody(array $arr): void + { + if (!array_key_exists('body', $arr)) { + throw new SchemaViolationException('Property \'body\' is required but was not found'); + } + } + + /** + * Check that the type of 'contentEncoding' matches expectations + * + * @psalm-assert array{contentEncoding: mixed} $arr + */ + private static function ensureContentEncoding(array $arr): void + { + if (!array_key_exists('contentEncoding', $arr)) { + throw new SchemaViolationException('Property \'contentEncoding\' is required but was not found'); + } + } + + /** + * Check that the type of 'mediaType' matches expectations + * + * @psalm-assert array{mediaType: mixed} $arr + */ + private static function ensureMediaType(array $arr): void + { + if (!array_key_exists('mediaType', $arr)) { + throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); + } + } + + /** + * Check that the type of 'source' matches expectations + * + * @psalm-assert array{source?: array} $arr + */ + private static function ensureSource(array $arr): void + { + if (array_key_exists('source', $arr) && !is_array($arr['source'])) { + throw new SchemaViolationException('Property \'source\' was not array'); + } + } +} diff --git a/messages/php/src-generated/Attachment/ContentEncoding.php b/messages/php/src-generated/Attachment/ContentEncoding.php new file mode 100644 index 00000000000..2e24a48b055 --- /dev/null +++ b/messages/php/src-generated/Attachment/ContentEncoding.php @@ -0,0 +1,14 @@ + $steps + */ + public readonly array $steps, + + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureSteps($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/Ci.php b/messages/php/src-generated/Ci.php new file mode 100644 index 00000000000..4086b2cd530 --- /dev/null +++ b/messages/php/src-generated/Ci.php @@ -0,0 +1,82 @@ + $rows + */ + public readonly array $rows, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureRows($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => TableRow::fromArray($member) , $arr['rows']), + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'rows' matches expectations + * + * @psalm-assert array{rows: array} $arr + */ + private static function ensureRows(array $arr): void + { + if (!array_key_exists('rows', $arr)) { + throw new SchemaViolationException('Property \'rows\' is required but was not found'); + } + if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { + throw new SchemaViolationException('Property \'rows\' was not array'); + } + } +} diff --git a/messages/php/src-generated/DocString.php b/messages/php/src-generated/DocString.php new file mode 100644 index 00000000000..e4ae19f0291 --- /dev/null +++ b/messages/php/src-generated/DocString.php @@ -0,0 +1,89 @@ + $tags + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + public readonly ?TableRow $tableHeader, + + /** + * @param list $tableBody + */ + public readonly array $tableBody, + + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureTableHeader($arr); + self::ensureTableBody($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, + array_map(fn(array $member) => TableRow::fromArray($member) , $arr['tableBody']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'tableHeader' matches expectations + * + * @psalm-assert array{tableHeader?: array} $arr + */ + private static function ensureTableHeader(array $arr): void + { + if (array_key_exists('tableHeader', $arr) && !is_array($arr['tableHeader'])) { + throw new SchemaViolationException('Property \'tableHeader\' was not array'); + } + } + + /** + * Check that the type of 'tableBody' matches expectations + * + * @psalm-assert array{tableBody: array} $arr + */ + private static function ensureTableBody(array $arr): void + { + if (!array_key_exists('tableBody', $arr)) { + throw new SchemaViolationException('Property \'tableBody\' is required but was not found'); + } + if (array_key_exists('tableBody', $arr) && !is_array($arr['tableBody'])) { + throw new SchemaViolationException('Property \'tableBody\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/Feature.php b/messages/php/src-generated/Feature.php new file mode 100644 index 00000000000..cdfb5dafbdc --- /dev/null +++ b/messages/php/src-generated/Feature.php @@ -0,0 +1,179 @@ + $tags + */ + public readonly array $tags, + + /** + * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document + */ + public readonly string $language, + + /** + * The text of the `Feature` keyword (in the language specified by `language`) + */ + public readonly string $keyword, + + /** + * The name of the feature (the text following the `keyword`) + */ + public readonly string $name, + + /** + * The line(s) underneath the line with the `keyword` that are used as description + */ + public readonly string $description, + + /** + * Zero or more children + * @param list $children + */ + public readonly array $children, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureLanguage($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureChildren($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['language'], + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => FeatureChild::fromArray($member) , $arr['children']), + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'language' matches expectations + * + * @psalm-assert array{language: mixed} $arr + */ + private static function ensureLanguage(array $arr): void + { + if (!array_key_exists('language', $arr)) { + throw new SchemaViolationException('Property \'language\' is required but was not found'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } +} diff --git a/messages/php/src-generated/FeatureChild.php b/messages/php/src-generated/FeatureChild.php new file mode 100644 index 00000000000..dc53ad46434 --- /dev/null +++ b/messages/php/src-generated/FeatureChild.php @@ -0,0 +1,83 @@ + $comments + */ + public readonly array $comments, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureFeature($arr); + self::ensureComments($arr); + + return new self( + isset($arr['uri']) ? (string) $arr['uri'] : null, + isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null, + array_map(fn(array $member) => Comment::fromArray($member) , $arr['comments']), + ); + } + + /** + * Check that the type of 'feature' matches expectations + * + * @psalm-assert array{feature?: array} $arr + */ + private static function ensureFeature(array $arr): void + { + if (array_key_exists('feature', $arr) && !is_array($arr['feature'])) { + throw new SchemaViolationException('Property \'feature\' was not array'); + } + } + + /** + * Check that the type of 'comments' matches expectations + * + * @psalm-assert array{comments: array} $arr + */ + private static function ensureComments(array $arr): void + { + if (!array_key_exists('comments', $arr)) { + throw new SchemaViolationException('Property \'comments\' is required but was not found'); + } + if (array_key_exists('comments', $arr) && !is_array($arr['comments'])) { + throw new SchemaViolationException('Property \'comments\' was not array'); + } + } +} diff --git a/messages/php/src-generated/Git.php b/messages/php/src-generated/Git.php new file mode 100644 index 00000000000..b03ad74ab9e --- /dev/null +++ b/messages/php/src-generated/Git.php @@ -0,0 +1,74 @@ + $children + */ + public readonly array $children, + + public readonly ?int $start, + + public readonly ?string $value, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureChildren($arr); + + return new self( + array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), + isset($arr['start']) ? (int) $arr['start'] : null, + isset($arr['value']) ? (string) $arr['value'] : null, + ); + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } +} diff --git a/messages/php/src-generated/Hook.php b/messages/php/src-generated/Hook.php new file mode 100644 index 00000000000..4bd18b5d1dd --- /dev/null +++ b/messages/php/src-generated/Hook.php @@ -0,0 +1,73 @@ + $methodParameterTypes + */ + public readonly array $methodParameterTypes, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureClassName($arr); + self::ensureMethodName($arr); + self::ensureMethodParameterTypes($arr); + + return new self( + (string) $arr['className'], + (string) $arr['methodName'], + array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), + ); + } + + /** + * Check that the type of 'className' matches expectations + * + * @psalm-assert array{className: mixed} $arr + */ + private static function ensureClassName(array $arr): void + { + if (!array_key_exists('className', $arr)) { + throw new SchemaViolationException('Property \'className\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodName' matches expectations + * + * @psalm-assert array{methodName: mixed} $arr + */ + private static function ensureMethodName(array $arr): void + { + if (!array_key_exists('methodName', $arr)) { + throw new SchemaViolationException('Property \'methodName\' is required but was not found'); + } + } + + /** + * Check that the type of 'methodParameterTypes' matches expectations + * + * @psalm-assert array{methodParameterTypes: array} $arr + */ + private static function ensureMethodParameterTypes(array $arr): void + { + if (!array_key_exists('methodParameterTypes', $arr)) { + throw new SchemaViolationException('Property \'methodParameterTypes\' is required but was not found'); + } + if (array_key_exists('methodParameterTypes', $arr) && !is_array($arr['methodParameterTypes'])) { + throw new SchemaViolationException('Property \'methodParameterTypes\' was not array'); + } + } +} diff --git a/messages/php/src-generated/JavaStackTraceElement.php b/messages/php/src-generated/JavaStackTraceElement.php new file mode 100644 index 00000000000..6d1537aeb52 --- /dev/null +++ b/messages/php/src-generated/JavaStackTraceElement.php @@ -0,0 +1,83 @@ + $regularExpressions + */ + public readonly array $regularExpressions, + + public readonly bool $preferForRegularExpressionMatch, + + public readonly bool $useForSnippets, + + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureName($arr); + self::ensureRegularExpressions($arr); + self::ensurePreferForRegularExpressionMatch($arr); + self::ensureUseForSnippets($arr); + self::ensureId($arr); + + return new self( + (string) $arr['name'], + array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), + (bool) $arr['preferForRegularExpressionMatch'], + (bool) $arr['useForSnippets'], + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'regularExpressions' matches expectations + * + * @psalm-assert array{regularExpressions: array} $arr + */ + private static function ensureRegularExpressions(array $arr): void + { + if (!array_key_exists('regularExpressions', $arr)) { + throw new SchemaViolationException('Property \'regularExpressions\' is required but was not found'); + } + if (array_key_exists('regularExpressions', $arr) && !is_array($arr['regularExpressions'])) { + throw new SchemaViolationException('Property \'regularExpressions\' was not array'); + } + } + + /** + * Check that the type of 'preferForRegularExpressionMatch' matches expectations + * + * @psalm-assert array{preferForRegularExpressionMatch: mixed} $arr + */ + private static function ensurePreferForRegularExpressionMatch(array $arr): void + { + if (!array_key_exists('preferForRegularExpressionMatch', $arr)) { + throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' is required but was not found'); + } + } + + /** + * Check that the type of 'useForSnippets' matches expectations + * + * @psalm-assert array{useForSnippets: mixed} $arr + */ + private static function ensureUseForSnippets(array $arr): void + { + if (!array_key_exists('useForSnippets', $arr)) { + throw new SchemaViolationException('Property \'useForSnippets\' is required but was not found'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/ParseError.php b/messages/php/src-generated/ParseError.php new file mode 100644 index 00000000000..fe416b9e69b --- /dev/null +++ b/messages/php/src-generated/ParseError.php @@ -0,0 +1,70 @@ + $steps + */ + public readonly array $steps, + + /** + * One or more tags. If this pickle is constructed from a Gherkin document, + * It includes inherited tags from the `Feature` as well. + * @param list $tags + */ + public readonly array $tags, + + /** + * Points to the AST node locations of the pickle. The last one represents the unique + * id of the pickle. A pickle constructed from `Examples` will have the first + * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. + * @param list $astNodeIds + */ + public readonly array $astNodeIds, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureId($arr); + self::ensureUri($arr); + self::ensureName($arr); + self::ensureLanguage($arr); + self::ensureSteps($arr); + self::ensureTags($arr); + self::ensureAstNodeIds($arr); + + return new self( + (string) $arr['id'], + (string) $arr['uri'], + (string) $arr['name'], + (string) $arr['language'], + array_map(fn(array $member) => PickleStep::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => PickleTag::fromArray($member) , $arr['tags']), + array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'uri' matches expectations + * + * @psalm-assert array{uri: mixed} $arr + */ + private static function ensureUri(array $arr): void + { + if (!array_key_exists('uri', $arr)) { + throw new SchemaViolationException('Property \'uri\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'language' matches expectations + * + * @psalm-assert array{language: mixed} $arr + */ + private static function ensureLanguage(array $arr): void + { + if (!array_key_exists('language', $arr)) { + throw new SchemaViolationException('Property \'language\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'astNodeIds' matches expectations + * + * @psalm-assert array{astNodeIds: array} $arr + */ + private static function ensureAstNodeIds(array $arr): void + { + if (!array_key_exists('astNodeIds', $arr)) { + throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); + } + if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { + throw new SchemaViolationException('Property \'astNodeIds\' was not array'); + } + } +} diff --git a/messages/php/src-generated/PickleDocString.php b/messages/php/src-generated/PickleDocString.php new file mode 100644 index 00000000000..561cb2609ed --- /dev/null +++ b/messages/php/src-generated/PickleDocString.php @@ -0,0 +1,54 @@ + $astNodeIds + */ + public readonly array $astNodeIds, + + /** + * A unique ID for the PickleStep + */ + public readonly string $id, + + public readonly string $text, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureArgument($arr); + self::ensureAstNodeIds($arr); + self::ensureId($arr); + self::ensureText($arr); + + return new self( + isset($arr['argument']) ? PickleStepArgument::fromArray($arr['argument']) : null, + array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + (string) $arr['id'], + (string) $arr['text'], + ); + } + + /** + * Check that the type of 'argument' matches expectations + * + * @psalm-assert array{argument?: array} $arr + */ + private static function ensureArgument(array $arr): void + { + if (array_key_exists('argument', $arr) && !is_array($arr['argument'])) { + throw new SchemaViolationException('Property \'argument\' was not array'); + } + } + + /** + * Check that the type of 'astNodeIds' matches expectations + * + * @psalm-assert array{astNodeIds: array} $arr + */ + private static function ensureAstNodeIds(array $arr): void + { + if (!array_key_exists('astNodeIds', $arr)) { + throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); + } + if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { + throw new SchemaViolationException('Property \'astNodeIds\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'text' matches expectations + * + * @psalm-assert array{text: mixed} $arr + */ + private static function ensureText(array $arr): void + { + if (!array_key_exists('text', $arr)) { + throw new SchemaViolationException('Property \'text\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/PickleStepArgument.php b/messages/php/src-generated/PickleStepArgument.php new file mode 100644 index 00000000000..c0e4f08ac2e --- /dev/null +++ b/messages/php/src-generated/PickleStepArgument.php @@ -0,0 +1,67 @@ + $rows + */ + public readonly array $rows, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureRows($arr); + + return new self( + array_map(fn(array $member) => PickleTableRow::fromArray($member) , $arr['rows']), + ); + } + + /** + * Check that the type of 'rows' matches expectations + * + * @psalm-assert array{rows: array} $arr + */ + private static function ensureRows(array $arr): void + { + if (!array_key_exists('rows', $arr)) { + throw new SchemaViolationException('Property \'rows\' is required but was not found'); + } + if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { + throw new SchemaViolationException('Property \'rows\' was not array'); + } + } +} diff --git a/messages/php/src-generated/PickleTableCell.php b/messages/php/src-generated/PickleTableCell.php new file mode 100644 index 00000000000..047616854ab --- /dev/null +++ b/messages/php/src-generated/PickleTableCell.php @@ -0,0 +1,51 @@ + $cells + */ + public readonly array $cells, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureCells($arr); + + return new self( + array_map(fn(array $member) => PickleTableCell::fromArray($member) , $arr['cells']), + ); + } + + /** + * Check that the type of 'cells' matches expectations + * + * @psalm-assert array{cells: array} $arr + */ + private static function ensureCells(array $arr): void + { + if (!array_key_exists('cells', $arr)) { + throw new SchemaViolationException('Property \'cells\' is required but was not found'); + } + if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { + throw new SchemaViolationException('Property \'cells\' was not array'); + } + } +} diff --git a/messages/php/src-generated/PickleTag.php b/messages/php/src-generated/PickleTag.php new file mode 100644 index 00000000000..b24eee18ba5 --- /dev/null +++ b/messages/php/src-generated/PickleTag.php @@ -0,0 +1,70 @@ + $tags + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + /** + * @param list $children + */ + public readonly array $children, + + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureChildren($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => RuleChild::fromArray($member) , $arr['children']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'children' matches expectations + * + * @psalm-assert array{children: array} $arr + */ + private static function ensureChildren(array $arr): void + { + if (!array_key_exists('children', $arr)) { + throw new SchemaViolationException('Property \'children\' is required but was not found'); + } + if (array_key_exists('children', $arr) && !is_array($arr['children'])) { + throw new SchemaViolationException('Property \'children\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/RuleChild.php b/messages/php/src-generated/RuleChild.php new file mode 100644 index 00000000000..6471aa1db92 --- /dev/null +++ b/messages/php/src-generated/RuleChild.php @@ -0,0 +1,67 @@ + $tags + */ + public readonly array $tags, + + public readonly string $keyword, + + public readonly string $name, + + public readonly string $description, + + /** + * @param list $steps + */ + public readonly array $steps, + + /** + * @param list $examples + */ + public readonly array $examples, + + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureTags($arr); + self::ensureKeyword($arr); + self::ensureName($arr); + self::ensureDescription($arr); + self::ensureSteps($arr); + self::ensureExamples($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + (string) $arr['keyword'], + (string) $arr['name'], + (string) $arr['description'], + array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), + array_map(fn(array $member) => Examples::fromArray($member) , $arr['examples']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'tags' matches expectations + * + * @psalm-assert array{tags: array} $arr + */ + private static function ensureTags(array $arr): void + { + if (!array_key_exists('tags', $arr)) { + throw new SchemaViolationException('Property \'tags\' is required but was not found'); + } + if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { + throw new SchemaViolationException('Property \'tags\' was not array'); + } + } + + /** + * Check that the type of 'keyword' matches expectations + * + * @psalm-assert array{keyword: mixed} $arr + */ + private static function ensureKeyword(array $arr): void + { + if (!array_key_exists('keyword', $arr)) { + throw new SchemaViolationException('Property \'keyword\' is required but was not found'); + } + } + + /** + * Check that the type of 'name' matches expectations + * + * @psalm-assert array{name: mixed} $arr + */ + private static function ensureName(array $arr): void + { + if (!array_key_exists('name', $arr)) { + throw new SchemaViolationException('Property \'name\' is required but was not found'); + } + } + + /** + * Check that the type of 'description' matches expectations + * + * @psalm-assert array{description: mixed} $arr + */ + private static function ensureDescription(array $arr): void + { + if (!array_key_exists('description', $arr)) { + throw new SchemaViolationException('Property \'description\' is required but was not found'); + } + } + + /** + * Check that the type of 'steps' matches expectations + * + * @psalm-assert array{steps: array} $arr + */ + private static function ensureSteps(array $arr): void + { + if (!array_key_exists('steps', $arr)) { + throw new SchemaViolationException('Property \'steps\' is required but was not found'); + } + if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { + throw new SchemaViolationException('Property \'steps\' was not array'); + } + } + + /** + * Check that the type of 'examples' matches expectations + * + * @psalm-assert array{examples: array} $arr + */ + private static function ensureExamples(array $arr): void + { + if (!array_key_exists('examples', $arr)) { + throw new SchemaViolationException('Property \'examples\' is required but was not found'); + } + if (array_key_exists('examples', $arr) && !is_array($arr['examples'])) { + throw new SchemaViolationException('Property \'examples\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/Source.php b/messages/php/src-generated/Source.php new file mode 100644 index 00000000000..a70a6dc0c52 --- /dev/null +++ b/messages/php/src-generated/Source.php @@ -0,0 +1,96 @@ + $stepMatchArguments + */ + public readonly array $stepMatchArguments, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureStepMatchArguments($arr); + + return new self( + array_map(fn(array $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), + ); + } + + /** + * Check that the type of 'stepMatchArguments' matches expectations + * + * @psalm-assert array{stepMatchArguments: array} $arr + */ + private static function ensureStepMatchArguments(array $arr): void + { + if (!array_key_exists('stepMatchArguments', $arr)) { + throw new SchemaViolationException('Property \'stepMatchArguments\' is required but was not found'); + } + if (array_key_exists('stepMatchArguments', $arr) && !is_array($arr['stepMatchArguments'])) { + throw new SchemaViolationException('Property \'stepMatchArguments\' was not array'); + } + } +} diff --git a/messages/php/src-generated/TableCell.php b/messages/php/src-generated/TableCell.php new file mode 100644 index 00000000000..7bc2fdf8a73 --- /dev/null +++ b/messages/php/src-generated/TableCell.php @@ -0,0 +1,76 @@ + $cells + */ + public readonly array $cells, + + public readonly string $id, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureLocation($arr); + self::ensureCells($arr); + self::ensureId($arr); + + return new self( + Location::fromArray($arr['location']), + array_map(fn(array $member) => TableCell::fromArray($member) , $arr['cells']), + (string) $arr['id'], + ); + } + + /** + * Check that the type of 'location' matches expectations + * + * @psalm-assert array{location: array} $arr + */ + private static function ensureLocation(array $arr): void + { + if (!array_key_exists('location', $arr)) { + throw new SchemaViolationException('Property \'location\' is required but was not found'); + } + if (array_key_exists('location', $arr) && !is_array($arr['location'])) { + throw new SchemaViolationException('Property \'location\' was not array'); + } + } + + /** + * Check that the type of 'cells' matches expectations + * + * @psalm-assert array{cells: array} $arr + */ + private static function ensureCells(array $arr): void + { + if (!array_key_exists('cells', $arr)) { + throw new SchemaViolationException('Property \'cells\' is required but was not found'); + } + if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { + throw new SchemaViolationException('Property \'cells\' was not array'); + } + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } +} diff --git a/messages/php/src-generated/Tag.php b/messages/php/src-generated/Tag.php new file mode 100644 index 00000000000..996c1d704aa --- /dev/null +++ b/messages/php/src-generated/Tag.php @@ -0,0 +1,95 @@ + $testSteps + */ + public readonly array $testSteps, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureId($arr); + self::ensurePickleId($arr); + self::ensureTestSteps($arr); + + return new self( + (string) $arr['id'], + (string) $arr['pickleId'], + array_map(fn(array $member) => TestStep::fromArray($member) , $arr['testSteps']), + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'pickleId' matches expectations + * + * @psalm-assert array{pickleId: mixed} $arr + */ + private static function ensurePickleId(array $arr): void + { + if (!array_key_exists('pickleId', $arr)) { + throw new SchemaViolationException('Property \'pickleId\' is required but was not found'); + } + } + + /** + * Check that the type of 'testSteps' matches expectations + * + * @psalm-assert array{testSteps: array} $arr + */ + private static function ensureTestSteps(array $arr): void + { + if (!array_key_exists('testSteps', $arr)) { + throw new SchemaViolationException('Property \'testSteps\' is required but was not found'); + } + if (array_key_exists('testSteps', $arr) && !is_array($arr['testSteps'])) { + throw new SchemaViolationException('Property \'testSteps\' was not array'); + } + } +} diff --git a/messages/php/src-generated/TestCaseFinished.php b/messages/php/src-generated/TestCaseFinished.php new file mode 100644 index 00000000000..b3faef541d5 --- /dev/null +++ b/messages/php/src-generated/TestCaseFinished.php @@ -0,0 +1,86 @@ + $stepDefinitionIds + */ + public readonly ?array $stepDefinitionIds, + + /** + * A list of list of StepMatchArgument (if derived from a `PickleStep`). + * Each element represents a matching step definition. A size of 0 means `UNDEFINED`, + * and a size of 2+ means `AMBIGUOUS` + * @param ?list $stepMatchArgumentsLists + */ + public readonly ?array $stepMatchArgumentsLists, + + ){} + + /** + * @throws SchemaViolationException + * + * @internal + */ + public static function fromArray(array $arr) : self + { + self::ensureId($arr); + self::ensureStepDefinitionIds($arr); + self::ensureStepMatchArgumentsLists($arr); + + return new self( + isset($arr['hookId']) ? (string) $arr['hookId'] : null, + (string) $arr['id'], + isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, + isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, + isset($arr['stepMatchArgumentsLists']) ? array_map(fn(array $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, + ); + } + + /** + * Check that the type of 'id' matches expectations + * + * @psalm-assert array{id: mixed} $arr + */ + private static function ensureId(array $arr): void + { + if (!array_key_exists('id', $arr)) { + throw new SchemaViolationException('Property \'id\' is required but was not found'); + } + } + + /** + * Check that the type of 'stepDefinitionIds' matches expectations + * + * @psalm-assert array{stepDefinitionIds?: array} $arr + */ + private static function ensureStepDefinitionIds(array $arr): void + { + if (array_key_exists('stepDefinitionIds', $arr) && !is_array($arr['stepDefinitionIds'])) { + throw new SchemaViolationException('Property \'stepDefinitionIds\' was not array'); + } + } + + /** + * Check that the type of 'stepMatchArgumentsLists' matches expectations + * + * @psalm-assert array{stepMatchArgumentsLists?: array} $arr + */ + private static function ensureStepMatchArgumentsLists(array $arr): void + { + if (array_key_exists('stepMatchArgumentsLists', $arr) && !is_array($arr['stepMatchArgumentsLists'])) { + throw new SchemaViolationException('Property \'stepMatchArgumentsLists\' was not array'); + } + } +} diff --git a/messages/php/src-generated/TestStepFinished.php b/messages/php/src-generated/TestStepFinished.php new file mode 100644 index 00000000000..f5b803a107a --- /dev/null +++ b/messages/php/src-generated/TestStepFinished.php @@ -0,0 +1,105 @@ + IDENTITY - * - byte array => BASE64 - * - stream => BASE64 - */ - public readonly Attachment\ContentEncoding $contentEncoding, - - /** - * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) - */ - public readonly ?string $fileName, - - /** - * The media type of the data. This can be any valid - * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) - * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` - * and `text/x.cucumber.stacktrace+plain` - */ - public readonly string $mediaType, - - public readonly ?Source $source, - - public readonly ?string $testCaseStartedId, - - public readonly ?string $testStepId, - - /** - * A URL where the attachment can be retrieved. This field should not be set by Cucumber. - * It should be set by a program that reads a message stream and does the following for - * each Attachment message: - * - * - Writes the body (after base64 decoding if necessary) to a new file. - * - Sets `body` and `contentEncoding` to `null` - * - Writes out the new attachment message - * - * This will result in a smaller message stream, which can improve performance and - * reduce bandwidth of message consumers. It also makes it easier to process and download attachments - * separately from reports. - */ - public readonly ?string $url, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureBody($arr); - self::ensureContentEncoding($arr); - self::ensureMediaType($arr); - self::ensureSource($arr); - - return new self( - (string) $arr['body'], - Attachment\ContentEncoding::from((string) $arr['contentEncoding']), - isset($arr['fileName']) ? (string) $arr['fileName'] : null, - (string) $arr['mediaType'], - isset($arr['source']) ? Source::fromArray($arr['source']) : null, - isset($arr['testCaseStartedId']) ? (string) $arr['testCaseStartedId'] : null, - isset($arr['testStepId']) ? (string) $arr['testStepId'] : null, - isset($arr['url']) ? (string) $arr['url'] : null, - ); - } - - /** - * Check that the type of 'body' matches expectations - * - * @psalm-assert array{body: mixed} $arr - */ - private static function ensureBody(array $arr): void - { - if (!array_key_exists('body', $arr)) { - throw new SchemaViolationException('Property \'body\' is required but was not found'); - } - } - - /** - * Check that the type of 'contentEncoding' matches expectations - * - * @psalm-assert array{contentEncoding: mixed} $arr - */ - private static function ensureContentEncoding(array $arr): void - { - if (!array_key_exists('contentEncoding', $arr)) { - throw new SchemaViolationException('Property \'contentEncoding\' is required but was not found'); - } - } - - /** - * Check that the type of 'mediaType' matches expectations - * - * @psalm-assert array{mediaType: mixed} $arr - */ - private static function ensureMediaType(array $arr): void - { - if (!array_key_exists('mediaType', $arr)) { - throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); - } - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source?: array} $arr - */ - private static function ensureSource(array $arr): void - { - if (array_key_exists('source', $arr) && !is_array($arr['source'])) { - throw new SchemaViolationException('Property \'source\' was not array'); - } - } -} - - -/** - * Represents the Duration message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * The structure is pretty close of the Timestamp one. For clarity, a second type - * of message is used. */ -final class Duration implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly int $seconds, - - /** - * Non-negative fractions of a second at nanosecond resolution. Negative - * second values with fractions must still have non-negative nanos values - * that count forward in time. Must be from 0 to 999,999,999 - * inclusive. - */ - public readonly int $nanos, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureSeconds($arr); - self::ensureNanos($arr); - - return new self( - (int) $arr['seconds'], - (int) $arr['nanos'], - ); - } - - /** - * Check that the type of 'seconds' matches expectations - * - * @psalm-assert array{seconds: mixed} $arr - */ - private static function ensureSeconds(array $arr): void - { - if (!array_key_exists('seconds', $arr)) { - throw new SchemaViolationException('Property \'seconds\' is required but was not found'); - } - } - - /** - * Check that the type of 'nanos' matches expectations - * - * @psalm-assert array{nanos: mixed} $arr - */ - private static function ensureNanos(array $arr): void - { - if (!array_key_exists('nanos', $arr)) { - throw new SchemaViolationException('Property \'nanos\' is required but was not found'); - } - } -} - - -/** - * Represents the Envelope message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * When removing a field, replace it with reserved, rather than deleting the line. - * When adding a field, add it to the end and increment the number by one. - * See https://developers.google.com/protocol-buffers/docs/proto#updating for details - * - * All the messages that are passed between different components/processes are Envelope - * messages. */ -final class Envelope implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?Attachment $attachment, - - public readonly ?GherkinDocument $gherkinDocument, - - public readonly ?Hook $hook, - - public readonly ?Meta $meta, - - public readonly ?ParameterType $parameterType, - - public readonly ?ParseError $parseError, - - public readonly ?Pickle $pickle, - - public readonly ?Source $source, - - public readonly ?StepDefinition $stepDefinition, - - public readonly ?TestCase $testCase, - - public readonly ?TestCaseFinished $testCaseFinished, - - public readonly ?TestCaseStarted $testCaseStarted, - - public readonly ?TestRunFinished $testRunFinished, - - public readonly ?TestRunStarted $testRunStarted, - - public readonly ?TestStepFinished $testStepFinished, - - public readonly ?TestStepStarted $testStepStarted, - - public readonly ?UndefinedParameterType $undefinedParameterType, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureAttachment($arr); - self::ensureGherkinDocument($arr); - self::ensureHook($arr); - self::ensureMeta($arr); - self::ensureParameterType($arr); - self::ensureParseError($arr); - self::ensurePickle($arr); - self::ensureSource($arr); - self::ensureStepDefinition($arr); - self::ensureTestCase($arr); - self::ensureTestCaseFinished($arr); - self::ensureTestCaseStarted($arr); - self::ensureTestRunFinished($arr); - self::ensureTestRunStarted($arr); - self::ensureTestStepFinished($arr); - self::ensureTestStepStarted($arr); - self::ensureUndefinedParameterType($arr); - - return new self( - isset($arr['attachment']) ? Attachment::fromArray($arr['attachment']) : null, - isset($arr['gherkinDocument']) ? GherkinDocument::fromArray($arr['gherkinDocument']) : null, - isset($arr['hook']) ? Hook::fromArray($arr['hook']) : null, - isset($arr['meta']) ? Meta::fromArray($arr['meta']) : null, - isset($arr['parameterType']) ? ParameterType::fromArray($arr['parameterType']) : null, - isset($arr['parseError']) ? ParseError::fromArray($arr['parseError']) : null, - isset($arr['pickle']) ? Pickle::fromArray($arr['pickle']) : null, - isset($arr['source']) ? Source::fromArray($arr['source']) : null, - isset($arr['stepDefinition']) ? StepDefinition::fromArray($arr['stepDefinition']) : null, - isset($arr['testCase']) ? TestCase::fromArray($arr['testCase']) : null, - isset($arr['testCaseFinished']) ? TestCaseFinished::fromArray($arr['testCaseFinished']) : null, - isset($arr['testCaseStarted']) ? TestCaseStarted::fromArray($arr['testCaseStarted']) : null, - isset($arr['testRunFinished']) ? TestRunFinished::fromArray($arr['testRunFinished']) : null, - isset($arr['testRunStarted']) ? TestRunStarted::fromArray($arr['testRunStarted']) : null, - isset($arr['testStepFinished']) ? TestStepFinished::fromArray($arr['testStepFinished']) : null, - isset($arr['testStepStarted']) ? TestStepStarted::fromArray($arr['testStepStarted']) : null, - isset($arr['undefinedParameterType']) ? UndefinedParameterType::fromArray($arr['undefinedParameterType']) : null, - ); - } - - /** - * Check that the type of 'attachment' matches expectations - * - * @psalm-assert array{attachment?: array} $arr - */ - private static function ensureAttachment(array $arr): void - { - if (array_key_exists('attachment', $arr) && !is_array($arr['attachment'])) { - throw new SchemaViolationException('Property \'attachment\' was not array'); - } - } - - /** - * Check that the type of 'gherkinDocument' matches expectations - * - * @psalm-assert array{gherkinDocument?: array} $arr - */ - private static function ensureGherkinDocument(array $arr): void - { - if (array_key_exists('gherkinDocument', $arr) && !is_array($arr['gherkinDocument'])) { - throw new SchemaViolationException('Property \'gherkinDocument\' was not array'); - } - } - - /** - * Check that the type of 'hook' matches expectations - * - * @psalm-assert array{hook?: array} $arr - */ - private static function ensureHook(array $arr): void - { - if (array_key_exists('hook', $arr) && !is_array($arr['hook'])) { - throw new SchemaViolationException('Property \'hook\' was not array'); - } - } - - /** - * Check that the type of 'meta' matches expectations - * - * @psalm-assert array{meta?: array} $arr - */ - private static function ensureMeta(array $arr): void - { - if (array_key_exists('meta', $arr) && !is_array($arr['meta'])) { - throw new SchemaViolationException('Property \'meta\' was not array'); - } - } - - /** - * Check that the type of 'parameterType' matches expectations - * - * @psalm-assert array{parameterType?: array} $arr - */ - private static function ensureParameterType(array $arr): void - { - if (array_key_exists('parameterType', $arr) && !is_array($arr['parameterType'])) { - throw new SchemaViolationException('Property \'parameterType\' was not array'); - } - } - - /** - * Check that the type of 'parseError' matches expectations - * - * @psalm-assert array{parseError?: array} $arr - */ - private static function ensureParseError(array $arr): void - { - if (array_key_exists('parseError', $arr) && !is_array($arr['parseError'])) { - throw new SchemaViolationException('Property \'parseError\' was not array'); - } - } - - /** - * Check that the type of 'pickle' matches expectations - * - * @psalm-assert array{pickle?: array} $arr - */ - private static function ensurePickle(array $arr): void - { - if (array_key_exists('pickle', $arr) && !is_array($arr['pickle'])) { - throw new SchemaViolationException('Property \'pickle\' was not array'); - } - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source?: array} $arr - */ - private static function ensureSource(array $arr): void - { - if (array_key_exists('source', $arr) && !is_array($arr['source'])) { - throw new SchemaViolationException('Property \'source\' was not array'); - } - } - - /** - * Check that the type of 'stepDefinition' matches expectations - * - * @psalm-assert array{stepDefinition?: array} $arr - */ - private static function ensureStepDefinition(array $arr): void - { - if (array_key_exists('stepDefinition', $arr) && !is_array($arr['stepDefinition'])) { - throw new SchemaViolationException('Property \'stepDefinition\' was not array'); - } - } - - /** - * Check that the type of 'testCase' matches expectations - * - * @psalm-assert array{testCase?: array} $arr - */ - private static function ensureTestCase(array $arr): void - { - if (array_key_exists('testCase', $arr) && !is_array($arr['testCase'])) { - throw new SchemaViolationException('Property \'testCase\' was not array'); - } - } - - /** - * Check that the type of 'testCaseFinished' matches expectations - * - * @psalm-assert array{testCaseFinished?: array} $arr - */ - private static function ensureTestCaseFinished(array $arr): void - { - if (array_key_exists('testCaseFinished', $arr) && !is_array($arr['testCaseFinished'])) { - throw new SchemaViolationException('Property \'testCaseFinished\' was not array'); - } - } - - /** - * Check that the type of 'testCaseStarted' matches expectations - * - * @psalm-assert array{testCaseStarted?: array} $arr - */ - private static function ensureTestCaseStarted(array $arr): void - { - if (array_key_exists('testCaseStarted', $arr) && !is_array($arr['testCaseStarted'])) { - throw new SchemaViolationException('Property \'testCaseStarted\' was not array'); - } - } - - /** - * Check that the type of 'testRunFinished' matches expectations - * - * @psalm-assert array{testRunFinished?: array} $arr - */ - private static function ensureTestRunFinished(array $arr): void - { - if (array_key_exists('testRunFinished', $arr) && !is_array($arr['testRunFinished'])) { - throw new SchemaViolationException('Property \'testRunFinished\' was not array'); - } - } - - /** - * Check that the type of 'testRunStarted' matches expectations - * - * @psalm-assert array{testRunStarted?: array} $arr - */ - private static function ensureTestRunStarted(array $arr): void - { - if (array_key_exists('testRunStarted', $arr) && !is_array($arr['testRunStarted'])) { - throw new SchemaViolationException('Property \'testRunStarted\' was not array'); - } - } - - /** - * Check that the type of 'testStepFinished' matches expectations - * - * @psalm-assert array{testStepFinished?: array} $arr - */ - private static function ensureTestStepFinished(array $arr): void - { - if (array_key_exists('testStepFinished', $arr) && !is_array($arr['testStepFinished'])) { - throw new SchemaViolationException('Property \'testStepFinished\' was not array'); - } - } - - /** - * Check that the type of 'testStepStarted' matches expectations - * - * @psalm-assert array{testStepStarted?: array} $arr - */ - private static function ensureTestStepStarted(array $arr): void - { - if (array_key_exists('testStepStarted', $arr) && !is_array($arr['testStepStarted'])) { - throw new SchemaViolationException('Property \'testStepStarted\' was not array'); - } - } - - /** - * Check that the type of 'undefinedParameterType' matches expectations - * - * @psalm-assert array{undefinedParameterType?: array} $arr - */ - private static function ensureUndefinedParameterType(array $arr): void - { - if (array_key_exists('undefinedParameterType', $arr) && !is_array($arr['undefinedParameterType'])) { - throw new SchemaViolationException('Property \'undefinedParameterType\' was not array'); - } - } -} - - -/** - * Represents the GherkinDocument message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document. - * Cucumber implementations should *not* depend on `GherkinDocument` or any of its - * children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead. - * - * The only consumers of `GherkinDocument` should only be formatters that produce - * "rich" output, resembling the original Gherkin document. */ -final class GherkinDocument implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) - * of the source, typically a file path relative to the root directory - */ - public readonly ?string $uri, - - public readonly ?Feature $feature, - - /** - * All the comments in the Gherkin document - * @param list $comments - */ - public readonly array $comments, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureFeature($arr); - self::ensureComments($arr); - - return new self( - isset($arr['uri']) ? (string) $arr['uri'] : null, - isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null, - array_map(fn(array $member) => Comment::fromArray($member) , $arr['comments']), - ); - } - - /** - * Check that the type of 'feature' matches expectations - * - * @psalm-assert array{feature?: array} $arr - */ - private static function ensureFeature(array $arr): void - { - if (array_key_exists('feature', $arr) && !is_array($arr['feature'])) { - throw new SchemaViolationException('Property \'feature\' was not array'); - } - } - - /** - * Check that the type of 'comments' matches expectations - * - * @psalm-assert array{comments: array} $arr - */ - private static function ensureComments(array $arr): void - { - if (!array_key_exists('comments', $arr)) { - throw new SchemaViolationException('Property \'comments\' is required but was not found'); - } - if (array_key_exists('comments', $arr) && !is_array($arr['comments'])) { - throw new SchemaViolationException('Property \'comments\' was not array'); - } - } -} - - -/** - * Represents the Background message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Background implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the `Background` keyword - */ - public readonly Location $location, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - /** - * @param list $steps - */ - public readonly array $steps, - - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureSteps($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'steps' matches expectations - * - * @psalm-assert array{steps: array} $arr - */ - private static function ensureSteps(array $arr): void - { - if (!array_key_exists('steps', $arr)) { - throw new SchemaViolationException('Property \'steps\' is required but was not found'); - } - if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { - throw new SchemaViolationException('Property \'steps\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the Comment message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A comment in a Gherkin document */ -final class Comment implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the comment - */ - public readonly Location $location, - - /** - * The text of the comment - */ - public readonly string $text, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureText($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['text'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr - */ - private static function ensureText(array $arr): void - { - if (!array_key_exists('text', $arr)) { - throw new SchemaViolationException('Property \'text\' is required but was not found'); - } - } -} - - -/** - * Represents the DataTable message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class DataTable implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly Location $location, - - /** - * @param list $rows - */ - public readonly array $rows, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureRows($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => TableRow::fromArray($member) , $arr['rows']), - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'rows' matches expectations - * - * @psalm-assert array{rows: array} $arr - */ - private static function ensureRows(array $arr): void - { - if (!array_key_exists('rows', $arr)) { - throw new SchemaViolationException('Property \'rows\' is required but was not found'); - } - if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { - throw new SchemaViolationException('Property \'rows\' was not array'); - } - } -} - - -/** - * Represents the DocString message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class DocString implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly Location $location, - - public readonly ?string $mediaType, - - public readonly string $content, - - public readonly string $delimiter, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureContent($arr); - self::ensureDelimiter($arr); - - return new self( - Location::fromArray($arr['location']), - isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, - (string) $arr['content'], - (string) $arr['delimiter'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'content' matches expectations - * - * @psalm-assert array{content: mixed} $arr - */ - private static function ensureContent(array $arr): void - { - if (!array_key_exists('content', $arr)) { - throw new SchemaViolationException('Property \'content\' is required but was not found'); - } - } - - /** - * Check that the type of 'delimiter' matches expectations - * - * @psalm-assert array{delimiter: mixed} $arr - */ - private static function ensureDelimiter(array $arr): void - { - if (!array_key_exists('delimiter', $arr)) { - throw new SchemaViolationException('Property \'delimiter\' is required but was not found'); - } - } -} - - -/** - * Represents the Examples message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Examples implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the `Examples` keyword - */ - public readonly Location $location, - - /** - * @param list $tags - */ - public readonly array $tags, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - public readonly ?TableRow $tableHeader, - - /** - * @param list $tableBody - */ - public readonly array $tableBody, - - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureTableHeader($arr); - self::ensureTableBody($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, - array_map(fn(array $member) => TableRow::fromArray($member) , $arr['tableBody']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'tableHeader' matches expectations - * - * @psalm-assert array{tableHeader?: array} $arr - */ - private static function ensureTableHeader(array $arr): void - { - if (array_key_exists('tableHeader', $arr) && !is_array($arr['tableHeader'])) { - throw new SchemaViolationException('Property \'tableHeader\' was not array'); - } - } - - /** - * Check that the type of 'tableBody' matches expectations - * - * @psalm-assert array{tableBody: array} $arr - */ - private static function ensureTableBody(array $arr): void - { - if (!array_key_exists('tableBody', $arr)) { - throw new SchemaViolationException('Property \'tableBody\' is required but was not found'); - } - if (array_key_exists('tableBody', $arr) && !is_array($arr['tableBody'])) { - throw new SchemaViolationException('Property \'tableBody\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the Feature message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Feature implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the `Feature` keyword - */ - public readonly Location $location, - - /** - * All the tags placed above the `Feature` keyword - * @param list $tags - */ - public readonly array $tags, - - /** - * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document - */ - public readonly string $language, - - /** - * The text of the `Feature` keyword (in the language specified by `language`) - */ - public readonly string $keyword, - - /** - * The name of the feature (the text following the `keyword`) - */ - public readonly string $name, - - /** - * The line(s) underneath the line with the `keyword` that are used as description - */ - public readonly string $description, - - /** - * Zero or more children - * @param list $children - */ - public readonly array $children, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureLanguage($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureChildren($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['language'], - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => FeatureChild::fromArray($member) , $arr['children']), - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'language' matches expectations - * - * @psalm-assert array{language: mixed} $arr - */ - private static function ensureLanguage(array $arr): void - { - if (!array_key_exists('language', $arr)) { - throw new SchemaViolationException('Property \'language\' is required but was not found'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'children' matches expectations - * - * @psalm-assert array{children: array} $arr - */ - private static function ensureChildren(array $arr): void - { - if (!array_key_exists('children', $arr)) { - throw new SchemaViolationException('Property \'children\' is required but was not found'); - } - if (array_key_exists('children', $arr) && !is_array($arr['children'])) { - throw new SchemaViolationException('Property \'children\' was not array'); - } - } -} - - -/** - * Represents the FeatureChild message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A child node of a `Feature` node */ -final class FeatureChild implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?Rule $rule, - - public readonly ?Background $background, - - public readonly ?Scenario $scenario, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureRule($arr); - self::ensureBackground($arr); - self::ensureScenario($arr); - - return new self( - isset($arr['rule']) ? Rule::fromArray($arr['rule']) : null, - isset($arr['background']) ? Background::fromArray($arr['background']) : null, - isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, - ); - } - - /** - * Check that the type of 'rule' matches expectations - * - * @psalm-assert array{rule?: array} $arr - */ - private static function ensureRule(array $arr): void - { - if (array_key_exists('rule', $arr) && !is_array($arr['rule'])) { - throw new SchemaViolationException('Property \'rule\' was not array'); - } - } - - /** - * Check that the type of 'background' matches expectations - * - * @psalm-assert array{background?: array} $arr - */ - private static function ensureBackground(array $arr): void - { - if (array_key_exists('background', $arr) && !is_array($arr['background'])) { - throw new SchemaViolationException('Property \'background\' was not array'); - } - } - - /** - * Check that the type of 'scenario' matches expectations - * - * @psalm-assert array{scenario?: array} $arr - */ - private static function ensureScenario(array $arr): void - { - if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { - throw new SchemaViolationException('Property \'scenario\' was not array'); - } - } -} - - -/** - * Represents the Rule message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Rule implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the `Rule` keyword - */ - public readonly Location $location, - - /** - * All the tags placed above the `Rule` keyword - * @param list $tags - */ - public readonly array $tags, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - /** - * @param list $children - */ - public readonly array $children, - - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureChildren($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => RuleChild::fromArray($member) , $arr['children']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'children' matches expectations - * - * @psalm-assert array{children: array} $arr - */ - private static function ensureChildren(array $arr): void - { - if (!array_key_exists('children', $arr)) { - throw new SchemaViolationException('Property \'children\' is required but was not found'); - } - if (array_key_exists('children', $arr) && !is_array($arr['children'])) { - throw new SchemaViolationException('Property \'children\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the RuleChild message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A child node of a `Rule` node */ -final class RuleChild implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?Background $background, - - public readonly ?Scenario $scenario, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureBackground($arr); - self::ensureScenario($arr); - - return new self( - isset($arr['background']) ? Background::fromArray($arr['background']) : null, - isset($arr['scenario']) ? Scenario::fromArray($arr['scenario']) : null, - ); - } - - /** - * Check that the type of 'background' matches expectations - * - * @psalm-assert array{background?: array} $arr - */ - private static function ensureBackground(array $arr): void - { - if (array_key_exists('background', $arr) && !is_array($arr['background'])) { - throw new SchemaViolationException('Property \'background\' was not array'); - } - } - - /** - * Check that the type of 'scenario' matches expectations - * - * @psalm-assert array{scenario?: array} $arr - */ - private static function ensureScenario(array $arr): void - { - if (array_key_exists('scenario', $arr) && !is_array($arr['scenario'])) { - throw new SchemaViolationException('Property \'scenario\' was not array'); - } - } -} - - -/** - * Represents the Scenario message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Scenario implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the `Scenario` keyword - */ - public readonly Location $location, - - /** - * @param list $tags - */ - public readonly array $tags, - - public readonly string $keyword, - - public readonly string $name, - - public readonly string $description, - - /** - * @param list $steps - */ - public readonly array $steps, - - /** - * @param list $examples - */ - public readonly array $examples, - - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureTags($arr); - self::ensureKeyword($arr); - self::ensureName($arr); - self::ensureDescription($arr); - self::ensureSteps($arr); - self::ensureExamples($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), - (string) $arr['keyword'], - (string) $arr['name'], - (string) $arr['description'], - array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), - array_map(fn(array $member) => Examples::fromArray($member) , $arr['examples']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr - */ - private static function ensureDescription(array $arr): void - { - if (!array_key_exists('description', $arr)) { - throw new SchemaViolationException('Property \'description\' is required but was not found'); - } - } - - /** - * Check that the type of 'steps' matches expectations - * - * @psalm-assert array{steps: array} $arr - */ - private static function ensureSteps(array $arr): void - { - if (!array_key_exists('steps', $arr)) { - throw new SchemaViolationException('Property \'steps\' is required but was not found'); - } - if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { - throw new SchemaViolationException('Property \'steps\' was not array'); - } - } - - /** - * Check that the type of 'examples' matches expectations - * - * @psalm-assert array{examples: array} $arr - */ - private static function ensureExamples(array $arr): void - { - if (!array_key_exists('examples', $arr)) { - throw new SchemaViolationException('Property \'examples\' is required but was not found'); - } - if (array_key_exists('examples', $arr) && !is_array($arr['examples'])) { - throw new SchemaViolationException('Property \'examples\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the Step message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A step */ -final class Step implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the steps' `keyword` - */ - public readonly Location $location, - - public readonly string $keyword, - - public readonly string $text, - - public readonly ?DocString $docString, - - public readonly ?DataTable $dataTable, - - /** - * Unique ID to be able to reference the Step from PickleStep - */ - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureKeyword($arr); - self::ensureText($arr); - self::ensureDocString($arr); - self::ensureDataTable($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['keyword'], - (string) $arr['text'], - isset($arr['docString']) ? DocString::fromArray($arr['docString']) : null, - isset($arr['dataTable']) ? DataTable::fromArray($arr['dataTable']) : null, - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr - */ - private static function ensureKeyword(array $arr): void - { - if (!array_key_exists('keyword', $arr)) { - throw new SchemaViolationException('Property \'keyword\' is required but was not found'); - } - } - - /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr - */ - private static function ensureText(array $arr): void - { - if (!array_key_exists('text', $arr)) { - throw new SchemaViolationException('Property \'text\' is required but was not found'); - } - } - - /** - * Check that the type of 'docString' matches expectations - * - * @psalm-assert array{docString?: array} $arr - */ - private static function ensureDocString(array $arr): void - { - if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { - throw new SchemaViolationException('Property \'docString\' was not array'); - } - } - - /** - * Check that the type of 'dataTable' matches expectations - * - * @psalm-assert array{dataTable?: array} $arr - */ - private static function ensureDataTable(array $arr): void - { - if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { - throw new SchemaViolationException('Property \'dataTable\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the TableCell message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A cell in a `TableRow` */ -final class TableCell implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the cell - */ - public readonly Location $location, - - /** - * The value of the cell - */ - public readonly string $value, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureValue($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['value'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'value' matches expectations - * - * @psalm-assert array{value: mixed} $arr - */ - private static function ensureValue(array $arr): void - { - if (!array_key_exists('value', $arr)) { - throw new SchemaViolationException('Property \'value\' is required but was not found'); - } - } -} - - -/** - * Represents the TableRow message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A row in a table */ -final class TableRow implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The location of the first cell in the row - */ - public readonly Location $location, - - /** - * Cells in the row - * @param list $cells - */ - public readonly array $cells, - - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureCells($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - array_map(fn(array $member) => TableCell::fromArray($member) , $arr['cells']), - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'cells' matches expectations - * - * @psalm-assert array{cells: array} $arr - */ - private static function ensureCells(array $arr): void - { - if (!array_key_exists('cells', $arr)) { - throw new SchemaViolationException('Property \'cells\' is required but was not found'); - } - if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { - throw new SchemaViolationException('Property \'cells\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the Tag message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A tag */ -final class Tag implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * Location of the tag - */ - public readonly Location $location, - - /** - * The name of the tag (including the leading `@`) - */ - public readonly string $name, - - /** - * Unique ID to be able to reference the Tag from PickleTag - */ - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLocation($arr); - self::ensureName($arr); - self::ensureId($arr); - - return new self( - Location::fromArray($arr['location']), - (string) $arr['name'], - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (!array_key_exists('location', $arr)) { - throw new SchemaViolationException('Property \'location\' is required but was not found'); - } - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the Hook message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Hook implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $id, - - public readonly SourceReference $sourceReference, - - public readonly ?string $tagExpression, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureId($arr); - self::ensureSourceReference($arr); - - return new self( - (string) $arr['id'], - SourceReference::fromArray($arr['sourceReference']), - isset($arr['tagExpression']) ? (string) $arr['tagExpression'] : null, - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'sourceReference' matches expectations - * - * @psalm-assert array{sourceReference: array} $arr - */ - private static function ensureSourceReference(array $arr): void - { - if (!array_key_exists('sourceReference', $arr)) { - throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); - } - if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { - throw new SchemaViolationException('Property \'sourceReference\' was not array'); - } - } -} - - -/** - * Represents the Location message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * Points to a line and a column in a text file */ -final class Location implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly int $line, - - public readonly ?int $column, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureLine($arr); - - return new self( - (int) $arr['line'], - isset($arr['column']) ? (int) $arr['column'] : null, - ); - } - - /** - * Check that the type of 'line' matches expectations - * - * @psalm-assert array{line: mixed} $arr - */ - private static function ensureLine(array $arr): void - { - if (!array_key_exists('line', $arr)) { - throw new SchemaViolationException('Property \'line\' is required but was not found'); - } - } -} - - -/** - * Represents the Meta message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * This message contains meta information about the environment. Consumers can use - * this for various purposes. */ -final class Meta implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The [SEMVER](https://semver.org/) version number of the protocol - */ - public readonly string $protocolVersion, - - /** - * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. - */ - public readonly Product $implementation, - - /** - * Java, Ruby, Node.js etc - */ - public readonly Product $runtime, - - /** - * Windows, Linux, MacOS etc - */ - public readonly Product $os, - - /** - * 386, arm, amd64 etc - */ - public readonly Product $cpu, - - public readonly ?Ci $ci, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureProtocolVersion($arr); - self::ensureImplementation($arr); - self::ensureRuntime($arr); - self::ensureOs($arr); - self::ensureCpu($arr); - self::ensureCi($arr); - - return new self( - (string) $arr['protocolVersion'], - Product::fromArray($arr['implementation']), - Product::fromArray($arr['runtime']), - Product::fromArray($arr['os']), - Product::fromArray($arr['cpu']), - isset($arr['ci']) ? Ci::fromArray($arr['ci']) : null, - ); - } - - /** - * Check that the type of 'protocolVersion' matches expectations - * - * @psalm-assert array{protocolVersion: mixed} $arr - */ - private static function ensureProtocolVersion(array $arr): void - { - if (!array_key_exists('protocolVersion', $arr)) { - throw new SchemaViolationException('Property \'protocolVersion\' is required but was not found'); - } - } - - /** - * Check that the type of 'implementation' matches expectations - * - * @psalm-assert array{implementation: array} $arr - */ - private static function ensureImplementation(array $arr): void - { - if (!array_key_exists('implementation', $arr)) { - throw new SchemaViolationException('Property \'implementation\' is required but was not found'); - } - if (array_key_exists('implementation', $arr) && !is_array($arr['implementation'])) { - throw new SchemaViolationException('Property \'implementation\' was not array'); - } - } - - /** - * Check that the type of 'runtime' matches expectations - * - * @psalm-assert array{runtime: array} $arr - */ - private static function ensureRuntime(array $arr): void - { - if (!array_key_exists('runtime', $arr)) { - throw new SchemaViolationException('Property \'runtime\' is required but was not found'); - } - if (array_key_exists('runtime', $arr) && !is_array($arr['runtime'])) { - throw new SchemaViolationException('Property \'runtime\' was not array'); - } - } - - /** - * Check that the type of 'os' matches expectations - * - * @psalm-assert array{os: array} $arr - */ - private static function ensureOs(array $arr): void - { - if (!array_key_exists('os', $arr)) { - throw new SchemaViolationException('Property \'os\' is required but was not found'); - } - if (array_key_exists('os', $arr) && !is_array($arr['os'])) { - throw new SchemaViolationException('Property \'os\' was not array'); - } - } - - /** - * Check that the type of 'cpu' matches expectations - * - * @psalm-assert array{cpu: array} $arr - */ - private static function ensureCpu(array $arr): void - { - if (!array_key_exists('cpu', $arr)) { - throw new SchemaViolationException('Property \'cpu\' is required but was not found'); - } - if (array_key_exists('cpu', $arr) && !is_array($arr['cpu'])) { - throw new SchemaViolationException('Property \'cpu\' was not array'); - } - } - - /** - * Check that the type of 'ci' matches expectations - * - * @psalm-assert array{ci?: array} $arr - */ - private static function ensureCi(array $arr): void - { - if (array_key_exists('ci', $arr) && !is_array($arr['ci'])) { - throw new SchemaViolationException('Property \'ci\' was not array'); - } - } -} - - -/** - * Represents the Ci message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * CI environment */ -final class Ci implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. - */ - public readonly string $name, - - /** - * Link to the build - */ - public readonly ?string $url, - - /** - * The build number. Some CI servers use non-numeric build numbers, which is why this is a string - */ - public readonly ?string $buildNumber, - - public readonly ?Git $git, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureName($arr); - self::ensureGit($arr); - - return new self( - (string) $arr['name'], - isset($arr['url']) ? (string) $arr['url'] : null, - isset($arr['buildNumber']) ? (string) $arr['buildNumber'] : null, - isset($arr['git']) ? Git::fromArray($arr['git']) : null, - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'git' matches expectations - * - * @psalm-assert array{git?: array} $arr - */ - private static function ensureGit(array $arr): void - { - if (array_key_exists('git', $arr) && !is_array($arr['git'])) { - throw new SchemaViolationException('Property \'git\' was not array'); - } - } -} - - -/** - * Represents the Git message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * Information about Git, provided by the Build/CI server as environment - * variables. */ -final class Git implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $remote, - - public readonly string $revision, - - public readonly ?string $branch, - - public readonly ?string $tag, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureRemote($arr); - self::ensureRevision($arr); - - return new self( - (string) $arr['remote'], - (string) $arr['revision'], - isset($arr['branch']) ? (string) $arr['branch'] : null, - isset($arr['tag']) ? (string) $arr['tag'] : null, - ); - } - - /** - * Check that the type of 'remote' matches expectations - * - * @psalm-assert array{remote: mixed} $arr - */ - private static function ensureRemote(array $arr): void - { - if (!array_key_exists('remote', $arr)) { - throw new SchemaViolationException('Property \'remote\' is required but was not found'); - } - } - - /** - * Check that the type of 'revision' matches expectations - * - * @psalm-assert array{revision: mixed} $arr - */ - private static function ensureRevision(array $arr): void - { - if (!array_key_exists('revision', $arr)) { - throw new SchemaViolationException('Property \'revision\' is required but was not found'); - } - } -} - - -/** - * Represents the Product message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * Used to describe various properties of Meta */ -final class Product implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The product name - */ - public readonly string $name, - - /** - * The product version - */ - public readonly ?string $version, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureName($arr); - - return new self( - (string) $arr['name'], - isset($arr['version']) ? (string) $arr['version'] : null, - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } -} - - -/** - * Represents the ParameterType message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class ParameterType implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The name is unique, so we don't need an id. - */ - public readonly string $name, - - /** - * @param list $regularExpressions - */ - public readonly array $regularExpressions, - - public readonly bool $preferForRegularExpressionMatch, - - public readonly bool $useForSnippets, - - public readonly string $id, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureName($arr); - self::ensureRegularExpressions($arr); - self::ensurePreferForRegularExpressionMatch($arr); - self::ensureUseForSnippets($arr); - self::ensureId($arr); - - return new self( - (string) $arr['name'], - array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), - (bool) $arr['preferForRegularExpressionMatch'], - (bool) $arr['useForSnippets'], - (string) $arr['id'], - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'regularExpressions' matches expectations - * - * @psalm-assert array{regularExpressions: array} $arr - */ - private static function ensureRegularExpressions(array $arr): void - { - if (!array_key_exists('regularExpressions', $arr)) { - throw new SchemaViolationException('Property \'regularExpressions\' is required but was not found'); - } - if (array_key_exists('regularExpressions', $arr) && !is_array($arr['regularExpressions'])) { - throw new SchemaViolationException('Property \'regularExpressions\' was not array'); - } - } - - /** - * Check that the type of 'preferForRegularExpressionMatch' matches expectations - * - * @psalm-assert array{preferForRegularExpressionMatch: mixed} $arr - */ - private static function ensurePreferForRegularExpressionMatch(array $arr): void - { - if (!array_key_exists('preferForRegularExpressionMatch', $arr)) { - throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' is required but was not found'); - } - } - - /** - * Check that the type of 'useForSnippets' matches expectations - * - * @psalm-assert array{useForSnippets: mixed} $arr - */ - private static function ensureUseForSnippets(array $arr): void - { - if (!array_key_exists('useForSnippets', $arr)) { - throw new SchemaViolationException('Property \'useForSnippets\' is required but was not found'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } -} - - -/** - * Represents the ParseError message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class ParseError implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly SourceReference $source, - - public readonly string $message, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureSource($arr); - self::ensureMessage($arr); - - return new self( - SourceReference::fromArray($arr['source']), - (string) $arr['message'], - ); - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source: array} $arr - */ - private static function ensureSource(array $arr): void - { - if (!array_key_exists('source', $arr)) { - throw new SchemaViolationException('Property \'source\' is required but was not found'); - } - if (array_key_exists('source', $arr) && !is_array($arr['source'])) { - throw new SchemaViolationException('Property \'source\' was not array'); - } - } - - /** - * Check that the type of 'message' matches expectations - * - * @psalm-assert array{message: mixed} $arr - */ - private static function ensureMessage(array $arr): void - { - if (!array_key_exists('message', $arr)) { - throw new SchemaViolationException('Property \'message\' is required but was not found'); - } - } -} - - -/** - * Represents the Pickle message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * //// Pickles - * - * A `Pickle` represents a template for a `TestCase`. It is typically derived - * from another format, such as [GherkinDocument](#io.cucumber.messages.GherkinDocument). - * In the future a `Pickle` may be derived from other formats such as Markdown or - * Excel files. - * - * By making `Pickle` the main data structure Cucumber uses for execution, the - * implementation of Cucumber itself becomes simpler, as it doesn't have to deal - * with the complex structure of a [GherkinDocument](#io.cucumber.messages.GherkinDocument). - * - * Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` */ -final class Pickle implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash - * from the source data and the `locations` of the pickle. - * This ID will change if source the file is modified. - */ - public readonly string $id, - - /** - * The uri of the source file - */ - public readonly string $uri, - - /** - * The name of the pickle - */ - public readonly string $name, - - /** - * The language of the pickle - */ - public readonly string $language, - - /** - * One or more steps - * @param list $steps - */ - public readonly array $steps, - - /** - * One or more tags. If this pickle is constructed from a Gherkin document, - * It includes inherited tags from the `Feature` as well. - * @param list $tags - */ - public readonly array $tags, - - /** - * Points to the AST node locations of the pickle. The last one represents the unique - * id of the pickle. A pickle constructed from `Examples` will have the first - * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. - * @param list $astNodeIds - */ - public readonly array $astNodeIds, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureId($arr); - self::ensureUri($arr); - self::ensureName($arr); - self::ensureLanguage($arr); - self::ensureSteps($arr); - self::ensureTags($arr); - self::ensureAstNodeIds($arr); - - return new self( - (string) $arr['id'], - (string) $arr['uri'], - (string) $arr['name'], - (string) $arr['language'], - array_map(fn(array $member) => PickleStep::fromArray($member) , $arr['steps']), - array_map(fn(array $member) => PickleTag::fromArray($member) , $arr['tags']), - array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'uri' matches expectations - * - * @psalm-assert array{uri: mixed} $arr - */ - private static function ensureUri(array $arr): void - { - if (!array_key_exists('uri', $arr)) { - throw new SchemaViolationException('Property \'uri\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'language' matches expectations - * - * @psalm-assert array{language: mixed} $arr - */ - private static function ensureLanguage(array $arr): void - { - if (!array_key_exists('language', $arr)) { - throw new SchemaViolationException('Property \'language\' is required but was not found'); - } - } - - /** - * Check that the type of 'steps' matches expectations - * - * @psalm-assert array{steps: array} $arr - */ - private static function ensureSteps(array $arr): void - { - if (!array_key_exists('steps', $arr)) { - throw new SchemaViolationException('Property \'steps\' is required but was not found'); - } - if (array_key_exists('steps', $arr) && !is_array($arr['steps'])) { - throw new SchemaViolationException('Property \'steps\' was not array'); - } - } - - /** - * Check that the type of 'tags' matches expectations - * - * @psalm-assert array{tags: array} $arr - */ - private static function ensureTags(array $arr): void - { - if (!array_key_exists('tags', $arr)) { - throw new SchemaViolationException('Property \'tags\' is required but was not found'); - } - if (array_key_exists('tags', $arr) && !is_array($arr['tags'])) { - throw new SchemaViolationException('Property \'tags\' was not array'); - } - } - - /** - * Check that the type of 'astNodeIds' matches expectations - * - * @psalm-assert array{astNodeIds: array} $arr - */ - private static function ensureAstNodeIds(array $arr): void - { - if (!array_key_exists('astNodeIds', $arr)) { - throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); - } - if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { - throw new SchemaViolationException('Property \'astNodeIds\' was not array'); - } - } -} - - -/** - * Represents the PickleDocString message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class PickleDocString implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?string $mediaType, - - public readonly string $content, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureContent($arr); - - return new self( - isset($arr['mediaType']) ? (string) $arr['mediaType'] : null, - (string) $arr['content'], - ); - } - - /** - * Check that the type of 'content' matches expectations - * - * @psalm-assert array{content: mixed} $arr - */ - private static function ensureContent(array $arr): void - { - if (!array_key_exists('content', $arr)) { - throw new SchemaViolationException('Property \'content\' is required but was not found'); - } - } -} - - -/** - * Represents the PickleStep message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * An executable step */ -final class PickleStep implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?PickleStepArgument $argument, - - /** - * References the IDs of the source of the step. For Gherkin, this can be - * the ID of a Step, and possibly also the ID of a TableRow - * @param list $astNodeIds - */ - public readonly array $astNodeIds, - - /** - * A unique ID for the PickleStep - */ - public readonly string $id, - - public readonly string $text, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureArgument($arr); - self::ensureAstNodeIds($arr); - self::ensureId($arr); - self::ensureText($arr); - - return new self( - isset($arr['argument']) ? PickleStepArgument::fromArray($arr['argument']) : null, - array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), - (string) $arr['id'], - (string) $arr['text'], - ); - } - - /** - * Check that the type of 'argument' matches expectations - * - * @psalm-assert array{argument?: array} $arr - */ - private static function ensureArgument(array $arr): void - { - if (array_key_exists('argument', $arr) && !is_array($arr['argument'])) { - throw new SchemaViolationException('Property \'argument\' was not array'); - } - } - - /** - * Check that the type of 'astNodeIds' matches expectations - * - * @psalm-assert array{astNodeIds: array} $arr - */ - private static function ensureAstNodeIds(array $arr): void - { - if (!array_key_exists('astNodeIds', $arr)) { - throw new SchemaViolationException('Property \'astNodeIds\' is required but was not found'); - } - if (array_key_exists('astNodeIds', $arr) && !is_array($arr['astNodeIds'])) { - throw new SchemaViolationException('Property \'astNodeIds\' was not array'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr - */ - private static function ensureText(array $arr): void - { - if (!array_key_exists('text', $arr)) { - throw new SchemaViolationException('Property \'text\' is required but was not found'); - } - } -} - - -/** - * Represents the PickleStepArgument message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * An optional argument */ -final class PickleStepArgument implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?PickleDocString $docString, - - public readonly ?PickleTable $dataTable, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureDocString($arr); - self::ensureDataTable($arr); - - return new self( - isset($arr['docString']) ? PickleDocString::fromArray($arr['docString']) : null, - isset($arr['dataTable']) ? PickleTable::fromArray($arr['dataTable']) : null, - ); - } - - /** - * Check that the type of 'docString' matches expectations - * - * @psalm-assert array{docString?: array} $arr - */ - private static function ensureDocString(array $arr): void - { - if (array_key_exists('docString', $arr) && !is_array($arr['docString'])) { - throw new SchemaViolationException('Property \'docString\' was not array'); - } - } - - /** - * Check that the type of 'dataTable' matches expectations - * - * @psalm-assert array{dataTable?: array} $arr - */ - private static function ensureDataTable(array $arr): void - { - if (array_key_exists('dataTable', $arr) && !is_array($arr['dataTable'])) { - throw new SchemaViolationException('Property \'dataTable\' was not array'); - } - } -} - - -/** - * Represents the PickleTable message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class PickleTable implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * @param list $rows - */ - public readonly array $rows, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureRows($arr); - - return new self( - array_map(fn(array $member) => PickleTableRow::fromArray($member) , $arr['rows']), - ); - } - - /** - * Check that the type of 'rows' matches expectations - * - * @psalm-assert array{rows: array} $arr - */ - private static function ensureRows(array $arr): void - { - if (!array_key_exists('rows', $arr)) { - throw new SchemaViolationException('Property \'rows\' is required but was not found'); - } - if (array_key_exists('rows', $arr) && !is_array($arr['rows'])) { - throw new SchemaViolationException('Property \'rows\' was not array'); - } - } -} - - -/** - * Represents the PickleTableCell message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class PickleTableCell implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $value, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureValue($arr); - - return new self( - (string) $arr['value'], - ); - } - - /** - * Check that the type of 'value' matches expectations - * - * @psalm-assert array{value: mixed} $arr - */ - private static function ensureValue(array $arr): void - { - if (!array_key_exists('value', $arr)) { - throw new SchemaViolationException('Property \'value\' is required but was not found'); - } - } -} - - -/** - * Represents the PickleTableRow message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class PickleTableRow implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * @param list $cells - */ - public readonly array $cells, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureCells($arr); - - return new self( - array_map(fn(array $member) => PickleTableCell::fromArray($member) , $arr['cells']), - ); - } - - /** - * Check that the type of 'cells' matches expectations - * - * @psalm-assert array{cells: array} $arr - */ - private static function ensureCells(array $arr): void - { - if (!array_key_exists('cells', $arr)) { - throw new SchemaViolationException('Property \'cells\' is required but was not found'); - } - if (array_key_exists('cells', $arr) && !is_array($arr['cells'])) { - throw new SchemaViolationException('Property \'cells\' was not array'); - } - } -} - - -/** - * Represents the PickleTag message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A tag */ -final class PickleTag implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $name, - - /** - * Points to the AST node this was created from - */ - public readonly string $astNodeId, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureName($arr); - self::ensureAstNodeId($arr); - - return new self( - (string) $arr['name'], - (string) $arr['astNodeId'], - ); - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } - - /** - * Check that the type of 'astNodeId' matches expectations - * - * @psalm-assert array{astNodeId: mixed} $arr - */ - private static function ensureAstNodeId(array $arr): void - { - if (!array_key_exists('astNodeId', $arr)) { - throw new SchemaViolationException('Property \'astNodeId\' is required but was not found'); - } - } -} - - -/** - * Represents the Source message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * //// Source - * - * A source file, typically a Gherkin document or Java/Ruby/JavaScript source code */ -final class Source implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) - * of the source, typically a file path relative to the root directory - */ - public readonly string $uri, - - /** - * The contents of the file - */ - public readonly string $data, - - /** - * The media type of the file. Can be used to specify custom types, such as - * text/x.cucumber.gherkin+plain - */ - public readonly Source\MediaType $mediaType, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureUri($arr); - self::ensureData($arr); - self::ensureMediaType($arr); - - return new self( - (string) $arr['uri'], - (string) $arr['data'], - Source\MediaType::from((string) $arr['mediaType']), - ); - } - - /** - * Check that the type of 'uri' matches expectations - * - * @psalm-assert array{uri: mixed} $arr - */ - private static function ensureUri(array $arr): void - { - if (!array_key_exists('uri', $arr)) { - throw new SchemaViolationException('Property \'uri\' is required but was not found'); - } - } - - /** - * Check that the type of 'data' matches expectations - * - * @psalm-assert array{data: mixed} $arr - */ - private static function ensureData(array $arr): void - { - if (!array_key_exists('data', $arr)) { - throw new SchemaViolationException('Property \'data\' is required but was not found'); - } - } - - /** - * Check that the type of 'mediaType' matches expectations - * - * @psalm-assert array{mediaType: mixed} $arr - */ - private static function ensureMediaType(array $arr): void - { - if (!array_key_exists('mediaType', $arr)) { - throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); - } - } -} - - -/** - * Represents the SourceReference message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * Points to a [Source](#io.cucumber.messages.Source) identified by `uri` and a - * [Location](#io.cucumber.messages.Location) within that file. */ -final class SourceReference implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly ?string $uri, - - public readonly ?JavaMethod $javaMethod, - - public readonly ?JavaStackTraceElement $javaStackTraceElement, - - public readonly ?Location $location, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureJavaMethod($arr); - self::ensureJavaStackTraceElement($arr); - self::ensureLocation($arr); - - return new self( - isset($arr['uri']) ? (string) $arr['uri'] : null, - isset($arr['javaMethod']) ? JavaMethod::fromArray($arr['javaMethod']) : null, - isset($arr['javaStackTraceElement']) ? JavaStackTraceElement::fromArray($arr['javaStackTraceElement']) : null, - isset($arr['location']) ? Location::fromArray($arr['location']) : null, - ); - } - - /** - * Check that the type of 'javaMethod' matches expectations - * - * @psalm-assert array{javaMethod?: array} $arr - */ - private static function ensureJavaMethod(array $arr): void - { - if (array_key_exists('javaMethod', $arr) && !is_array($arr['javaMethod'])) { - throw new SchemaViolationException('Property \'javaMethod\' was not array'); - } - } - - /** - * Check that the type of 'javaStackTraceElement' matches expectations - * - * @psalm-assert array{javaStackTraceElement?: array} $arr - */ - private static function ensureJavaStackTraceElement(array $arr): void - { - if (array_key_exists('javaStackTraceElement', $arr) && !is_array($arr['javaStackTraceElement'])) { - throw new SchemaViolationException('Property \'javaStackTraceElement\' was not array'); - } - } - - /** - * Check that the type of 'location' matches expectations - * - * @psalm-assert array{location?: array} $arr - */ - private static function ensureLocation(array $arr): void - { - if (array_key_exists('location', $arr) && !is_array($arr['location'])) { - throw new SchemaViolationException('Property \'location\' was not array'); - } - } -} - - -/** - * Represents the JavaMethod message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class JavaMethod implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $className, - - public readonly string $methodName, - - /** - * @param list $methodParameterTypes - */ - public readonly array $methodParameterTypes, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureClassName($arr); - self::ensureMethodName($arr); - self::ensureMethodParameterTypes($arr); - - return new self( - (string) $arr['className'], - (string) $arr['methodName'], - array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), - ); - } - - /** - * Check that the type of 'className' matches expectations - * - * @psalm-assert array{className: mixed} $arr - */ - private static function ensureClassName(array $arr): void - { - if (!array_key_exists('className', $arr)) { - throw new SchemaViolationException('Property \'className\' is required but was not found'); - } - } - - /** - * Check that the type of 'methodName' matches expectations - * - * @psalm-assert array{methodName: mixed} $arr - */ - private static function ensureMethodName(array $arr): void - { - if (!array_key_exists('methodName', $arr)) { - throw new SchemaViolationException('Property \'methodName\' is required but was not found'); - } - } - - /** - * Check that the type of 'methodParameterTypes' matches expectations - * - * @psalm-assert array{methodParameterTypes: array} $arr - */ - private static function ensureMethodParameterTypes(array $arr): void - { - if (!array_key_exists('methodParameterTypes', $arr)) { - throw new SchemaViolationException('Property \'methodParameterTypes\' is required but was not found'); - } - if (array_key_exists('methodParameterTypes', $arr) && !is_array($arr['methodParameterTypes'])) { - throw new SchemaViolationException('Property \'methodParameterTypes\' was not array'); - } - } -} - - -/** - * Represents the JavaStackTraceElement message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class JavaStackTraceElement implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $className, - - public readonly string $fileName, - - public readonly string $methodName, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureClassName($arr); - self::ensureFileName($arr); - self::ensureMethodName($arr); - - return new self( - (string) $arr['className'], - (string) $arr['fileName'], - (string) $arr['methodName'], - ); - } - - /** - * Check that the type of 'className' matches expectations - * - * @psalm-assert array{className: mixed} $arr - */ - private static function ensureClassName(array $arr): void - { - if (!array_key_exists('className', $arr)) { - throw new SchemaViolationException('Property \'className\' is required but was not found'); - } - } - - /** - * Check that the type of 'fileName' matches expectations - * - * @psalm-assert array{fileName: mixed} $arr - */ - private static function ensureFileName(array $arr): void - { - if (!array_key_exists('fileName', $arr)) { - throw new SchemaViolationException('Property \'fileName\' is required but was not found'); - } - } - - /** - * Check that the type of 'methodName' matches expectations - * - * @psalm-assert array{methodName: mixed} $arr - */ - private static function ensureMethodName(array $arr): void - { - if (!array_key_exists('methodName', $arr)) { - throw new SchemaViolationException('Property \'methodName\' is required but was not found'); - } - } -} - - -/** - * Represents the StepDefinition message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class StepDefinition implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $id, - - public readonly StepDefinitionPattern $pattern, - - public readonly SourceReference $sourceReference, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureId($arr); - self::ensurePattern($arr); - self::ensureSourceReference($arr); - - return new self( - (string) $arr['id'], - StepDefinitionPattern::fromArray($arr['pattern']), - SourceReference::fromArray($arr['sourceReference']), - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'pattern' matches expectations - * - * @psalm-assert array{pattern: array} $arr - */ - private static function ensurePattern(array $arr): void - { - if (!array_key_exists('pattern', $arr)) { - throw new SchemaViolationException('Property \'pattern\' is required but was not found'); - } - if (array_key_exists('pattern', $arr) && !is_array($arr['pattern'])) { - throw new SchemaViolationException('Property \'pattern\' was not array'); - } - } - - /** - * Check that the type of 'sourceReference' matches expectations - * - * @psalm-assert array{sourceReference: array} $arr - */ - private static function ensureSourceReference(array $arr): void - { - if (!array_key_exists('sourceReference', $arr)) { - throw new SchemaViolationException('Property \'sourceReference\' is required but was not found'); - } - if (array_key_exists('sourceReference', $arr) && !is_array($arr['sourceReference'])) { - throw new SchemaViolationException('Property \'sourceReference\' was not array'); - } - } -} - - -/** - * Represents the StepDefinitionPattern message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class StepDefinitionPattern implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $source, - - public readonly StepDefinitionPattern\Type $type, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureSource($arr); - self::ensureType($arr); - - return new self( - (string) $arr['source'], - StepDefinitionPattern\Type::from((string) $arr['type']), - ); - } - - /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source: mixed} $arr - */ - private static function ensureSource(array $arr): void - { - if (!array_key_exists('source', $arr)) { - throw new SchemaViolationException('Property \'source\' is required but was not found'); - } - } - - /** - * Check that the type of 'type' matches expectations - * - * @psalm-assert array{type: mixed} $arr - */ - private static function ensureType(array $arr): void - { - if (!array_key_exists('type', $arr)) { - throw new SchemaViolationException('Property \'type\' is required but was not found'); - } - } -} - - -/** - * Represents the TestCase message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * //// TestCases - * - * A `TestCase` contains a sequence of `TestStep`s. */ -final class TestCase implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $id, - - /** - * The ID of the `Pickle` this `TestCase` is derived from. - */ - public readonly string $pickleId, - - /** - * @param list $testSteps - */ - public readonly array $testSteps, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureId($arr); - self::ensurePickleId($arr); - self::ensureTestSteps($arr); - - return new self( - (string) $arr['id'], - (string) $arr['pickleId'], - array_map(fn(array $member) => TestStep::fromArray($member) , $arr['testSteps']), - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'pickleId' matches expectations - * - * @psalm-assert array{pickleId: mixed} $arr - */ - private static function ensurePickleId(array $arr): void - { - if (!array_key_exists('pickleId', $arr)) { - throw new SchemaViolationException('Property \'pickleId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testSteps' matches expectations - * - * @psalm-assert array{testSteps: array} $arr - */ - private static function ensureTestSteps(array $arr): void - { - if (!array_key_exists('testSteps', $arr)) { - throw new SchemaViolationException('Property \'testSteps\' is required but was not found'); - } - if (array_key_exists('testSteps', $arr) && !is_array($arr['testSteps'])) { - throw new SchemaViolationException('Property \'testSteps\' was not array'); - } - } -} - - -/** - * Represents the Group message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Group implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * @param list $children - */ - public readonly array $children, - - public readonly ?int $start, - - public readonly ?string $value, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureChildren($arr); - - return new self( - array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), - isset($arr['start']) ? (int) $arr['start'] : null, - isset($arr['value']) ? (string) $arr['value'] : null, - ); - } - - /** - * Check that the type of 'children' matches expectations - * - * @psalm-assert array{children: array} $arr - */ - private static function ensureChildren(array $arr): void - { - if (!array_key_exists('children', $arr)) { - throw new SchemaViolationException('Property \'children\' is required but was not found'); - } - if (array_key_exists('children', $arr) && !is_array($arr['children'])) { - throw new SchemaViolationException('Property \'children\' was not array'); - } - } -} - - -/** - * Represents the StepMatchArgument message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * Represents a single argument extracted from a step match and passed to a step definition. - * This is used for the following purposes: - * - Construct an argument to pass to a step definition (possibly through a parameter type transform) - * - Highlight the matched parameter in rich formatters such as the HTML formatter - * - * This message closely matches the `Argument` class in the `cucumber-expressions` library. */ -final class StepMatchArgument implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * Represents the outermost capture group of an argument. This message closely matches the - * `Group` class in the `cucumber-expressions` library. - */ - public readonly Group $group, - - public readonly ?string $parameterTypeName, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureGroup($arr); - - return new self( - Group::fromArray($arr['group']), - isset($arr['parameterTypeName']) ? (string) $arr['parameterTypeName'] : null, - ); - } - - /** - * Check that the type of 'group' matches expectations - * - * @psalm-assert array{group: array} $arr - */ - private static function ensureGroup(array $arr): void - { - if (!array_key_exists('group', $arr)) { - throw new SchemaViolationException('Property \'group\' is required but was not found'); - } - if (array_key_exists('group', $arr) && !is_array($arr['group'])) { - throw new SchemaViolationException('Property \'group\' was not array'); - } - } -} - - -/** - * Represents the StepMatchArgumentsList message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class StepMatchArgumentsList implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * @param list $stepMatchArguments - */ - public readonly array $stepMatchArguments, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureStepMatchArguments($arr); - - return new self( - array_map(fn(array $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), - ); - } - - /** - * Check that the type of 'stepMatchArguments' matches expectations - * - * @psalm-assert array{stepMatchArguments: array} $arr - */ - private static function ensureStepMatchArguments(array $arr): void - { - if (!array_key_exists('stepMatchArguments', $arr)) { - throw new SchemaViolationException('Property \'stepMatchArguments\' is required but was not found'); - } - if (array_key_exists('stepMatchArguments', $arr) && !is_array($arr['stepMatchArguments'])) { - throw new SchemaViolationException('Property \'stepMatchArguments\' was not array'); - } - } -} - - -/** - * Represents the TestStep message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - * A `TestStep` is derived from either a `PickleStep` - * combined with a `StepDefinition`, or from a `Hook`. */ -final class TestStep implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * Pointer to the `Hook` (if derived from a Hook) - */ - public readonly ?string $hookId, - - public readonly string $id, - - /** - * Pointer to the `PickleStep` (if derived from a `PickleStep`) - */ - public readonly ?string $pickleStepId, - - /** - * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) - * @param ?list $stepDefinitionIds - */ - public readonly ?array $stepDefinitionIds, - - /** - * A list of list of StepMatchArgument (if derived from a `PickleStep`). - * Each element represents a matching step definition. A size of 0 means `UNDEFINED`, - * and a size of 2+ means `AMBIGUOUS` - * @param ?list $stepMatchArgumentsLists - */ - public readonly ?array $stepMatchArgumentsLists, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureId($arr); - self::ensureStepDefinitionIds($arr); - self::ensureStepMatchArgumentsLists($arr); - - return new self( - isset($arr['hookId']) ? (string) $arr['hookId'] : null, - (string) $arr['id'], - isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, - isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, - isset($arr['stepMatchArgumentsLists']) ? array_map(fn(array $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, - ); - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'stepDefinitionIds' matches expectations - * - * @psalm-assert array{stepDefinitionIds?: array} $arr - */ - private static function ensureStepDefinitionIds(array $arr): void - { - if (array_key_exists('stepDefinitionIds', $arr) && !is_array($arr['stepDefinitionIds'])) { - throw new SchemaViolationException('Property \'stepDefinitionIds\' was not array'); - } - } - - /** - * Check that the type of 'stepMatchArgumentsLists' matches expectations - * - * @psalm-assert array{stepMatchArgumentsLists?: array} $arr - */ - private static function ensureStepMatchArgumentsLists(array $arr): void - { - if (array_key_exists('stepMatchArgumentsLists', $arr) && !is_array($arr['stepMatchArgumentsLists'])) { - throw new SchemaViolationException('Property \'stepMatchArgumentsLists\' was not array'); - } - } -} - - -/** - * Represents the TestCaseFinished message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestCaseFinished implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $testCaseStartedId, - - public readonly Timestamp $timestamp, - - public readonly bool $willBeRetried, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureTestCaseStartedId($arr); - self::ensureTimestamp($arr); - self::ensureWillBeRetried($arr); - - return new self( - (string) $arr['testCaseStartedId'], - Timestamp::fromArray($arr['timestamp']), - (bool) $arr['willBeRetried'], - ); - } - - /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr - */ - private static function ensureTestCaseStartedId(array $arr): void - { - if (!array_key_exists('testCaseStartedId', $arr)) { - throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } - - /** - * Check that the type of 'willBeRetried' matches expectations - * - * @psalm-assert array{willBeRetried: mixed} $arr - */ - private static function ensureWillBeRetried(array $arr): void - { - if (!array_key_exists('willBeRetried', $arr)) { - throw new SchemaViolationException('Property \'willBeRetried\' is required but was not found'); - } - } -} - - -/** - * Represents the TestCaseStarted message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestCaseStarted implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * The first attempt should have value 0, and for each retry the value - * should increase by 1. - */ - public readonly int $attempt, - - /** - * Because a `TestCase` can be run multiple times (in case of a retry), - * we use this field to group messages relating to the same attempt. - */ - public readonly string $id, - - public readonly string $testCaseId, - - public readonly Timestamp $timestamp, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureAttempt($arr); - self::ensureId($arr); - self::ensureTestCaseId($arr); - self::ensureTimestamp($arr); - - return new self( - (int) $arr['attempt'], - (string) $arr['id'], - (string) $arr['testCaseId'], - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'attempt' matches expectations - * - * @psalm-assert array{attempt: mixed} $arr - */ - private static function ensureAttempt(array $arr): void - { - if (!array_key_exists('attempt', $arr)) { - throw new SchemaViolationException('Property \'attempt\' is required but was not found'); - } - } - - /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr - */ - private static function ensureId(array $arr): void - { - if (!array_key_exists('id', $arr)) { - throw new SchemaViolationException('Property \'id\' is required but was not found'); - } - } - - /** - * Check that the type of 'testCaseId' matches expectations - * - * @psalm-assert array{testCaseId: mixed} $arr - */ - private static function ensureTestCaseId(array $arr): void - { - if (!array_key_exists('testCaseId', $arr)) { - throw new SchemaViolationException('Property \'testCaseId\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } -} - - -/** - * Represents the TestRunFinished message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestRunFinished implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. - * If there are undefined parameter types, the message is simply - * "The following parameter type(s() are not defined: xxx, yyy". - * The independent `UndefinedParameterType` messages can be used to generate - * snippets for those parameter types. - */ - public readonly ?string $message, - - /** - * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) - */ - public readonly bool $success, - - /** - * Timestamp when the TestRun is finished - */ - public readonly Timestamp $timestamp, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureSuccess($arr); - self::ensureTimestamp($arr); - - return new self( - isset($arr['message']) ? (string) $arr['message'] : null, - (bool) $arr['success'], - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'success' matches expectations - * - * @psalm-assert array{success: mixed} $arr - */ - private static function ensureSuccess(array $arr): void - { - if (!array_key_exists('success', $arr)) { - throw new SchemaViolationException('Property \'success\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } -} - - -/** - * Represents the TestRunStarted message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestRunStarted implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly Timestamp $timestamp, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureTimestamp($arr); - - return new self( - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } -} - - -/** - * Represents the TestStepFinished message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestStepFinished implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $testCaseStartedId, - - public readonly string $testStepId, - - public readonly TestStepResult $testStepResult, - - public readonly Timestamp $timestamp, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureTestCaseStartedId($arr); - self::ensureTestStepId($arr); - self::ensureTestStepResult($arr); - self::ensureTimestamp($arr); - - return new self( - (string) $arr['testCaseStartedId'], - (string) $arr['testStepId'], - TestStepResult::fromArray($arr['testStepResult']), - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr - */ - private static function ensureTestCaseStartedId(array $arr): void - { - if (!array_key_exists('testCaseStartedId', $arr)) { - throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testStepId' matches expectations - * - * @psalm-assert array{testStepId: mixed} $arr - */ - private static function ensureTestStepId(array $arr): void - { - if (!array_key_exists('testStepId', $arr)) { - throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testStepResult' matches expectations - * - * @psalm-assert array{testStepResult: array} $arr - */ - private static function ensureTestStepResult(array $arr): void - { - if (!array_key_exists('testStepResult', $arr)) { - throw new SchemaViolationException('Property \'testStepResult\' is required but was not found'); - } - if (array_key_exists('testStepResult', $arr) && !is_array($arr['testStepResult'])) { - throw new SchemaViolationException('Property \'testStepResult\' was not array'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } -} - - -/** - * Represents the TestStepResult message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestStepResult implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly Duration $duration, - - public readonly ?string $message, - - public readonly TestStepResult\Status $status, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureDuration($arr); - self::ensureStatus($arr); - - return new self( - Duration::fromArray($arr['duration']), - isset($arr['message']) ? (string) $arr['message'] : null, - TestStepResult\Status::from((string) $arr['status']), - ); - } - - /** - * Check that the type of 'duration' matches expectations - * - * @psalm-assert array{duration: array} $arr - */ - private static function ensureDuration(array $arr): void - { - if (!array_key_exists('duration', $arr)) { - throw new SchemaViolationException('Property \'duration\' is required but was not found'); - } - if (array_key_exists('duration', $arr) && !is_array($arr['duration'])) { - throw new SchemaViolationException('Property \'duration\' was not array'); - } - } - - /** - * Check that the type of 'status' matches expectations - * - * @psalm-assert array{status: mixed} $arr - */ - private static function ensureStatus(array $arr): void - { - if (!array_key_exists('status', $arr)) { - throw new SchemaViolationException('Property \'status\' is required but was not found'); - } - } -} - - -/** - * Represents the TestStepStarted message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class TestStepStarted implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $testCaseStartedId, - - public readonly string $testStepId, - - public readonly Timestamp $timestamp, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureTestCaseStartedId($arr); - self::ensureTestStepId($arr); - self::ensureTimestamp($arr); - - return new self( - (string) $arr['testCaseStartedId'], - (string) $arr['testStepId'], - Timestamp::fromArray($arr['timestamp']), - ); - } - - /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr - */ - private static function ensureTestCaseStartedId(array $arr): void - { - if (!array_key_exists('testCaseStartedId', $arr)) { - throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); - } - } - - /** - * Check that the type of 'testStepId' matches expectations - * - * @psalm-assert array{testStepId: mixed} $arr - */ - private static function ensureTestStepId(array $arr): void - { - if (!array_key_exists('testStepId', $arr)) { - throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); - } - } - - /** - * Check that the type of 'timestamp' matches expectations - * - * @psalm-assert array{timestamp: array} $arr - */ - private static function ensureTimestamp(array $arr): void - { - if (!array_key_exists('timestamp', $arr)) { - throw new SchemaViolationException('Property \'timestamp\' is required but was not found'); - } - if (array_key_exists('timestamp', $arr) && !is_array($arr['timestamp'])) { - throw new SchemaViolationException('Property \'timestamp\' was not array'); - } - } -} - - -/** - * Represents the Timestamp message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class Timestamp implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - /** - * Represents seconds of UTC time since Unix epoch - * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - * 9999-12-31T23:59:59Z inclusive. - */ - public readonly int $seconds, - - /** - * Non-negative fractions of a second at nanosecond resolution. Negative - * second values with fractions must still have non-negative nanos values - * that count forward in time. Must be from 0 to 999,999,999 - * inclusive. - */ - public readonly int $nanos, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureSeconds($arr); - self::ensureNanos($arr); - - return new self( - (int) $arr['seconds'], - (int) $arr['nanos'], - ); - } - - /** - * Check that the type of 'seconds' matches expectations - * - * @psalm-assert array{seconds: mixed} $arr - */ - private static function ensureSeconds(array $arr): void - { - if (!array_key_exists('seconds', $arr)) { - throw new SchemaViolationException('Property \'seconds\' is required but was not found'); - } - } - - /** - * Check that the type of 'nanos' matches expectations - * - * @psalm-assert array{nanos: mixed} $arr - */ - private static function ensureNanos(array $arr): void - { - if (!array_key_exists('nanos', $arr)) { - throw new SchemaViolationException('Property \'nanos\' is required but was not found'); - } - } -} - - -/** - * Represents the UndefinedParameterType message in Cucumber's message protocol - * @see https://github.com/cucumber/common/tree/main/messages#readme - * - */ -final class UndefinedParameterType implements JsonSerializable -{ - use JsonEncodingTrait; - - private function __construct( - - public readonly string $expression, - - public readonly string $name, - - ){} - - /** - * @throws SchemaViolationException - * - * @internal - */ - public static function fromArray(array $arr) : self - { - self::ensureExpression($arr); - self::ensureName($arr); - - return new self( - (string) $arr['expression'], - (string) $arr['name'], - ); - } - - /** - * Check that the type of 'expression' matches expectations - * - * @psalm-assert array{expression: mixed} $arr - */ - private static function ensureExpression(array $arr): void - { - if (!array_key_exists('expression', $arr)) { - throw new SchemaViolationException('Property \'expression\' is required but was not found'); - } - } - - /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr - */ - private static function ensureName(array $arr): void - { - if (!array_key_exists('name', $arr)) { - throw new SchemaViolationException('Property \'name\' is required but was not found'); - } - } -} - - -namespace Cucumber\Messages\Attachment; - -enum ContentEncoding : string -{ - case IDENTITY = 'IDENTITY'; - case BASE64 = 'BASE64'; -} - - -namespace Cucumber\Messages\Source; - -enum MediaType : string -{ - case TEXT_X_CUCUMBER_GHERKIN_PLAIN = 'text/x.cucumber.gherkin+plain'; - case TEXT_X_CUCUMBER_GHERKIN_MARKDOWN = 'text/x.cucumber.gherkin+markdown'; -} - - -namespace Cucumber\Messages\StepDefinitionPattern; - -enum Type : string -{ - case CUCUMBER_EXPRESSION = 'CUCUMBER_EXPRESSION'; - case REGULAR_EXPRESSION = 'REGULAR_EXPRESSION'; -} - - -namespace Cucumber\Messages\TestStepResult; - -enum Status : string -{ - case UNKNOWN = 'UNKNOWN'; - case PASSED = 'PASSED'; - case SKIPPED = 'SKIPPED'; - case PENDING = 'PENDING'; - case UNDEFINED = 'UNDEFINED'; - case AMBIGUOUS = 'AMBIGUOUS'; - case FAILED = 'FAILED'; -} - - From dc7432c774af9189b1953a6d07fbe973c32b89b8 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Sun, 23 Jan 2022 20:39:40 +0000 Subject: [PATCH 08/37] Ensure final classes, strict types --- messages/php/psalm.xml | 3 ++- messages/php/src/DecodingException.php | 4 ++-- messages/php/src/JsonEncodingTrait.php | 5 ++++- messages/php/src/SchemaViolationException.php | 5 ++--- messages/php/src/UnknownDecodingException.php | 5 ++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/messages/php/psalm.xml b/messages/php/psalm.xml index b1c0c4c7611..f6a2ada034d 100644 --- a/messages/php/psalm.xml +++ b/messages/php/psalm.xml @@ -15,4 +15,5 @@ - + + diff --git a/messages/php/src/DecodingException.php b/messages/php/src/DecodingException.php index e7a8f6d6632..420b3b76188 100644 --- a/messages/php/src/DecodingException.php +++ b/messages/php/src/DecodingException.php @@ -1,10 +1,10 @@ - Date: Sun, 23 Jan 2022 20:45:27 +0000 Subject: [PATCH 09/37] Stream readers and writers for NdJson input --- .../php/src/Streams/NdJsonStreamReader.php | 21 +++++++ .../php/src/Streams/NdJsonStreamWriter.php | 20 +++++++ messages/php/src/Streams/StreamReader.php | 13 ++++ messages/php/src/Streams/StreamWriter.php | 13 ++++ .../php/src/Streams/WithFileHandleTrait.php | 24 ++++++++ messages/php/tests/AcceptanceTest.php | 59 +++++++++++++++++-- 6 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 messages/php/src/Streams/NdJsonStreamReader.php create mode 100644 messages/php/src/Streams/NdJsonStreamWriter.php create mode 100644 messages/php/src/Streams/StreamReader.php create mode 100644 messages/php/src/Streams/StreamWriter.php create mode 100644 messages/php/src/Streams/WithFileHandleTrait.php diff --git a/messages/php/src/Streams/NdJsonStreamReader.php b/messages/php/src/Streams/NdJsonStreamReader.php new file mode 100644 index 00000000000..ed3396da765 --- /dev/null +++ b/messages/php/src/Streams/NdJsonStreamReader.php @@ -0,0 +1,21 @@ + + */ + public function envelopes() : Generator + { + while (!feof($this->fileHandle) && ($line = fgets($this->fileHandle))) { + yield Envelope::fromJson($line); + } + } +} diff --git a/messages/php/src/Streams/NdJsonStreamWriter.php b/messages/php/src/Streams/NdJsonStreamWriter.php new file mode 100644 index 00000000000..2b447cb9ea9 --- /dev/null +++ b/messages/php/src/Streams/NdJsonStreamWriter.php @@ -0,0 +1,20 @@ + $envelopes + */ + public function writeEnvelopes(iterable $envelopes) : void + { + foreach($envelopes as $envelope) { + fputs($this->fileHandle, $envelope->asJson() . "\n"); + } + } +} diff --git a/messages/php/src/Streams/StreamReader.php b/messages/php/src/Streams/StreamReader.php new file mode 100644 index 00000000000..09744516774 --- /dev/null +++ b/messages/php/src/Streams/StreamReader.php @@ -0,0 +1,13 @@ + + */ + public function envelopes(): iterable; +} diff --git a/messages/php/src/Streams/StreamWriter.php b/messages/php/src/Streams/StreamWriter.php new file mode 100644 index 00000000000..606c74c6eda --- /dev/null +++ b/messages/php/src/Streams/StreamWriter.php @@ -0,0 +1,13 @@ + $envelopes + */ + public function writeEnvelopes(iterable $envelopes): void; +} diff --git a/messages/php/src/Streams/WithFileHandleTrait.php b/messages/php/src/Streams/WithFileHandleTrait.php new file mode 100644 index 00000000000..187821662f6 --- /dev/null +++ b/messages/php/src/Streams/WithFileHandleTrait.php @@ -0,0 +1,24 @@ +writeEnvelopes($reader->envelopes()); + + rewind($sourceHandle); + rewind($destHandle); + + while (!feof($sourceHandle)) { + $sourceLine = fgets($sourceHandle); + $destLine = fgets($destHandle); + + if (!$sourceLine && !$destLine) { + break; + } + + self::assertJsonStringEqualsJsonString($sourceLine, $destLine); + } + + // we exhausted source so dest should also be at end + self::assertTrue(feof($destHandle)); + } + /** - * @return Generator> + * @return Generator */ - public function provideJson() : Generator + public function provideJsonLines() : Generator { - $filePattern = __DIR__ . '/../../../compatibility-kit/javascript/features/**/*.ndjson'; - foreach (glob($filePattern) as $filename) { + foreach ($this->getSampleFiles() as $filename) { foreach(file($filename) ?: [] as $lineNumber => $line) { // key is provided for better error messages $key = realpath($filename) . ':' . $lineNumber; @@ -28,4 +58,23 @@ public function provideJson() : Generator } } } + + /** + * @return Generator + */ + public function provideNdJsonFilenames() : Generator + { + foreach($this->getSampleFiles() as $filename) + { + yield $filename => [$filename]; + } + } + + /** + * @return list + */ + private function getSampleFiles(): array + { + return glob(__DIR__ . '/../../../compatibility-kit/javascript/features/**/*.ndjson') ?: []; + } } From 9a4f3b170c21e69b5fe75e901565a0131cf2381b Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Sun, 23 Jan 2022 21:05:07 +0000 Subject: [PATCH 10/37] Clarify return type of Generator --- messages/php/src/Streams/NdJsonStreamReader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/php/src/Streams/NdJsonStreamReader.php b/messages/php/src/Streams/NdJsonStreamReader.php index ed3396da765..66dbb8b0538 100644 --- a/messages/php/src/Streams/NdJsonStreamReader.php +++ b/messages/php/src/Streams/NdJsonStreamReader.php @@ -10,7 +10,7 @@ final class NdJsonStreamReader implements StreamReader use WithFileHandleTrait; /** - * @return Generator + * @return Generator */ public function envelopes() : Generator { From b98975966a2f325a18254e6d960117554784c89e Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Tue, 25 Jan 2022 21:08:26 +0000 Subject: [PATCH 11/37] Add README.md --- messages/php/README.md | 62 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 messages/php/README.md diff --git a/messages/php/README.md b/messages/php/README.md new file mode 100644 index 00000000000..953e4b81d82 --- /dev/null +++ b/messages/php/README.md @@ -0,0 +1,62 @@ +# Cucumber Messages + +This is a PHP implementation of the [Cucumber Messages protocol](https://github.com/cucumber/common/blob/main/messages/README.md) + +## Requirements + +* PHP 8.1 +* Ext-JSON + +## Installation + +Install using [composer](https://getcomposer.org). + +```shell +composer require cucumber/messages +``` + +## Usage + +All cucumber messages are contained in an Envelope object. + +You can construct an Envelope from a JSON string: + +```php +use Cucumber\Messages\DecodingException; +use Cucumber\Messages\Envelope; + +try { + $envelope = Envelope::fromJson($json); +} +catch (DecodingException $e) { + // handle the error +} +``` + +Cucumber Messages are serialised as Newline Delimited JSON (NDJSON). + +You can use the NdJsonStreamReader to obtain a Generator: + +```php +use Cucumber\Messages\DecodingException; +use Cucumber\Messages\Envelope; +use Cucumber\Messages\Streams\NdJsonStreamReader; + +$fh = fopen('messages.ndjson', 'r'); +$reader = new NdJsonStreamReader($fh); + +/** @var Generator $envelopes */ +$envelopes = $reader->envelopes(); + +try { + foreach ($envelopes as $envelope) { + // process $envelope + } +} +catch (DecodingException $e) { + // handle the error +} +finally { + fclose($fh); +} +``` From 1082381738a5943d2023a6fc8b1135f0be8fe8ab Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Wed, 26 Jan 2022 16:16:42 +0000 Subject: [PATCH 12/37] Add clean target to Makefile --- messages/php/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/messages/php/Makefile b/messages/php/Makefile index 79db1f9efc5..22415e7a967 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -1,6 +1,9 @@ JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") +clean: + rm -rf vendor composer.lock build/messages.php + .deps: composer.lock build/messages.php composer.lock: From 5ba4a34f96fdb91080fca1f04ae03cb8634150d8 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Wed, 26 Jan 2022 16:18:14 +0000 Subject: [PATCH 13/37] Additional validation rules --- .../jsonschema/scripts/templates/php.php.erb | 32 ++++----- messages/php/src-generated/Attachment.php | 67 ++++++++++++++++--- messages/php/src-generated/Background.php | 32 ++++----- messages/php/src-generated/Ci.php | 31 +++++++-- messages/php/src-generated/Comment.php | 9 ++- messages/php/src-generated/DataTable.php | 4 -- messages/php/src-generated/DocString.php | 27 +++++--- messages/php/src-generated/Duration.php | 14 ++-- messages/php/src-generated/Envelope.php | 34 ---------- messages/php/src-generated/Examples.php | 36 +++++----- messages/php/src-generated/Feature.php | 34 +++++----- messages/php/src-generated/FeatureChild.php | 6 -- .../php/src-generated/GherkinDocument.php | 15 +++-- messages/php/src-generated/Git.php | 36 ++++++++-- messages/php/src-generated/Group.php | 24 ++++++- messages/php/src-generated/Hook.php | 20 ++++-- messages/php/src-generated/JavaMethod.php | 16 ++--- .../src-generated/JavaStackTraceElement.php | 21 +++--- messages/php/src-generated/Location.php | 18 ++++- messages/php/src-generated/Meta.php | 17 ++--- messages/php/src-generated/ParameterType.php | 30 +++++---- messages/php/src-generated/ParseError.php | 9 ++- messages/php/src-generated/Pickle.php | 34 +++++----- .../php/src-generated/PickleDocString.php | 18 ++++- messages/php/src-generated/PickleStep.php | 18 +++-- .../php/src-generated/PickleStepArgument.php | 4 -- messages/php/src-generated/PickleTable.php | 2 - .../php/src-generated/PickleTableCell.php | 7 +- messages/php/src-generated/PickleTableRow.php | 2 - messages/php/src-generated/PickleTag.php | 14 ++-- messages/php/src-generated/Product.php | 18 ++++- messages/php/src-generated/Rule.php | 34 +++++----- messages/php/src-generated/RuleChild.php | 4 -- messages/php/src-generated/Scenario.php | 36 +++++----- messages/php/src-generated/Source.php | 21 +++--- .../php/src-generated/SourceReference.php | 17 +++-- messages/php/src-generated/Step.php | 27 ++++---- messages/php/src-generated/StepDefinition.php | 11 ++- .../src-generated/StepDefinitionPattern.php | 14 ++-- .../php/src-generated/StepMatchArgument.php | 13 +++- .../src-generated/StepMatchArgumentsList.php | 2 - messages/php/src-generated/TableCell.php | 9 ++- messages/php/src-generated/TableRow.php | 11 ++- messages/php/src-generated/Tag.php | 16 ++--- messages/php/src-generated/TestCase.php | 16 ++--- .../php/src-generated/TestCaseFinished.php | 16 ++--- .../php/src-generated/TestCaseStarted.php | 23 ++++--- .../php/src-generated/TestRunFinished.php | 20 ++++-- messages/php/src-generated/TestRunStarted.php | 2 - messages/php/src-generated/TestStep.php | 33 +++++++-- .../php/src-generated/TestStepFinished.php | 18 +++-- messages/php/src-generated/TestStepResult.php | 20 ++++-- .../php/src-generated/TestStepStarted.php | 16 ++--- messages/php/src-generated/Timestamp.php | 14 ++-- .../src-generated/UndefinedParameterType.php | 14 ++-- messages/php/src/JsonEncodingTrait.php | 9 +++ 56 files changed, 608 insertions(+), 457 deletions(-) diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index b7489159348..fa7595e9020 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -45,9 +45,7 @@ final class <%= class_name(key) %> implements JsonSerializable public static function fromArray(array $arr) : self { <%- schema['properties'].each do |property_name, property| -%> - <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> self::ensure<%= capitalize(property_name)%>($arr); - <%- end -%> <%- end -%> return new self( @@ -57,35 +55,31 @@ final class <%= class_name(key) %> implements JsonSerializable ); } <%- schema['properties'].each do |property_name, property| -%> - <%- if (!is_nullable(property_name, schema) || !is_scalar(property)) -%> /** - * Check that the type of '<%= property_name %>' matches expectations - * - <%- if (!is_nullable(property_name, schema)) -%> - <%- if is_scalar(property) -%> - * @psalm-assert array{<%= property_name %>: mixed} $arr - <%- else -%> - * @psalm-assert array{<%= property_name %>: array} $arr - <%- end -%> - <%- else -%> - * @psalm-assert array{<%= property_name %>?: array} $arr - <%- end -%> + <%- if is_scalar(property) -%> + * @psalm-assert array{<%= property_name %>: string|int|bool} $arr + <%- else -%> + * @psalm-assert array{<%= property_name %><%- if (is_nullable(property_name, schema)) -%>?<%- end -%>: array} $arr + <%- end -%> */ private static function ensure<%= capitalize(property_name)%>(array $arr): void { - <%- if (!is_nullable(property_name, schema)) -%> + <%- if (!is_nullable(property_name, schema)) -%> if (!array_key_exists('<%= property_name %>', $arr)) { throw new SchemaViolationException('Property \'<%= property_name %>\' is required but was not found'); } - <%- end -%> - <%- if(!is_scalar(property)) -%> + <%- end -%> + <%- if(is_scalar(property)) -%> + if (array_key_exists('<%= property_name %>', $arr) && is_array($arr['<%= property_name %>'])) { + throw new SchemaViolationException('Property \'<%= property_name %>\' was array'); + } + <%- else -%> if (array_key_exists('<%= property_name %>', $arr) && !is_array($arr['<%= property_name %>'])) { throw new SchemaViolationException('Property \'<%= property_name %>\' was not array'); } - <%- end -%> - } <%- end -%> + } <%- end -%> } diff --git a/messages/php/src-generated/Attachment.php b/messages/php/src-generated/Attachment.php index 08f44b8a59b..ecbde3b66a6 100644 --- a/messages/php/src-generated/Attachment.php +++ b/messages/php/src-generated/Attachment.php @@ -93,8 +93,12 @@ public static function fromArray(array $arr) : self { self::ensureBody($arr); self::ensureContentEncoding($arr); + self::ensureFileName($arr); self::ensureMediaType($arr); self::ensureSource($arr); + self::ensureTestCaseStartedId($arr); + self::ensureTestStepId($arr); + self::ensureUrl($arr); return new self( (string) $arr['body'], @@ -109,44 +113,55 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'body' matches expectations - * - * @psalm-assert array{body: mixed} $arr + * @psalm-assert array{body: string|int|bool} $arr */ private static function ensureBody(array $arr): void { if (!array_key_exists('body', $arr)) { throw new SchemaViolationException('Property \'body\' is required but was not found'); } + if (array_key_exists('body', $arr) && is_array($arr['body'])) { + throw new SchemaViolationException('Property \'body\' was array'); + } } /** - * Check that the type of 'contentEncoding' matches expectations - * - * @psalm-assert array{contentEncoding: mixed} $arr + * @psalm-assert array{contentEncoding: string|int|bool} $arr */ private static function ensureContentEncoding(array $arr): void { if (!array_key_exists('contentEncoding', $arr)) { throw new SchemaViolationException('Property \'contentEncoding\' is required but was not found'); } + if (array_key_exists('contentEncoding', $arr) && is_array($arr['contentEncoding'])) { + throw new SchemaViolationException('Property \'contentEncoding\' was array'); + } } /** - * Check that the type of 'mediaType' matches expectations - * - * @psalm-assert array{mediaType: mixed} $arr + * @psalm-assert array{fileName: string|int|bool} $arr + */ + private static function ensureFileName(array $arr): void + { + if (array_key_exists('fileName', $arr) && is_array($arr['fileName'])) { + throw new SchemaViolationException('Property \'fileName\' was array'); + } + } + + /** + * @psalm-assert array{mediaType: string|int|bool} $arr */ private static function ensureMediaType(array $arr): void { if (!array_key_exists('mediaType', $arr)) { throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); } + if (array_key_exists('mediaType', $arr) && is_array($arr['mediaType'])) { + throw new SchemaViolationException('Property \'mediaType\' was array'); + } } /** - * Check that the type of 'source' matches expectations - * * @psalm-assert array{source?: array} $arr */ private static function ensureSource(array $arr): void @@ -155,4 +170,34 @@ private static function ensureSource(array $arr): void throw new SchemaViolationException('Property \'source\' was not array'); } } + + /** + * @psalm-assert array{testCaseStartedId: string|int|bool} $arr + */ + private static function ensureTestCaseStartedId(array $arr): void + { + if (array_key_exists('testCaseStartedId', $arr) && is_array($arr['testCaseStartedId'])) { + throw new SchemaViolationException('Property \'testCaseStartedId\' was array'); + } + } + + /** + * @psalm-assert array{testStepId: string|int|bool} $arr + */ + private static function ensureTestStepId(array $arr): void + { + if (array_key_exists('testStepId', $arr) && is_array($arr['testStepId'])) { + throw new SchemaViolationException('Property \'testStepId\' was array'); + } + } + + /** + * @psalm-assert array{url: string|int|bool} $arr + */ + private static function ensureUrl(array $arr): void + { + if (array_key_exists('url', $arr) && is_array($arr['url'])) { + throw new SchemaViolationException('Property \'url\' was array'); + } + } } diff --git a/messages/php/src-generated/Background.php b/messages/php/src-generated/Background.php index df1eeacb614..c7204b0231c 100644 --- a/messages/php/src-generated/Background.php +++ b/messages/php/src-generated/Background.php @@ -64,8 +64,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -79,44 +77,45 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr + * @psalm-assert array{keyword: string|int|bool} $arr */ private static function ensureKeyword(array $arr): void { if (!array_key_exists('keyword', $arr)) { throw new SchemaViolationException('Property \'keyword\' is required but was not found'); } + if (array_key_exists('keyword', $arr) && is_array($arr['keyword'])) { + throw new SchemaViolationException('Property \'keyword\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr + * @psalm-assert array{description: string|int|bool} $arr */ private static function ensureDescription(array $arr): void { if (!array_key_exists('description', $arr)) { throw new SchemaViolationException('Property \'description\' is required but was not found'); } + if (array_key_exists('description', $arr) && is_array($arr['description'])) { + throw new SchemaViolationException('Property \'description\' was array'); + } } /** - * Check that the type of 'steps' matches expectations - * * @psalm-assert array{steps: array} $arr */ private static function ensureSteps(array $arr): void @@ -130,14 +129,15 @@ private static function ensureSteps(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/Ci.php b/messages/php/src-generated/Ci.php index 4086b2cd530..cf7d7bb35fc 100644 --- a/messages/php/src-generated/Ci.php +++ b/messages/php/src-generated/Ci.php @@ -46,6 +46,8 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureName($arr); + self::ensureUrl($arr); + self::ensureBuildNumber($arr); self::ensureGit($arr); return new self( @@ -57,20 +59,39 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } + } + + /** + * @psalm-assert array{url: string|int|bool} $arr + */ + private static function ensureUrl(array $arr): void + { + if (array_key_exists('url', $arr) && is_array($arr['url'])) { + throw new SchemaViolationException('Property \'url\' was array'); + } + } + + /** + * @psalm-assert array{buildNumber: string|int|bool} $arr + */ + private static function ensureBuildNumber(array $arr): void + { + if (array_key_exists('buildNumber', $arr) && is_array($arr['buildNumber'])) { + throw new SchemaViolationException('Property \'buildNumber\' was array'); + } } /** - * Check that the type of 'git' matches expectations - * * @psalm-assert array{git?: array} $arr */ private static function ensureGit(array $arr): void diff --git a/messages/php/src-generated/Comment.php b/messages/php/src-generated/Comment.php index b58c42c52e7..ca0fe505dd8 100644 --- a/messages/php/src-generated/Comment.php +++ b/messages/php/src-generated/Comment.php @@ -48,8 +48,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -63,14 +61,15 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr + * @psalm-assert array{text: string|int|bool} $arr */ private static function ensureText(array $arr): void { if (!array_key_exists('text', $arr)) { throw new SchemaViolationException('Property \'text\' is required but was not found'); } + if (array_key_exists('text', $arr) && is_array($arr['text'])) { + throw new SchemaViolationException('Property \'text\' was array'); + } } } diff --git a/messages/php/src-generated/DataTable.php b/messages/php/src-generated/DataTable.php index 90b9203d122..989bbad8549 100644 --- a/messages/php/src-generated/DataTable.php +++ b/messages/php/src-generated/DataTable.php @@ -45,8 +45,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -60,8 +58,6 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'rows' matches expectations - * * @psalm-assert array{rows: array} $arr */ private static function ensureRows(array $arr): void diff --git a/messages/php/src-generated/DocString.php b/messages/php/src-generated/DocString.php index e4ae19f0291..551f15502bb 100644 --- a/messages/php/src-generated/DocString.php +++ b/messages/php/src-generated/DocString.php @@ -37,6 +37,7 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureLocation($arr); + self::ensureMediaType($arr); self::ensureContent($arr); self::ensureDelimiter($arr); @@ -49,8 +50,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -64,26 +63,38 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'content' matches expectations - * - * @psalm-assert array{content: mixed} $arr + * @psalm-assert array{mediaType: string|int|bool} $arr + */ + private static function ensureMediaType(array $arr): void + { + if (array_key_exists('mediaType', $arr) && is_array($arr['mediaType'])) { + throw new SchemaViolationException('Property \'mediaType\' was array'); + } + } + + /** + * @psalm-assert array{content: string|int|bool} $arr */ private static function ensureContent(array $arr): void { if (!array_key_exists('content', $arr)) { throw new SchemaViolationException('Property \'content\' is required but was not found'); } + if (array_key_exists('content', $arr) && is_array($arr['content'])) { + throw new SchemaViolationException('Property \'content\' was array'); + } } /** - * Check that the type of 'delimiter' matches expectations - * - * @psalm-assert array{delimiter: mixed} $arr + * @psalm-assert array{delimiter: string|int|bool} $arr */ private static function ensureDelimiter(array $arr): void { if (!array_key_exists('delimiter', $arr)) { throw new SchemaViolationException('Property \'delimiter\' is required but was not found'); } + if (array_key_exists('delimiter', $arr) && is_array($arr['delimiter'])) { + throw new SchemaViolationException('Property \'delimiter\' was array'); + } } } diff --git a/messages/php/src-generated/Duration.php b/messages/php/src-generated/Duration.php index 372ffea375b..fda9bc06da4 100644 --- a/messages/php/src-generated/Duration.php +++ b/messages/php/src-generated/Duration.php @@ -49,26 +49,28 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'seconds' matches expectations - * - * @psalm-assert array{seconds: mixed} $arr + * @psalm-assert array{seconds: string|int|bool} $arr */ private static function ensureSeconds(array $arr): void { if (!array_key_exists('seconds', $arr)) { throw new SchemaViolationException('Property \'seconds\' is required but was not found'); } + if (array_key_exists('seconds', $arr) && is_array($arr['seconds'])) { + throw new SchemaViolationException('Property \'seconds\' was array'); + } } /** - * Check that the type of 'nanos' matches expectations - * - * @psalm-assert array{nanos: mixed} $arr + * @psalm-assert array{nanos: string|int|bool} $arr */ private static function ensureNanos(array $arr): void { if (!array_key_exists('nanos', $arr)) { throw new SchemaViolationException('Property \'nanos\' is required but was not found'); } + if (array_key_exists('nanos', $arr) && is_array($arr['nanos'])) { + throw new SchemaViolationException('Property \'nanos\' was array'); + } } } diff --git a/messages/php/src-generated/Envelope.php b/messages/php/src-generated/Envelope.php index 179d18bac63..9e7b57d819b 100644 --- a/messages/php/src-generated/Envelope.php +++ b/messages/php/src-generated/Envelope.php @@ -107,8 +107,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'attachment' matches expectations - * * @psalm-assert array{attachment?: array} $arr */ private static function ensureAttachment(array $arr): void @@ -119,8 +117,6 @@ private static function ensureAttachment(array $arr): void } /** - * Check that the type of 'gherkinDocument' matches expectations - * * @psalm-assert array{gherkinDocument?: array} $arr */ private static function ensureGherkinDocument(array $arr): void @@ -131,8 +127,6 @@ private static function ensureGherkinDocument(array $arr): void } /** - * Check that the type of 'hook' matches expectations - * * @psalm-assert array{hook?: array} $arr */ private static function ensureHook(array $arr): void @@ -143,8 +137,6 @@ private static function ensureHook(array $arr): void } /** - * Check that the type of 'meta' matches expectations - * * @psalm-assert array{meta?: array} $arr */ private static function ensureMeta(array $arr): void @@ -155,8 +147,6 @@ private static function ensureMeta(array $arr): void } /** - * Check that the type of 'parameterType' matches expectations - * * @psalm-assert array{parameterType?: array} $arr */ private static function ensureParameterType(array $arr): void @@ -167,8 +157,6 @@ private static function ensureParameterType(array $arr): void } /** - * Check that the type of 'parseError' matches expectations - * * @psalm-assert array{parseError?: array} $arr */ private static function ensureParseError(array $arr): void @@ -179,8 +167,6 @@ private static function ensureParseError(array $arr): void } /** - * Check that the type of 'pickle' matches expectations - * * @psalm-assert array{pickle?: array} $arr */ private static function ensurePickle(array $arr): void @@ -191,8 +177,6 @@ private static function ensurePickle(array $arr): void } /** - * Check that the type of 'source' matches expectations - * * @psalm-assert array{source?: array} $arr */ private static function ensureSource(array $arr): void @@ -203,8 +187,6 @@ private static function ensureSource(array $arr): void } /** - * Check that the type of 'stepDefinition' matches expectations - * * @psalm-assert array{stepDefinition?: array} $arr */ private static function ensureStepDefinition(array $arr): void @@ -215,8 +197,6 @@ private static function ensureStepDefinition(array $arr): void } /** - * Check that the type of 'testCase' matches expectations - * * @psalm-assert array{testCase?: array} $arr */ private static function ensureTestCase(array $arr): void @@ -227,8 +207,6 @@ private static function ensureTestCase(array $arr): void } /** - * Check that the type of 'testCaseFinished' matches expectations - * * @psalm-assert array{testCaseFinished?: array} $arr */ private static function ensureTestCaseFinished(array $arr): void @@ -239,8 +217,6 @@ private static function ensureTestCaseFinished(array $arr): void } /** - * Check that the type of 'testCaseStarted' matches expectations - * * @psalm-assert array{testCaseStarted?: array} $arr */ private static function ensureTestCaseStarted(array $arr): void @@ -251,8 +227,6 @@ private static function ensureTestCaseStarted(array $arr): void } /** - * Check that the type of 'testRunFinished' matches expectations - * * @psalm-assert array{testRunFinished?: array} $arr */ private static function ensureTestRunFinished(array $arr): void @@ -263,8 +237,6 @@ private static function ensureTestRunFinished(array $arr): void } /** - * Check that the type of 'testRunStarted' matches expectations - * * @psalm-assert array{testRunStarted?: array} $arr */ private static function ensureTestRunStarted(array $arr): void @@ -275,8 +247,6 @@ private static function ensureTestRunStarted(array $arr): void } /** - * Check that the type of 'testStepFinished' matches expectations - * * @psalm-assert array{testStepFinished?: array} $arr */ private static function ensureTestStepFinished(array $arr): void @@ -287,8 +257,6 @@ private static function ensureTestStepFinished(array $arr): void } /** - * Check that the type of 'testStepStarted' matches expectations - * * @psalm-assert array{testStepStarted?: array} $arr */ private static function ensureTestStepStarted(array $arr): void @@ -299,8 +267,6 @@ private static function ensureTestStepStarted(array $arr): void } /** - * Check that the type of 'undefinedParameterType' matches expectations - * * @psalm-assert array{undefinedParameterType?: array} $arr */ private static function ensureUndefinedParameterType(array $arr): void diff --git a/messages/php/src-generated/Examples.php b/messages/php/src-generated/Examples.php index 0fc7a1d6377..e6863b9dc89 100644 --- a/messages/php/src-generated/Examples.php +++ b/messages/php/src-generated/Examples.php @@ -75,8 +75,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -90,8 +88,6 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'tags' matches expectations - * * @psalm-assert array{tags: array} $arr */ private static function ensureTags(array $arr): void @@ -105,44 +101,45 @@ private static function ensureTags(array $arr): void } /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr + * @psalm-assert array{keyword: string|int|bool} $arr */ private static function ensureKeyword(array $arr): void { if (!array_key_exists('keyword', $arr)) { throw new SchemaViolationException('Property \'keyword\' is required but was not found'); } + if (array_key_exists('keyword', $arr) && is_array($arr['keyword'])) { + throw new SchemaViolationException('Property \'keyword\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr + * @psalm-assert array{description: string|int|bool} $arr */ private static function ensureDescription(array $arr): void { if (!array_key_exists('description', $arr)) { throw new SchemaViolationException('Property \'description\' is required but was not found'); } + if (array_key_exists('description', $arr) && is_array($arr['description'])) { + throw new SchemaViolationException('Property \'description\' was array'); + } } /** - * Check that the type of 'tableHeader' matches expectations - * * @psalm-assert array{tableHeader?: array} $arr */ private static function ensureTableHeader(array $arr): void @@ -153,8 +150,6 @@ private static function ensureTableHeader(array $arr): void } /** - * Check that the type of 'tableBody' matches expectations - * * @psalm-assert array{tableBody: array} $arr */ private static function ensureTableBody(array $arr): void @@ -168,14 +163,15 @@ private static function ensureTableBody(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/Feature.php b/messages/php/src-generated/Feature.php index cdfb5dafbdc..65eb3d67512 100644 --- a/messages/php/src-generated/Feature.php +++ b/messages/php/src-generated/Feature.php @@ -85,8 +85,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -100,8 +98,6 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'tags' matches expectations - * * @psalm-assert array{tags: array} $arr */ private static function ensureTags(array $arr): void @@ -115,56 +111,58 @@ private static function ensureTags(array $arr): void } /** - * Check that the type of 'language' matches expectations - * - * @psalm-assert array{language: mixed} $arr + * @psalm-assert array{language: string|int|bool} $arr */ private static function ensureLanguage(array $arr): void { if (!array_key_exists('language', $arr)) { throw new SchemaViolationException('Property \'language\' is required but was not found'); } + if (array_key_exists('language', $arr) && is_array($arr['language'])) { + throw new SchemaViolationException('Property \'language\' was array'); + } } /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr + * @psalm-assert array{keyword: string|int|bool} $arr */ private static function ensureKeyword(array $arr): void { if (!array_key_exists('keyword', $arr)) { throw new SchemaViolationException('Property \'keyword\' is required but was not found'); } + if (array_key_exists('keyword', $arr) && is_array($arr['keyword'])) { + throw new SchemaViolationException('Property \'keyword\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr + * @psalm-assert array{description: string|int|bool} $arr */ private static function ensureDescription(array $arr): void { if (!array_key_exists('description', $arr)) { throw new SchemaViolationException('Property \'description\' is required but was not found'); } + if (array_key_exists('description', $arr) && is_array($arr['description'])) { + throw new SchemaViolationException('Property \'description\' was array'); + } } /** - * Check that the type of 'children' matches expectations - * * @psalm-assert array{children: array} $arr */ private static function ensureChildren(array $arr): void diff --git a/messages/php/src-generated/FeatureChild.php b/messages/php/src-generated/FeatureChild.php index dc53ad46434..d320e6d6704 100644 --- a/messages/php/src-generated/FeatureChild.php +++ b/messages/php/src-generated/FeatureChild.php @@ -46,8 +46,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'rule' matches expectations - * * @psalm-assert array{rule?: array} $arr */ private static function ensureRule(array $arr): void @@ -58,8 +56,6 @@ private static function ensureRule(array $arr): void } /** - * Check that the type of 'background' matches expectations - * * @psalm-assert array{background?: array} $arr */ private static function ensureBackground(array $arr): void @@ -70,8 +66,6 @@ private static function ensureBackground(array $arr): void } /** - * Check that the type of 'scenario' matches expectations - * * @psalm-assert array{scenario?: array} $arr */ private static function ensureScenario(array $arr): void diff --git a/messages/php/src-generated/GherkinDocument.php b/messages/php/src-generated/GherkinDocument.php index 0a4989958e6..1701680f1ae 100644 --- a/messages/php/src-generated/GherkinDocument.php +++ b/messages/php/src-generated/GherkinDocument.php @@ -47,6 +47,7 @@ private function __construct( */ public static function fromArray(array $arr) : self { + self::ensureUri($arr); self::ensureFeature($arr); self::ensureComments($arr); @@ -58,8 +59,16 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'feature' matches expectations - * + * @psalm-assert array{uri: string|int|bool} $arr + */ + private static function ensureUri(array $arr): void + { + if (array_key_exists('uri', $arr) && is_array($arr['uri'])) { + throw new SchemaViolationException('Property \'uri\' was array'); + } + } + + /** * @psalm-assert array{feature?: array} $arr */ private static function ensureFeature(array $arr): void @@ -70,8 +79,6 @@ private static function ensureFeature(array $arr): void } /** - * Check that the type of 'comments' matches expectations - * * @psalm-assert array{comments: array} $arr */ private static function ensureComments(array $arr): void diff --git a/messages/php/src-generated/Git.php b/messages/php/src-generated/Git.php index b03ad74ab9e..9d81d0dd22f 100644 --- a/messages/php/src-generated/Git.php +++ b/messages/php/src-generated/Git.php @@ -39,6 +39,8 @@ public static function fromArray(array $arr) : self { self::ensureRemote($arr); self::ensureRevision($arr); + self::ensureBranch($arr); + self::ensureTag($arr); return new self( (string) $arr['remote'], @@ -49,26 +51,48 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'remote' matches expectations - * - * @psalm-assert array{remote: mixed} $arr + * @psalm-assert array{remote: string|int|bool} $arr */ private static function ensureRemote(array $arr): void { if (!array_key_exists('remote', $arr)) { throw new SchemaViolationException('Property \'remote\' is required but was not found'); } + if (array_key_exists('remote', $arr) && is_array($arr['remote'])) { + throw new SchemaViolationException('Property \'remote\' was array'); + } } /** - * Check that the type of 'revision' matches expectations - * - * @psalm-assert array{revision: mixed} $arr + * @psalm-assert array{revision: string|int|bool} $arr */ private static function ensureRevision(array $arr): void { if (!array_key_exists('revision', $arr)) { throw new SchemaViolationException('Property \'revision\' is required but was not found'); } + if (array_key_exists('revision', $arr) && is_array($arr['revision'])) { + throw new SchemaViolationException('Property \'revision\' was array'); + } + } + + /** + * @psalm-assert array{branch: string|int|bool} $arr + */ + private static function ensureBranch(array $arr): void + { + if (array_key_exists('branch', $arr) && is_array($arr['branch'])) { + throw new SchemaViolationException('Property \'branch\' was array'); + } + } + + /** + * @psalm-assert array{tag: string|int|bool} $arr + */ + private static function ensureTag(array $arr): void + { + if (array_key_exists('tag', $arr) && is_array($arr['tag'])) { + throw new SchemaViolationException('Property \'tag\' was array'); + } } } diff --git a/messages/php/src-generated/Group.php b/messages/php/src-generated/Group.php index 91b09ed93db..b376df4ce23 100644 --- a/messages/php/src-generated/Group.php +++ b/messages/php/src-generated/Group.php @@ -38,6 +38,8 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureChildren($arr); + self::ensureStart($arr); + self::ensureValue($arr); return new self( array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), @@ -47,8 +49,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'children' matches expectations - * * @psalm-assert array{children: array} $arr */ private static function ensureChildren(array $arr): void @@ -60,4 +60,24 @@ private static function ensureChildren(array $arr): void throw new SchemaViolationException('Property \'children\' was not array'); } } + + /** + * @psalm-assert array{start: string|int|bool} $arr + */ + private static function ensureStart(array $arr): void + { + if (array_key_exists('start', $arr) && is_array($arr['start'])) { + throw new SchemaViolationException('Property \'start\' was array'); + } + } + + /** + * @psalm-assert array{value: string|int|bool} $arr + */ + private static function ensureValue(array $arr): void + { + if (array_key_exists('value', $arr) && is_array($arr['value'])) { + throw new SchemaViolationException('Property \'value\' was array'); + } + } } diff --git a/messages/php/src-generated/Hook.php b/messages/php/src-generated/Hook.php index 4bd18b5d1dd..1193ee9dfe9 100644 --- a/messages/php/src-generated/Hook.php +++ b/messages/php/src-generated/Hook.php @@ -36,6 +36,7 @@ public static function fromArray(array $arr) : self { self::ensureId($arr); self::ensureSourceReference($arr); + self::ensureTagExpression($arr); return new self( (string) $arr['id'], @@ -45,20 +46,19 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } /** - * Check that the type of 'sourceReference' matches expectations - * * @psalm-assert array{sourceReference: array} $arr */ private static function ensureSourceReference(array $arr): void @@ -70,4 +70,14 @@ private static function ensureSourceReference(array $arr): void throw new SchemaViolationException('Property \'sourceReference\' was not array'); } } + + /** + * @psalm-assert array{tagExpression: string|int|bool} $arr + */ + private static function ensureTagExpression(array $arr): void + { + if (array_key_exists('tagExpression', $arr) && is_array($arr['tagExpression'])) { + throw new SchemaViolationException('Property \'tagExpression\' was array'); + } + } } diff --git a/messages/php/src-generated/JavaMethod.php b/messages/php/src-generated/JavaMethod.php index 8b229aff688..cff65a84253 100644 --- a/messages/php/src-generated/JavaMethod.php +++ b/messages/php/src-generated/JavaMethod.php @@ -49,32 +49,32 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'className' matches expectations - * - * @psalm-assert array{className: mixed} $arr + * @psalm-assert array{className: string|int|bool} $arr */ private static function ensureClassName(array $arr): void { if (!array_key_exists('className', $arr)) { throw new SchemaViolationException('Property \'className\' is required but was not found'); } + if (array_key_exists('className', $arr) && is_array($arr['className'])) { + throw new SchemaViolationException('Property \'className\' was array'); + } } /** - * Check that the type of 'methodName' matches expectations - * - * @psalm-assert array{methodName: mixed} $arr + * @psalm-assert array{methodName: string|int|bool} $arr */ private static function ensureMethodName(array $arr): void { if (!array_key_exists('methodName', $arr)) { throw new SchemaViolationException('Property \'methodName\' is required but was not found'); } + if (array_key_exists('methodName', $arr) && is_array($arr['methodName'])) { + throw new SchemaViolationException('Property \'methodName\' was array'); + } } /** - * Check that the type of 'methodParameterTypes' matches expectations - * * @psalm-assert array{methodParameterTypes: array} $arr */ private static function ensureMethodParameterTypes(array $arr): void diff --git a/messages/php/src-generated/JavaStackTraceElement.php b/messages/php/src-generated/JavaStackTraceElement.php index 6d1537aeb52..06632b72dac 100644 --- a/messages/php/src-generated/JavaStackTraceElement.php +++ b/messages/php/src-generated/JavaStackTraceElement.php @@ -46,38 +46,41 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'className' matches expectations - * - * @psalm-assert array{className: mixed} $arr + * @psalm-assert array{className: string|int|bool} $arr */ private static function ensureClassName(array $arr): void { if (!array_key_exists('className', $arr)) { throw new SchemaViolationException('Property \'className\' is required but was not found'); } + if (array_key_exists('className', $arr) && is_array($arr['className'])) { + throw new SchemaViolationException('Property \'className\' was array'); + } } /** - * Check that the type of 'fileName' matches expectations - * - * @psalm-assert array{fileName: mixed} $arr + * @psalm-assert array{fileName: string|int|bool} $arr */ private static function ensureFileName(array $arr): void { if (!array_key_exists('fileName', $arr)) { throw new SchemaViolationException('Property \'fileName\' is required but was not found'); } + if (array_key_exists('fileName', $arr) && is_array($arr['fileName'])) { + throw new SchemaViolationException('Property \'fileName\' was array'); + } } /** - * Check that the type of 'methodName' matches expectations - * - * @psalm-assert array{methodName: mixed} $arr + * @psalm-assert array{methodName: string|int|bool} $arr */ private static function ensureMethodName(array $arr): void { if (!array_key_exists('methodName', $arr)) { throw new SchemaViolationException('Property \'methodName\' is required but was not found'); } + if (array_key_exists('methodName', $arr) && is_array($arr['methodName'])) { + throw new SchemaViolationException('Property \'methodName\' was array'); + } } } diff --git a/messages/php/src-generated/Location.php b/messages/php/src-generated/Location.php index 3e13ed584b5..34b83233943 100644 --- a/messages/php/src-generated/Location.php +++ b/messages/php/src-generated/Location.php @@ -33,6 +33,7 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureLine($arr); + self::ensureColumn($arr); return new self( (int) $arr['line'], @@ -41,14 +42,25 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'line' matches expectations - * - * @psalm-assert array{line: mixed} $arr + * @psalm-assert array{line: string|int|bool} $arr */ private static function ensureLine(array $arr): void { if (!array_key_exists('line', $arr)) { throw new SchemaViolationException('Property \'line\' is required but was not found'); } + if (array_key_exists('line', $arr) && is_array($arr['line'])) { + throw new SchemaViolationException('Property \'line\' was array'); + } + } + + /** + * @psalm-assert array{column: string|int|bool} $arr + */ + private static function ensureColumn(array $arr): void + { + if (array_key_exists('column', $arr) && is_array($arr['column'])) { + throw new SchemaViolationException('Property \'column\' was array'); + } } } diff --git a/messages/php/src-generated/Meta.php b/messages/php/src-generated/Meta.php index 9f4aed250c1..0b39cb31bdd 100644 --- a/messages/php/src-generated/Meta.php +++ b/messages/php/src-generated/Meta.php @@ -74,20 +74,19 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'protocolVersion' matches expectations - * - * @psalm-assert array{protocolVersion: mixed} $arr + * @psalm-assert array{protocolVersion: string|int|bool} $arr */ private static function ensureProtocolVersion(array $arr): void { if (!array_key_exists('protocolVersion', $arr)) { throw new SchemaViolationException('Property \'protocolVersion\' is required but was not found'); } + if (array_key_exists('protocolVersion', $arr) && is_array($arr['protocolVersion'])) { + throw new SchemaViolationException('Property \'protocolVersion\' was array'); + } } /** - * Check that the type of 'implementation' matches expectations - * * @psalm-assert array{implementation: array} $arr */ private static function ensureImplementation(array $arr): void @@ -101,8 +100,6 @@ private static function ensureImplementation(array $arr): void } /** - * Check that the type of 'runtime' matches expectations - * * @psalm-assert array{runtime: array} $arr */ private static function ensureRuntime(array $arr): void @@ -116,8 +113,6 @@ private static function ensureRuntime(array $arr): void } /** - * Check that the type of 'os' matches expectations - * * @psalm-assert array{os: array} $arr */ private static function ensureOs(array $arr): void @@ -131,8 +126,6 @@ private static function ensureOs(array $arr): void } /** - * Check that the type of 'cpu' matches expectations - * * @psalm-assert array{cpu: array} $arr */ private static function ensureCpu(array $arr): void @@ -146,8 +139,6 @@ private static function ensureCpu(array $arr): void } /** - * Check that the type of 'ci' matches expectations - * * @psalm-assert array{ci?: array} $arr */ private static function ensureCi(array $arr): void diff --git a/messages/php/src-generated/ParameterType.php b/messages/php/src-generated/ParameterType.php index 89cc3a04aea..1e60af6757d 100644 --- a/messages/php/src-generated/ParameterType.php +++ b/messages/php/src-generated/ParameterType.php @@ -60,20 +60,19 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'regularExpressions' matches expectations - * * @psalm-assert array{regularExpressions: array} $arr */ private static function ensureRegularExpressions(array $arr): void @@ -87,38 +86,41 @@ private static function ensureRegularExpressions(array $arr): void } /** - * Check that the type of 'preferForRegularExpressionMatch' matches expectations - * - * @psalm-assert array{preferForRegularExpressionMatch: mixed} $arr + * @psalm-assert array{preferForRegularExpressionMatch: string|int|bool} $arr */ private static function ensurePreferForRegularExpressionMatch(array $arr): void { if (!array_key_exists('preferForRegularExpressionMatch', $arr)) { throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' is required but was not found'); } + if (array_key_exists('preferForRegularExpressionMatch', $arr) && is_array($arr['preferForRegularExpressionMatch'])) { + throw new SchemaViolationException('Property \'preferForRegularExpressionMatch\' was array'); + } } /** - * Check that the type of 'useForSnippets' matches expectations - * - * @psalm-assert array{useForSnippets: mixed} $arr + * @psalm-assert array{useForSnippets: string|int|bool} $arr */ private static function ensureUseForSnippets(array $arr): void { if (!array_key_exists('useForSnippets', $arr)) { throw new SchemaViolationException('Property \'useForSnippets\' is required but was not found'); } + if (array_key_exists('useForSnippets', $arr) && is_array($arr['useForSnippets'])) { + throw new SchemaViolationException('Property \'useForSnippets\' was array'); + } } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/ParseError.php b/messages/php/src-generated/ParseError.php index fe416b9e69b..abc09393ec9 100644 --- a/messages/php/src-generated/ParseError.php +++ b/messages/php/src-generated/ParseError.php @@ -42,8 +42,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'source' matches expectations - * * @psalm-assert array{source: array} $arr */ private static function ensureSource(array $arr): void @@ -57,14 +55,15 @@ private static function ensureSource(array $arr): void } /** - * Check that the type of 'message' matches expectations - * - * @psalm-assert array{message: mixed} $arr + * @psalm-assert array{message: string|int|bool} $arr */ private static function ensureMessage(array $arr): void { if (!array_key_exists('message', $arr)) { throw new SchemaViolationException('Property \'message\' is required but was not found'); } + if (array_key_exists('message', $arr) && is_array($arr['message'])) { + throw new SchemaViolationException('Property \'message\' was array'); + } } } diff --git a/messages/php/src-generated/Pickle.php b/messages/php/src-generated/Pickle.php index c951601f3e2..a87487d5ddb 100644 --- a/messages/php/src-generated/Pickle.php +++ b/messages/php/src-generated/Pickle.php @@ -102,56 +102,58 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } /** - * Check that the type of 'uri' matches expectations - * - * @psalm-assert array{uri: mixed} $arr + * @psalm-assert array{uri: string|int|bool} $arr */ private static function ensureUri(array $arr): void { if (!array_key_exists('uri', $arr)) { throw new SchemaViolationException('Property \'uri\' is required but was not found'); } + if (array_key_exists('uri', $arr) && is_array($arr['uri'])) { + throw new SchemaViolationException('Property \'uri\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'language' matches expectations - * - * @psalm-assert array{language: mixed} $arr + * @psalm-assert array{language: string|int|bool} $arr */ private static function ensureLanguage(array $arr): void { if (!array_key_exists('language', $arr)) { throw new SchemaViolationException('Property \'language\' is required but was not found'); } + if (array_key_exists('language', $arr) && is_array($arr['language'])) { + throw new SchemaViolationException('Property \'language\' was array'); + } } /** - * Check that the type of 'steps' matches expectations - * * @psalm-assert array{steps: array} $arr */ private static function ensureSteps(array $arr): void @@ -165,8 +167,6 @@ private static function ensureSteps(array $arr): void } /** - * Check that the type of 'tags' matches expectations - * * @psalm-assert array{tags: array} $arr */ private static function ensureTags(array $arr): void @@ -180,8 +180,6 @@ private static function ensureTags(array $arr): void } /** - * Check that the type of 'astNodeIds' matches expectations - * * @psalm-assert array{astNodeIds: array} $arr */ private static function ensureAstNodeIds(array $arr): void diff --git a/messages/php/src-generated/PickleDocString.php b/messages/php/src-generated/PickleDocString.php index 561cb2609ed..f032c3f1eb1 100644 --- a/messages/php/src-generated/PickleDocString.php +++ b/messages/php/src-generated/PickleDocString.php @@ -32,6 +32,7 @@ private function __construct( */ public static function fromArray(array $arr) : self { + self::ensureMediaType($arr); self::ensureContent($arr); return new self( @@ -41,14 +42,25 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'content' matches expectations - * - * @psalm-assert array{content: mixed} $arr + * @psalm-assert array{mediaType: string|int|bool} $arr + */ + private static function ensureMediaType(array $arr): void + { + if (array_key_exists('mediaType', $arr) && is_array($arr['mediaType'])) { + throw new SchemaViolationException('Property \'mediaType\' was array'); + } + } + + /** + * @psalm-assert array{content: string|int|bool} $arr */ private static function ensureContent(array $arr): void { if (!array_key_exists('content', $arr)) { throw new SchemaViolationException('Property \'content\' is required but was not found'); } + if (array_key_exists('content', $arr) && is_array($arr['content'])) { + throw new SchemaViolationException('Property \'content\' was array'); + } } } diff --git a/messages/php/src-generated/PickleStep.php b/messages/php/src-generated/PickleStep.php index a9a81433635..8793ccf3ad3 100644 --- a/messages/php/src-generated/PickleStep.php +++ b/messages/php/src-generated/PickleStep.php @@ -58,8 +58,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'argument' matches expectations - * * @psalm-assert array{argument?: array} $arr */ private static function ensureArgument(array $arr): void @@ -70,8 +68,6 @@ private static function ensureArgument(array $arr): void } /** - * Check that the type of 'astNodeIds' matches expectations - * * @psalm-assert array{astNodeIds: array} $arr */ private static function ensureAstNodeIds(array $arr): void @@ -85,26 +81,28 @@ private static function ensureAstNodeIds(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr + * @psalm-assert array{text: string|int|bool} $arr */ private static function ensureText(array $arr): void { if (!array_key_exists('text', $arr)) { throw new SchemaViolationException('Property \'text\' is required but was not found'); } + if (array_key_exists('text', $arr) && is_array($arr['text'])) { + throw new SchemaViolationException('Property \'text\' was array'); + } } } diff --git a/messages/php/src-generated/PickleStepArgument.php b/messages/php/src-generated/PickleStepArgument.php index c0e4f08ac2e..f27a96f2223 100644 --- a/messages/php/src-generated/PickleStepArgument.php +++ b/messages/php/src-generated/PickleStepArgument.php @@ -42,8 +42,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'docString' matches expectations - * * @psalm-assert array{docString?: array} $arr */ private static function ensureDocString(array $arr): void @@ -54,8 +52,6 @@ private static function ensureDocString(array $arr): void } /** - * Check that the type of 'dataTable' matches expectations - * * @psalm-assert array{dataTable?: array} $arr */ private static function ensureDataTable(array $arr): void diff --git a/messages/php/src-generated/PickleTable.php b/messages/php/src-generated/PickleTable.php index 5bb3ef262a2..2aa7afe38d6 100644 --- a/messages/php/src-generated/PickleTable.php +++ b/messages/php/src-generated/PickleTable.php @@ -41,8 +41,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'rows' matches expectations - * * @psalm-assert array{rows: array} $arr */ private static function ensureRows(array $arr): void diff --git a/messages/php/src-generated/PickleTableCell.php b/messages/php/src-generated/PickleTableCell.php index 047616854ab..f894f3fdb9d 100644 --- a/messages/php/src-generated/PickleTableCell.php +++ b/messages/php/src-generated/PickleTableCell.php @@ -38,14 +38,15 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'value' matches expectations - * - * @psalm-assert array{value: mixed} $arr + * @psalm-assert array{value: string|int|bool} $arr */ private static function ensureValue(array $arr): void { if (!array_key_exists('value', $arr)) { throw new SchemaViolationException('Property \'value\' is required but was not found'); } + if (array_key_exists('value', $arr) && is_array($arr['value'])) { + throw new SchemaViolationException('Property \'value\' was array'); + } } } diff --git a/messages/php/src-generated/PickleTableRow.php b/messages/php/src-generated/PickleTableRow.php index cff65d71d1d..8a1e7c5b196 100644 --- a/messages/php/src-generated/PickleTableRow.php +++ b/messages/php/src-generated/PickleTableRow.php @@ -41,8 +41,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'cells' matches expectations - * * @psalm-assert array{cells: array} $arr */ private static function ensureCells(array $arr): void diff --git a/messages/php/src-generated/PickleTag.php b/messages/php/src-generated/PickleTag.php index b24eee18ba5..8fc335d9ad5 100644 --- a/messages/php/src-generated/PickleTag.php +++ b/messages/php/src-generated/PickleTag.php @@ -45,26 +45,28 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'astNodeId' matches expectations - * - * @psalm-assert array{astNodeId: mixed} $arr + * @psalm-assert array{astNodeId: string|int|bool} $arr */ private static function ensureAstNodeId(array $arr): void { if (!array_key_exists('astNodeId', $arr)) { throw new SchemaViolationException('Property \'astNodeId\' is required but was not found'); } + if (array_key_exists('astNodeId', $arr) && is_array($arr['astNodeId'])) { + throw new SchemaViolationException('Property \'astNodeId\' was array'); + } } } diff --git a/messages/php/src-generated/Product.php b/messages/php/src-generated/Product.php index 8a76b303a29..a908eb202c7 100644 --- a/messages/php/src-generated/Product.php +++ b/messages/php/src-generated/Product.php @@ -39,6 +39,7 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureName($arr); + self::ensureVersion($arr); return new self( (string) $arr['name'], @@ -47,14 +48,25 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } + } + + /** + * @psalm-assert array{version: string|int|bool} $arr + */ + private static function ensureVersion(array $arr): void + { + if (array_key_exists('version', $arr) && is_array($arr['version'])) { + throw new SchemaViolationException('Property \'version\' was array'); + } } } diff --git a/messages/php/src-generated/Rule.php b/messages/php/src-generated/Rule.php index 6ce5029a6de..f41617ae713 100644 --- a/messages/php/src-generated/Rule.php +++ b/messages/php/src-generated/Rule.php @@ -72,8 +72,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -87,8 +85,6 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'tags' matches expectations - * * @psalm-assert array{tags: array} $arr */ private static function ensureTags(array $arr): void @@ -102,44 +98,45 @@ private static function ensureTags(array $arr): void } /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr + * @psalm-assert array{keyword: string|int|bool} $arr */ private static function ensureKeyword(array $arr): void { if (!array_key_exists('keyword', $arr)) { throw new SchemaViolationException('Property \'keyword\' is required but was not found'); } + if (array_key_exists('keyword', $arr) && is_array($arr['keyword'])) { + throw new SchemaViolationException('Property \'keyword\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr + * @psalm-assert array{description: string|int|bool} $arr */ private static function ensureDescription(array $arr): void { if (!array_key_exists('description', $arr)) { throw new SchemaViolationException('Property \'description\' is required but was not found'); } + if (array_key_exists('description', $arr) && is_array($arr['description'])) { + throw new SchemaViolationException('Property \'description\' was array'); + } } /** - * Check that the type of 'children' matches expectations - * * @psalm-assert array{children: array} $arr */ private static function ensureChildren(array $arr): void @@ -153,14 +150,15 @@ private static function ensureChildren(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/RuleChild.php b/messages/php/src-generated/RuleChild.php index 6471aa1db92..f35f2287ce4 100644 --- a/messages/php/src-generated/RuleChild.php +++ b/messages/php/src-generated/RuleChild.php @@ -42,8 +42,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'background' matches expectations - * * @psalm-assert array{background?: array} $arr */ private static function ensureBackground(array $arr): void @@ -54,8 +52,6 @@ private static function ensureBackground(array $arr): void } /** - * Check that the type of 'scenario' matches expectations - * * @psalm-assert array{scenario?: array} $arr */ private static function ensureScenario(array $arr): void diff --git a/messages/php/src-generated/Scenario.php b/messages/php/src-generated/Scenario.php index ad1686f7b7d..957036011aa 100644 --- a/messages/php/src-generated/Scenario.php +++ b/messages/php/src-generated/Scenario.php @@ -78,8 +78,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -93,8 +91,6 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'tags' matches expectations - * * @psalm-assert array{tags: array} $arr */ private static function ensureTags(array $arr): void @@ -108,44 +104,45 @@ private static function ensureTags(array $arr): void } /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr + * @psalm-assert array{keyword: string|int|bool} $arr */ private static function ensureKeyword(array $arr): void { if (!array_key_exists('keyword', $arr)) { throw new SchemaViolationException('Property \'keyword\' is required but was not found'); } + if (array_key_exists('keyword', $arr) && is_array($arr['keyword'])) { + throw new SchemaViolationException('Property \'keyword\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'description' matches expectations - * - * @psalm-assert array{description: mixed} $arr + * @psalm-assert array{description: string|int|bool} $arr */ private static function ensureDescription(array $arr): void { if (!array_key_exists('description', $arr)) { throw new SchemaViolationException('Property \'description\' is required but was not found'); } + if (array_key_exists('description', $arr) && is_array($arr['description'])) { + throw new SchemaViolationException('Property \'description\' was array'); + } } /** - * Check that the type of 'steps' matches expectations - * * @psalm-assert array{steps: array} $arr */ private static function ensureSteps(array $arr): void @@ -159,8 +156,6 @@ private static function ensureSteps(array $arr): void } /** - * Check that the type of 'examples' matches expectations - * * @psalm-assert array{examples: array} $arr */ private static function ensureExamples(array $arr): void @@ -174,14 +169,15 @@ private static function ensureExamples(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/Source.php b/messages/php/src-generated/Source.php index a70a6dc0c52..4218e967c8a 100644 --- a/messages/php/src-generated/Source.php +++ b/messages/php/src-generated/Source.php @@ -59,38 +59,41 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'uri' matches expectations - * - * @psalm-assert array{uri: mixed} $arr + * @psalm-assert array{uri: string|int|bool} $arr */ private static function ensureUri(array $arr): void { if (!array_key_exists('uri', $arr)) { throw new SchemaViolationException('Property \'uri\' is required but was not found'); } + if (array_key_exists('uri', $arr) && is_array($arr['uri'])) { + throw new SchemaViolationException('Property \'uri\' was array'); + } } /** - * Check that the type of 'data' matches expectations - * - * @psalm-assert array{data: mixed} $arr + * @psalm-assert array{data: string|int|bool} $arr */ private static function ensureData(array $arr): void { if (!array_key_exists('data', $arr)) { throw new SchemaViolationException('Property \'data\' is required but was not found'); } + if (array_key_exists('data', $arr) && is_array($arr['data'])) { + throw new SchemaViolationException('Property \'data\' was array'); + } } /** - * Check that the type of 'mediaType' matches expectations - * - * @psalm-assert array{mediaType: mixed} $arr + * @psalm-assert array{mediaType: string|int|bool} $arr */ private static function ensureMediaType(array $arr): void { if (!array_key_exists('mediaType', $arr)) { throw new SchemaViolationException('Property \'mediaType\' is required but was not found'); } + if (array_key_exists('mediaType', $arr) && is_array($arr['mediaType'])) { + throw new SchemaViolationException('Property \'mediaType\' was array'); + } } } diff --git a/messages/php/src-generated/SourceReference.php b/messages/php/src-generated/SourceReference.php index f9d8228c8b3..31637d777ae 100644 --- a/messages/php/src-generated/SourceReference.php +++ b/messages/php/src-generated/SourceReference.php @@ -37,6 +37,7 @@ private function __construct( */ public static function fromArray(array $arr) : self { + self::ensureUri($arr); self::ensureJavaMethod($arr); self::ensureJavaStackTraceElement($arr); self::ensureLocation($arr); @@ -50,8 +51,16 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'javaMethod' matches expectations - * + * @psalm-assert array{uri: string|int|bool} $arr + */ + private static function ensureUri(array $arr): void + { + if (array_key_exists('uri', $arr) && is_array($arr['uri'])) { + throw new SchemaViolationException('Property \'uri\' was array'); + } + } + + /** * @psalm-assert array{javaMethod?: array} $arr */ private static function ensureJavaMethod(array $arr): void @@ -62,8 +71,6 @@ private static function ensureJavaMethod(array $arr): void } /** - * Check that the type of 'javaStackTraceElement' matches expectations - * * @psalm-assert array{javaStackTraceElement?: array} $arr */ private static function ensureJavaStackTraceElement(array $arr): void @@ -74,8 +81,6 @@ private static function ensureJavaStackTraceElement(array $arr): void } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location?: array} $arr */ private static function ensureLocation(array $arr): void diff --git a/messages/php/src-generated/Step.php b/messages/php/src-generated/Step.php index c4b6c23a9c7..a3c4ae42c9c 100644 --- a/messages/php/src-generated/Step.php +++ b/messages/php/src-generated/Step.php @@ -64,8 +64,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -79,32 +77,32 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'keyword' matches expectations - * - * @psalm-assert array{keyword: mixed} $arr + * @psalm-assert array{keyword: string|int|bool} $arr */ private static function ensureKeyword(array $arr): void { if (!array_key_exists('keyword', $arr)) { throw new SchemaViolationException('Property \'keyword\' is required but was not found'); } + if (array_key_exists('keyword', $arr) && is_array($arr['keyword'])) { + throw new SchemaViolationException('Property \'keyword\' was array'); + } } /** - * Check that the type of 'text' matches expectations - * - * @psalm-assert array{text: mixed} $arr + * @psalm-assert array{text: string|int|bool} $arr */ private static function ensureText(array $arr): void { if (!array_key_exists('text', $arr)) { throw new SchemaViolationException('Property \'text\' is required but was not found'); } + if (array_key_exists('text', $arr) && is_array($arr['text'])) { + throw new SchemaViolationException('Property \'text\' was array'); + } } /** - * Check that the type of 'docString' matches expectations - * * @psalm-assert array{docString?: array} $arr */ private static function ensureDocString(array $arr): void @@ -115,8 +113,6 @@ private static function ensureDocString(array $arr): void } /** - * Check that the type of 'dataTable' matches expectations - * * @psalm-assert array{dataTable?: array} $arr */ private static function ensureDataTable(array $arr): void @@ -127,14 +123,15 @@ private static function ensureDataTable(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/StepDefinition.php b/messages/php/src-generated/StepDefinition.php index 5b6a702af26..681a07ef747 100644 --- a/messages/php/src-generated/StepDefinition.php +++ b/messages/php/src-generated/StepDefinition.php @@ -46,20 +46,19 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } /** - * Check that the type of 'pattern' matches expectations - * * @psalm-assert array{pattern: array} $arr */ private static function ensurePattern(array $arr): void @@ -73,8 +72,6 @@ private static function ensurePattern(array $arr): void } /** - * Check that the type of 'sourceReference' matches expectations - * * @psalm-assert array{sourceReference: array} $arr */ private static function ensureSourceReference(array $arr): void diff --git a/messages/php/src-generated/StepDefinitionPattern.php b/messages/php/src-generated/StepDefinitionPattern.php index fc624ba862c..dfe0c6489a6 100644 --- a/messages/php/src-generated/StepDefinitionPattern.php +++ b/messages/php/src-generated/StepDefinitionPattern.php @@ -42,26 +42,28 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'source' matches expectations - * - * @psalm-assert array{source: mixed} $arr + * @psalm-assert array{source: string|int|bool} $arr */ private static function ensureSource(array $arr): void { if (!array_key_exists('source', $arr)) { throw new SchemaViolationException('Property \'source\' is required but was not found'); } + if (array_key_exists('source', $arr) && is_array($arr['source'])) { + throw new SchemaViolationException('Property \'source\' was array'); + } } /** - * Check that the type of 'type' matches expectations - * - * @psalm-assert array{type: mixed} $arr + * @psalm-assert array{type: string|int|bool} $arr */ private static function ensureType(array $arr): void { if (!array_key_exists('type', $arr)) { throw new SchemaViolationException('Property \'type\' is required but was not found'); } + if (array_key_exists('type', $arr) && is_array($arr['type'])) { + throw new SchemaViolationException('Property \'type\' was array'); + } } } diff --git a/messages/php/src-generated/StepMatchArgument.php b/messages/php/src-generated/StepMatchArgument.php index 410330b999c..08738dfa8d7 100644 --- a/messages/php/src-generated/StepMatchArgument.php +++ b/messages/php/src-generated/StepMatchArgument.php @@ -42,6 +42,7 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureGroup($arr); + self::ensureParameterTypeName($arr); return new self( Group::fromArray($arr['group']), @@ -50,8 +51,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'group' matches expectations - * * @psalm-assert array{group: array} $arr */ private static function ensureGroup(array $arr): void @@ -63,4 +62,14 @@ private static function ensureGroup(array $arr): void throw new SchemaViolationException('Property \'group\' was not array'); } } + + /** + * @psalm-assert array{parameterTypeName: string|int|bool} $arr + */ + private static function ensureParameterTypeName(array $arr): void + { + if (array_key_exists('parameterTypeName', $arr) && is_array($arr['parameterTypeName'])) { + throw new SchemaViolationException('Property \'parameterTypeName\' was array'); + } + } } diff --git a/messages/php/src-generated/StepMatchArgumentsList.php b/messages/php/src-generated/StepMatchArgumentsList.php index 226da963cbe..9bcced16101 100644 --- a/messages/php/src-generated/StepMatchArgumentsList.php +++ b/messages/php/src-generated/StepMatchArgumentsList.php @@ -41,8 +41,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'stepMatchArguments' matches expectations - * * @psalm-assert array{stepMatchArguments: array} $arr */ private static function ensureStepMatchArguments(array $arr): void diff --git a/messages/php/src-generated/TableCell.php b/messages/php/src-generated/TableCell.php index 7bc2fdf8a73..6374763dd64 100644 --- a/messages/php/src-generated/TableCell.php +++ b/messages/php/src-generated/TableCell.php @@ -48,8 +48,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -63,14 +61,15 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'value' matches expectations - * - * @psalm-assert array{value: mixed} $arr + * @psalm-assert array{value: string|int|bool} $arr */ private static function ensureValue(array $arr): void { if (!array_key_exists('value', $arr)) { throw new SchemaViolationException('Property \'value\' is required but was not found'); } + if (array_key_exists('value', $arr) && is_array($arr['value'])) { + throw new SchemaViolationException('Property \'value\' was array'); + } } } diff --git a/messages/php/src-generated/TableRow.php b/messages/php/src-generated/TableRow.php index 03c95d180e0..c539632ebc4 100644 --- a/messages/php/src-generated/TableRow.php +++ b/messages/php/src-generated/TableRow.php @@ -53,8 +53,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -68,8 +66,6 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'cells' matches expectations - * * @psalm-assert array{cells: array} $arr */ private static function ensureCells(array $arr): void @@ -83,14 +79,15 @@ private static function ensureCells(array $arr): void } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/Tag.php b/messages/php/src-generated/Tag.php index 996c1d704aa..d2960c6032c 100644 --- a/messages/php/src-generated/Tag.php +++ b/messages/php/src-generated/Tag.php @@ -55,8 +55,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'location' matches expectations - * * @psalm-assert array{location: array} $arr */ private static function ensureLocation(array $arr): void @@ -70,26 +68,28 @@ private static function ensureLocation(array $arr): void } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } } diff --git a/messages/php/src-generated/TestCase.php b/messages/php/src-generated/TestCase.php index 608050bf30e..2691db7e690 100644 --- a/messages/php/src-generated/TestCase.php +++ b/messages/php/src-generated/TestCase.php @@ -54,32 +54,32 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } /** - * Check that the type of 'pickleId' matches expectations - * - * @psalm-assert array{pickleId: mixed} $arr + * @psalm-assert array{pickleId: string|int|bool} $arr */ private static function ensurePickleId(array $arr): void { if (!array_key_exists('pickleId', $arr)) { throw new SchemaViolationException('Property \'pickleId\' is required but was not found'); } + if (array_key_exists('pickleId', $arr) && is_array($arr['pickleId'])) { + throw new SchemaViolationException('Property \'pickleId\' was array'); + } } /** - * Check that the type of 'testSteps' matches expectations - * * @psalm-assert array{testSteps: array} $arr */ private static function ensureTestSteps(array $arr): void diff --git a/messages/php/src-generated/TestCaseFinished.php b/messages/php/src-generated/TestCaseFinished.php index b3faef541d5..7c0a3ba0814 100644 --- a/messages/php/src-generated/TestCaseFinished.php +++ b/messages/php/src-generated/TestCaseFinished.php @@ -46,20 +46,19 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr + * @psalm-assert array{testCaseStartedId: string|int|bool} $arr */ private static function ensureTestCaseStartedId(array $arr): void { if (!array_key_exists('testCaseStartedId', $arr)) { throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); } + if (array_key_exists('testCaseStartedId', $arr) && is_array($arr['testCaseStartedId'])) { + throw new SchemaViolationException('Property \'testCaseStartedId\' was array'); + } } /** - * Check that the type of 'timestamp' matches expectations - * * @psalm-assert array{timestamp: array} $arr */ private static function ensureTimestamp(array $arr): void @@ -73,14 +72,15 @@ private static function ensureTimestamp(array $arr): void } /** - * Check that the type of 'willBeRetried' matches expectations - * - * @psalm-assert array{willBeRetried: mixed} $arr + * @psalm-assert array{willBeRetried: string|int|bool} $arr */ private static function ensureWillBeRetried(array $arr): void { if (!array_key_exists('willBeRetried', $arr)) { throw new SchemaViolationException('Property \'willBeRetried\' is required but was not found'); } + if (array_key_exists('willBeRetried', $arr) && is_array($arr['willBeRetried'])) { + throw new SchemaViolationException('Property \'willBeRetried\' was array'); + } } } diff --git a/messages/php/src-generated/TestCaseStarted.php b/messages/php/src-generated/TestCaseStarted.php index 52166d86b53..e243fa5c82c 100644 --- a/messages/php/src-generated/TestCaseStarted.php +++ b/messages/php/src-generated/TestCaseStarted.php @@ -58,44 +58,45 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'attempt' matches expectations - * - * @psalm-assert array{attempt: mixed} $arr + * @psalm-assert array{attempt: string|int|bool} $arr */ private static function ensureAttempt(array $arr): void { if (!array_key_exists('attempt', $arr)) { throw new SchemaViolationException('Property \'attempt\' is required but was not found'); } + if (array_key_exists('attempt', $arr) && is_array($arr['attempt'])) { + throw new SchemaViolationException('Property \'attempt\' was array'); + } } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } } /** - * Check that the type of 'testCaseId' matches expectations - * - * @psalm-assert array{testCaseId: mixed} $arr + * @psalm-assert array{testCaseId: string|int|bool} $arr */ private static function ensureTestCaseId(array $arr): void { if (!array_key_exists('testCaseId', $arr)) { throw new SchemaViolationException('Property \'testCaseId\' is required but was not found'); } + if (array_key_exists('testCaseId', $arr) && is_array($arr['testCaseId'])) { + throw new SchemaViolationException('Property \'testCaseId\' was array'); + } } /** - * Check that the type of 'timestamp' matches expectations - * * @psalm-assert array{timestamp: array} $arr */ private static function ensureTimestamp(array $arr): void diff --git a/messages/php/src-generated/TestRunFinished.php b/messages/php/src-generated/TestRunFinished.php index 704ba1db4e3..47a83a3b398 100644 --- a/messages/php/src-generated/TestRunFinished.php +++ b/messages/php/src-generated/TestRunFinished.php @@ -47,6 +47,7 @@ private function __construct( */ public static function fromArray(array $arr) : self { + self::ensureMessage($arr); self::ensureSuccess($arr); self::ensureTimestamp($arr); @@ -58,20 +59,29 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'success' matches expectations - * - * @psalm-assert array{success: mixed} $arr + * @psalm-assert array{message: string|int|bool} $arr + */ + private static function ensureMessage(array $arr): void + { + if (array_key_exists('message', $arr) && is_array($arr['message'])) { + throw new SchemaViolationException('Property \'message\' was array'); + } + } + + /** + * @psalm-assert array{success: string|int|bool} $arr */ private static function ensureSuccess(array $arr): void { if (!array_key_exists('success', $arr)) { throw new SchemaViolationException('Property \'success\' is required but was not found'); } + if (array_key_exists('success', $arr) && is_array($arr['success'])) { + throw new SchemaViolationException('Property \'success\' was array'); + } } /** - * Check that the type of 'timestamp' matches expectations - * * @psalm-assert array{timestamp: array} $arr */ private static function ensureTimestamp(array $arr): void diff --git a/messages/php/src-generated/TestRunStarted.php b/messages/php/src-generated/TestRunStarted.php index 7f167949f44..17fb6dcdfe0 100644 --- a/messages/php/src-generated/TestRunStarted.php +++ b/messages/php/src-generated/TestRunStarted.php @@ -38,8 +38,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'timestamp' matches expectations - * * @psalm-assert array{timestamp: array} $arr */ private static function ensureTimestamp(array $arr): void diff --git a/messages/php/src-generated/TestStep.php b/messages/php/src-generated/TestStep.php index a2a476e6a72..eb900b23271 100644 --- a/messages/php/src-generated/TestStep.php +++ b/messages/php/src-generated/TestStep.php @@ -55,7 +55,9 @@ private function __construct( */ public static function fromArray(array $arr) : self { + self::ensureHookId($arr); self::ensureId($arr); + self::ensurePickleStepId($arr); self::ensureStepDefinitionIds($arr); self::ensureStepMatchArgumentsLists($arr); @@ -69,20 +71,39 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'id' matches expectations - * - * @psalm-assert array{id: mixed} $arr + * @psalm-assert array{hookId: string|int|bool} $arr + */ + private static function ensureHookId(array $arr): void + { + if (array_key_exists('hookId', $arr) && is_array($arr['hookId'])) { + throw new SchemaViolationException('Property \'hookId\' was array'); + } + } + + /** + * @psalm-assert array{id: string|int|bool} $arr */ private static function ensureId(array $arr): void { if (!array_key_exists('id', $arr)) { throw new SchemaViolationException('Property \'id\' is required but was not found'); } + if (array_key_exists('id', $arr) && is_array($arr['id'])) { + throw new SchemaViolationException('Property \'id\' was array'); + } + } + + /** + * @psalm-assert array{pickleStepId: string|int|bool} $arr + */ + private static function ensurePickleStepId(array $arr): void + { + if (array_key_exists('pickleStepId', $arr) && is_array($arr['pickleStepId'])) { + throw new SchemaViolationException('Property \'pickleStepId\' was array'); + } } /** - * Check that the type of 'stepDefinitionIds' matches expectations - * * @psalm-assert array{stepDefinitionIds?: array} $arr */ private static function ensureStepDefinitionIds(array $arr): void @@ -93,8 +114,6 @@ private static function ensureStepDefinitionIds(array $arr): void } /** - * Check that the type of 'stepMatchArgumentsLists' matches expectations - * * @psalm-assert array{stepMatchArgumentsLists?: array} $arr */ private static function ensureStepMatchArgumentsLists(array $arr): void diff --git a/messages/php/src-generated/TestStepFinished.php b/messages/php/src-generated/TestStepFinished.php index f5b803a107a..5948488c046 100644 --- a/messages/php/src-generated/TestStepFinished.php +++ b/messages/php/src-generated/TestStepFinished.php @@ -50,32 +50,32 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr + * @psalm-assert array{testCaseStartedId: string|int|bool} $arr */ private static function ensureTestCaseStartedId(array $arr): void { if (!array_key_exists('testCaseStartedId', $arr)) { throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); } + if (array_key_exists('testCaseStartedId', $arr) && is_array($arr['testCaseStartedId'])) { + throw new SchemaViolationException('Property \'testCaseStartedId\' was array'); + } } /** - * Check that the type of 'testStepId' matches expectations - * - * @psalm-assert array{testStepId: mixed} $arr + * @psalm-assert array{testStepId: string|int|bool} $arr */ private static function ensureTestStepId(array $arr): void { if (!array_key_exists('testStepId', $arr)) { throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); } + if (array_key_exists('testStepId', $arr) && is_array($arr['testStepId'])) { + throw new SchemaViolationException('Property \'testStepId\' was array'); + } } /** - * Check that the type of 'testStepResult' matches expectations - * * @psalm-assert array{testStepResult: array} $arr */ private static function ensureTestStepResult(array $arr): void @@ -89,8 +89,6 @@ private static function ensureTestStepResult(array $arr): void } /** - * Check that the type of 'timestamp' matches expectations - * * @psalm-assert array{timestamp: array} $arr */ private static function ensureTimestamp(array $arr): void diff --git a/messages/php/src-generated/TestStepResult.php b/messages/php/src-generated/TestStepResult.php index d8ad6964b23..3f97e29dcd3 100644 --- a/messages/php/src-generated/TestStepResult.php +++ b/messages/php/src-generated/TestStepResult.php @@ -35,6 +35,7 @@ private function __construct( public static function fromArray(array $arr) : self { self::ensureDuration($arr); + self::ensureMessage($arr); self::ensureStatus($arr); return new self( @@ -45,8 +46,6 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'duration' matches expectations - * * @psalm-assert array{duration: array} $arr */ private static function ensureDuration(array $arr): void @@ -60,14 +59,25 @@ private static function ensureDuration(array $arr): void } /** - * Check that the type of 'status' matches expectations - * - * @psalm-assert array{status: mixed} $arr + * @psalm-assert array{message: string|int|bool} $arr + */ + private static function ensureMessage(array $arr): void + { + if (array_key_exists('message', $arr) && is_array($arr['message'])) { + throw new SchemaViolationException('Property \'message\' was array'); + } + } + + /** + * @psalm-assert array{status: string|int|bool} $arr */ private static function ensureStatus(array $arr): void { if (!array_key_exists('status', $arr)) { throw new SchemaViolationException('Property \'status\' is required but was not found'); } + if (array_key_exists('status', $arr) && is_array($arr['status'])) { + throw new SchemaViolationException('Property \'status\' was array'); + } } } diff --git a/messages/php/src-generated/TestStepStarted.php b/messages/php/src-generated/TestStepStarted.php index ba3bf874694..e5d623578f4 100644 --- a/messages/php/src-generated/TestStepStarted.php +++ b/messages/php/src-generated/TestStepStarted.php @@ -46,32 +46,32 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'testCaseStartedId' matches expectations - * - * @psalm-assert array{testCaseStartedId: mixed} $arr + * @psalm-assert array{testCaseStartedId: string|int|bool} $arr */ private static function ensureTestCaseStartedId(array $arr): void { if (!array_key_exists('testCaseStartedId', $arr)) { throw new SchemaViolationException('Property \'testCaseStartedId\' is required but was not found'); } + if (array_key_exists('testCaseStartedId', $arr) && is_array($arr['testCaseStartedId'])) { + throw new SchemaViolationException('Property \'testCaseStartedId\' was array'); + } } /** - * Check that the type of 'testStepId' matches expectations - * - * @psalm-assert array{testStepId: mixed} $arr + * @psalm-assert array{testStepId: string|int|bool} $arr */ private static function ensureTestStepId(array $arr): void { if (!array_key_exists('testStepId', $arr)) { throw new SchemaViolationException('Property \'testStepId\' is required but was not found'); } + if (array_key_exists('testStepId', $arr) && is_array($arr['testStepId'])) { + throw new SchemaViolationException('Property \'testStepId\' was array'); + } } /** - * Check that the type of 'timestamp' matches expectations - * * @psalm-assert array{timestamp: array} $arr */ private static function ensureTimestamp(array $arr): void diff --git a/messages/php/src-generated/Timestamp.php b/messages/php/src-generated/Timestamp.php index af935b3c86c..7efc752a97b 100644 --- a/messages/php/src-generated/Timestamp.php +++ b/messages/php/src-generated/Timestamp.php @@ -53,26 +53,28 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'seconds' matches expectations - * - * @psalm-assert array{seconds: mixed} $arr + * @psalm-assert array{seconds: string|int|bool} $arr */ private static function ensureSeconds(array $arr): void { if (!array_key_exists('seconds', $arr)) { throw new SchemaViolationException('Property \'seconds\' is required but was not found'); } + if (array_key_exists('seconds', $arr) && is_array($arr['seconds'])) { + throw new SchemaViolationException('Property \'seconds\' was array'); + } } /** - * Check that the type of 'nanos' matches expectations - * - * @psalm-assert array{nanos: mixed} $arr + * @psalm-assert array{nanos: string|int|bool} $arr */ private static function ensureNanos(array $arr): void { if (!array_key_exists('nanos', $arr)) { throw new SchemaViolationException('Property \'nanos\' is required but was not found'); } + if (array_key_exists('nanos', $arr) && is_array($arr['nanos'])) { + throw new SchemaViolationException('Property \'nanos\' was array'); + } } } diff --git a/messages/php/src-generated/UndefinedParameterType.php b/messages/php/src-generated/UndefinedParameterType.php index 8435b1dd9ba..15d931691be 100644 --- a/messages/php/src-generated/UndefinedParameterType.php +++ b/messages/php/src-generated/UndefinedParameterType.php @@ -42,26 +42,28 @@ public static function fromArray(array $arr) : self } /** - * Check that the type of 'expression' matches expectations - * - * @psalm-assert array{expression: mixed} $arr + * @psalm-assert array{expression: string|int|bool} $arr */ private static function ensureExpression(array $arr): void { if (!array_key_exists('expression', $arr)) { throw new SchemaViolationException('Property \'expression\' is required but was not found'); } + if (array_key_exists('expression', $arr) && is_array($arr['expression'])) { + throw new SchemaViolationException('Property \'expression\' was array'); + } } /** - * Check that the type of 'name' matches expectations - * - * @psalm-assert array{name: mixed} $arr + * @psalm-assert array{name: string|int|bool} $arr */ private static function ensureName(array $arr): void { if (!array_key_exists('name', $arr)) { throw new SchemaViolationException('Property \'name\' is required but was not found'); } + if (array_key_exists('name', $arr) && is_array($arr['name'])) { + throw new SchemaViolationException('Property \'name\' was array'); + } } } diff --git a/messages/php/src/JsonEncodingTrait.php b/messages/php/src/JsonEncodingTrait.php index d6e9e27e8c0..30c2695f1b7 100644 --- a/messages/php/src/JsonEncodingTrait.php +++ b/messages/php/src/JsonEncodingTrait.php @@ -2,12 +2,18 @@ namespace Cucumber\Messages; +use JsonSerializable; + /** * @internal + * + * @psalm-require-implements JsonSerializable */ trait JsonEncodingTrait { /** + * Creates this type of message from an appropriate JSON string + * * @throws DecodingException */ public static function fromJson(string $json) : self @@ -26,6 +32,9 @@ public static function fromJson(string $json) : self return self::fromArray($data); } + /** + * Serialise the message into a JSON string + */ public function asJson() : string { return json_encode($this, JSON_THROW_ON_ERROR); From 83f6a85a7c24cfb270a2a374b32f90e77032c259 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Wed, 26 Jan 2022 22:28:52 +0000 Subject: [PATCH 14/37] Better formatting in README --- messages/php/README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/messages/php/README.md b/messages/php/README.md index 953e4b81d82..a30463f8667 100644 --- a/messages/php/README.md +++ b/messages/php/README.md @@ -17,6 +17,8 @@ composer require cucumber/messages ## Usage +### Envelopes + All cucumber messages are contained in an Envelope object. You can construct an Envelope from a JSON string: @@ -33,30 +35,27 @@ catch (DecodingException $e) { } ``` -Cucumber Messages are serialised as Newline Delimited JSON (NDJSON). +### Handling Streams + +Cucumber Messages are streamed as Newline Delimited JSON (NDJSON). -You can use the NdJsonStreamReader to obtain a Generator: +You can use the `NdJsonStreamReader` to obtain a Generator. It's important to remember that any decoding errors will +be thrown as the Envelope is created during the loop. ```php -use Cucumber\Messages\DecodingException; -use Cucumber\Messages\Envelope; use Cucumber\Messages\Streams\NdJsonStreamReader; $fh = fopen('messages.ndjson', 'r'); $reader = new NdJsonStreamReader($fh); -/** @var Generator $envelopes */ $envelopes = $reader->envelopes(); try { foreach ($envelopes as $envelope) { - // process $envelope + // $envelope is an Envelope } } catch (DecodingException $e) { - // handle the error -} -finally { - fclose($fh); + // handle the error here } ``` From d6db891b91fc67bf066bebc1f780c82cebc39643 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Thu, 27 Jan 2022 14:32:36 +0000 Subject: [PATCH 15/37] Neater class hierarchy --- messages/jsonschema/scripts/templates/php.php.erb | 2 ++ messages/php/README.md | 2 +- messages/php/src-generated/Attachment.php | 2 ++ messages/php/src-generated/Background.php | 2 ++ messages/php/src-generated/Ci.php | 2 ++ messages/php/src-generated/Comment.php | 2 ++ messages/php/src-generated/DataTable.php | 2 ++ messages/php/src-generated/DocString.php | 2 ++ messages/php/src-generated/Duration.php | 2 ++ messages/php/src-generated/Envelope.php | 2 ++ messages/php/src-generated/Examples.php | 2 ++ messages/php/src-generated/Feature.php | 2 ++ messages/php/src-generated/FeatureChild.php | 2 ++ messages/php/src-generated/GherkinDocument.php | 2 ++ messages/php/src-generated/Git.php | 2 ++ messages/php/src-generated/Group.php | 2 ++ messages/php/src-generated/Hook.php | 2 ++ messages/php/src-generated/JavaMethod.php | 2 ++ messages/php/src-generated/JavaStackTraceElement.php | 2 ++ messages/php/src-generated/Location.php | 2 ++ messages/php/src-generated/Meta.php | 2 ++ messages/php/src-generated/ParameterType.php | 2 ++ messages/php/src-generated/ParseError.php | 2 ++ messages/php/src-generated/Pickle.php | 2 ++ messages/php/src-generated/PickleDocString.php | 2 ++ messages/php/src-generated/PickleStep.php | 2 ++ messages/php/src-generated/PickleStepArgument.php | 2 ++ messages/php/src-generated/PickleTable.php | 2 ++ messages/php/src-generated/PickleTableCell.php | 2 ++ messages/php/src-generated/PickleTableRow.php | 2 ++ messages/php/src-generated/PickleTag.php | 2 ++ messages/php/src-generated/Product.php | 2 ++ messages/php/src-generated/Rule.php | 2 ++ messages/php/src-generated/RuleChild.php | 2 ++ messages/php/src-generated/Scenario.php | 2 ++ messages/php/src-generated/Source.php | 2 ++ messages/php/src-generated/SourceReference.php | 2 ++ messages/php/src-generated/Step.php | 2 ++ messages/php/src-generated/StepDefinition.php | 2 ++ messages/php/src-generated/StepDefinitionPattern.php | 2 ++ messages/php/src-generated/StepMatchArgument.php | 2 ++ messages/php/src-generated/StepMatchArgumentsList.php | 2 ++ messages/php/src-generated/TableCell.php | 2 ++ messages/php/src-generated/TableRow.php | 2 ++ messages/php/src-generated/Tag.php | 2 ++ messages/php/src-generated/TestCase.php | 2 ++ messages/php/src-generated/TestCaseFinished.php | 2 ++ messages/php/src-generated/TestCaseStarted.php | 2 ++ messages/php/src-generated/TestRunFinished.php | 2 ++ messages/php/src-generated/TestRunStarted.php | 2 ++ messages/php/src-generated/TestStep.php | 2 ++ messages/php/src-generated/TestStepFinished.php | 2 ++ messages/php/src-generated/TestStepResult.php | 2 ++ messages/php/src-generated/TestStepStarted.php | 2 ++ messages/php/src-generated/Timestamp.php | 2 ++ messages/php/src-generated/UndefinedParameterType.php | 2 ++ .../src/{ => DecodingException}/SchemaViolationException.php | 4 +++- .../src/{ => DecodingException}/UnknownDecodingException.php | 4 +++- messages/php/src/JsonEncodingTrait.php | 2 ++ messages/php/src/Streams/{ => NdJson}/NdJsonStreamReader.php | 4 +++- messages/php/src/Streams/{ => NdJson}/NdJsonStreamWriter.php | 4 +++- messages/php/tests/AcceptanceTest.php | 4 ++-- 62 files changed, 127 insertions(+), 7 deletions(-) rename messages/php/src/{ => DecodingException}/SchemaViolationException.php (65%) rename messages/php/src/{ => DecodingException}/UnknownDecodingException.php (65%) rename messages/php/src/Streams/{ => NdJson}/NdJsonStreamReader.php (74%) rename messages/php/src/Streams/{ => NdJson}/NdJsonStreamWriter.php (74%) diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index fa7595e9020..7f0fc7c723b 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + <%- @schemas.sort.each do |key, schema| -%> // CLASS_START <%= class_name(key) %>.php /** diff --git a/messages/php/README.md b/messages/php/README.md index a30463f8667..60e47b8f4c9 100644 --- a/messages/php/README.md +++ b/messages/php/README.md @@ -43,7 +43,7 @@ You can use the `NdJsonStreamReader` to obtain a Generator. It's important to re be thrown as the Envelope is created during the loop. ```php -use Cucumber\Messages\Streams\NdJsonStreamReader; +use Cucumber\Messages\Streams\NdJson\NdJsonStreamReader; $fh = fopen('messages.ndjson', 'r'); $reader = new NdJsonStreamReader($fh); diff --git a/messages/php/src-generated/Attachment.php b/messages/php/src-generated/Attachment.php index ecbde3b66a6..8fc632025a8 100644 --- a/messages/php/src-generated/Attachment.php +++ b/messages/php/src-generated/Attachment.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Attachment message in Cucumber's message protocol diff --git a/messages/php/src-generated/Background.php b/messages/php/src-generated/Background.php index c7204b0231c..961b65d632a 100644 --- a/messages/php/src-generated/Background.php +++ b/messages/php/src-generated/Background.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Background message in Cucumber's message protocol diff --git a/messages/php/src-generated/Ci.php b/messages/php/src-generated/Ci.php index cf7d7bb35fc..de818b18da6 100644 --- a/messages/php/src-generated/Ci.php +++ b/messages/php/src-generated/Ci.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Ci message in Cucumber's message protocol diff --git a/messages/php/src-generated/Comment.php b/messages/php/src-generated/Comment.php index ca0fe505dd8..a2dd65f1b96 100644 --- a/messages/php/src-generated/Comment.php +++ b/messages/php/src-generated/Comment.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Comment message in Cucumber's message protocol diff --git a/messages/php/src-generated/DataTable.php b/messages/php/src-generated/DataTable.php index 989bbad8549..a7017c9c383 100644 --- a/messages/php/src-generated/DataTable.php +++ b/messages/php/src-generated/DataTable.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the DataTable message in Cucumber's message protocol diff --git a/messages/php/src-generated/DocString.php b/messages/php/src-generated/DocString.php index 551f15502bb..0d79cc607c1 100644 --- a/messages/php/src-generated/DocString.php +++ b/messages/php/src-generated/DocString.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the DocString message in Cucumber's message protocol diff --git a/messages/php/src-generated/Duration.php b/messages/php/src-generated/Duration.php index fda9bc06da4..ba2b1ad9c89 100644 --- a/messages/php/src-generated/Duration.php +++ b/messages/php/src-generated/Duration.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Duration message in Cucumber's message protocol diff --git a/messages/php/src-generated/Envelope.php b/messages/php/src-generated/Envelope.php index 9e7b57d819b..64e3e91baaa 100644 --- a/messages/php/src-generated/Envelope.php +++ b/messages/php/src-generated/Envelope.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Envelope message in Cucumber's message protocol diff --git a/messages/php/src-generated/Examples.php b/messages/php/src-generated/Examples.php index e6863b9dc89..3eb3402ec56 100644 --- a/messages/php/src-generated/Examples.php +++ b/messages/php/src-generated/Examples.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Examples message in Cucumber's message protocol diff --git a/messages/php/src-generated/Feature.php b/messages/php/src-generated/Feature.php index 65eb3d67512..63c0477398b 100644 --- a/messages/php/src-generated/Feature.php +++ b/messages/php/src-generated/Feature.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Feature message in Cucumber's message protocol diff --git a/messages/php/src-generated/FeatureChild.php b/messages/php/src-generated/FeatureChild.php index d320e6d6704..1be99201240 100644 --- a/messages/php/src-generated/FeatureChild.php +++ b/messages/php/src-generated/FeatureChild.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the FeatureChild message in Cucumber's message protocol diff --git a/messages/php/src-generated/GherkinDocument.php b/messages/php/src-generated/GherkinDocument.php index 1701680f1ae..be26200c00e 100644 --- a/messages/php/src-generated/GherkinDocument.php +++ b/messages/php/src-generated/GherkinDocument.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the GherkinDocument message in Cucumber's message protocol diff --git a/messages/php/src-generated/Git.php b/messages/php/src-generated/Git.php index 9d81d0dd22f..4c3a57ddf86 100644 --- a/messages/php/src-generated/Git.php +++ b/messages/php/src-generated/Git.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Git message in Cucumber's message protocol diff --git a/messages/php/src-generated/Group.php b/messages/php/src-generated/Group.php index b376df4ce23..fa39c8b7d94 100644 --- a/messages/php/src-generated/Group.php +++ b/messages/php/src-generated/Group.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Group message in Cucumber's message protocol diff --git a/messages/php/src-generated/Hook.php b/messages/php/src-generated/Hook.php index 1193ee9dfe9..0314fb0f03f 100644 --- a/messages/php/src-generated/Hook.php +++ b/messages/php/src-generated/Hook.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Hook message in Cucumber's message protocol diff --git a/messages/php/src-generated/JavaMethod.php b/messages/php/src-generated/JavaMethod.php index cff65a84253..346ded7c136 100644 --- a/messages/php/src-generated/JavaMethod.php +++ b/messages/php/src-generated/JavaMethod.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the JavaMethod message in Cucumber's message protocol diff --git a/messages/php/src-generated/JavaStackTraceElement.php b/messages/php/src-generated/JavaStackTraceElement.php index 06632b72dac..79fee26b5e8 100644 --- a/messages/php/src-generated/JavaStackTraceElement.php +++ b/messages/php/src-generated/JavaStackTraceElement.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the JavaStackTraceElement message in Cucumber's message protocol diff --git a/messages/php/src-generated/Location.php b/messages/php/src-generated/Location.php index 34b83233943..065766a3e09 100644 --- a/messages/php/src-generated/Location.php +++ b/messages/php/src-generated/Location.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Location message in Cucumber's message protocol diff --git a/messages/php/src-generated/Meta.php b/messages/php/src-generated/Meta.php index 0b39cb31bdd..0e0fa37be26 100644 --- a/messages/php/src-generated/Meta.php +++ b/messages/php/src-generated/Meta.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Meta message in Cucumber's message protocol diff --git a/messages/php/src-generated/ParameterType.php b/messages/php/src-generated/ParameterType.php index 1e60af6757d..daae87987aa 100644 --- a/messages/php/src-generated/ParameterType.php +++ b/messages/php/src-generated/ParameterType.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the ParameterType message in Cucumber's message protocol diff --git a/messages/php/src-generated/ParseError.php b/messages/php/src-generated/ParseError.php index abc09393ec9..359f658def6 100644 --- a/messages/php/src-generated/ParseError.php +++ b/messages/php/src-generated/ParseError.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the ParseError message in Cucumber's message protocol diff --git a/messages/php/src-generated/Pickle.php b/messages/php/src-generated/Pickle.php index a87487d5ddb..1b6455a349a 100644 --- a/messages/php/src-generated/Pickle.php +++ b/messages/php/src-generated/Pickle.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Pickle message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleDocString.php b/messages/php/src-generated/PickleDocString.php index f032c3f1eb1..996a863f2ac 100644 --- a/messages/php/src-generated/PickleDocString.php +++ b/messages/php/src-generated/PickleDocString.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleDocString message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleStep.php b/messages/php/src-generated/PickleStep.php index 8793ccf3ad3..c2ccc0940f2 100644 --- a/messages/php/src-generated/PickleStep.php +++ b/messages/php/src-generated/PickleStep.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleStep message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleStepArgument.php b/messages/php/src-generated/PickleStepArgument.php index f27a96f2223..7bc6fe425d2 100644 --- a/messages/php/src-generated/PickleStepArgument.php +++ b/messages/php/src-generated/PickleStepArgument.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleStepArgument message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleTable.php b/messages/php/src-generated/PickleTable.php index 2aa7afe38d6..a3304cbbcde 100644 --- a/messages/php/src-generated/PickleTable.php +++ b/messages/php/src-generated/PickleTable.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleTable message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleTableCell.php b/messages/php/src-generated/PickleTableCell.php index f894f3fdb9d..be4da955628 100644 --- a/messages/php/src-generated/PickleTableCell.php +++ b/messages/php/src-generated/PickleTableCell.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleTableCell message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleTableRow.php b/messages/php/src-generated/PickleTableRow.php index 8a1e7c5b196..dac88249a23 100644 --- a/messages/php/src-generated/PickleTableRow.php +++ b/messages/php/src-generated/PickleTableRow.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleTableRow message in Cucumber's message protocol diff --git a/messages/php/src-generated/PickleTag.php b/messages/php/src-generated/PickleTag.php index 8fc335d9ad5..325cdf353f9 100644 --- a/messages/php/src-generated/PickleTag.php +++ b/messages/php/src-generated/PickleTag.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the PickleTag message in Cucumber's message protocol diff --git a/messages/php/src-generated/Product.php b/messages/php/src-generated/Product.php index a908eb202c7..57dc330fe59 100644 --- a/messages/php/src-generated/Product.php +++ b/messages/php/src-generated/Product.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Product message in Cucumber's message protocol diff --git a/messages/php/src-generated/Rule.php b/messages/php/src-generated/Rule.php index f41617ae713..6e74241325d 100644 --- a/messages/php/src-generated/Rule.php +++ b/messages/php/src-generated/Rule.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Rule message in Cucumber's message protocol diff --git a/messages/php/src-generated/RuleChild.php b/messages/php/src-generated/RuleChild.php index f35f2287ce4..9609e9b5f12 100644 --- a/messages/php/src-generated/RuleChild.php +++ b/messages/php/src-generated/RuleChild.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the RuleChild message in Cucumber's message protocol diff --git a/messages/php/src-generated/Scenario.php b/messages/php/src-generated/Scenario.php index 957036011aa..968e0cac537 100644 --- a/messages/php/src-generated/Scenario.php +++ b/messages/php/src-generated/Scenario.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Scenario message in Cucumber's message protocol diff --git a/messages/php/src-generated/Source.php b/messages/php/src-generated/Source.php index 4218e967c8a..8f2b7f87dd9 100644 --- a/messages/php/src-generated/Source.php +++ b/messages/php/src-generated/Source.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Source message in Cucumber's message protocol diff --git a/messages/php/src-generated/SourceReference.php b/messages/php/src-generated/SourceReference.php index 31637d777ae..7a0bf9f0c45 100644 --- a/messages/php/src-generated/SourceReference.php +++ b/messages/php/src-generated/SourceReference.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the SourceReference message in Cucumber's message protocol diff --git a/messages/php/src-generated/Step.php b/messages/php/src-generated/Step.php index a3c4ae42c9c..0f8586d51f7 100644 --- a/messages/php/src-generated/Step.php +++ b/messages/php/src-generated/Step.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Step message in Cucumber's message protocol diff --git a/messages/php/src-generated/StepDefinition.php b/messages/php/src-generated/StepDefinition.php index 681a07ef747..78e0bb26313 100644 --- a/messages/php/src-generated/StepDefinition.php +++ b/messages/php/src-generated/StepDefinition.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the StepDefinition message in Cucumber's message protocol diff --git a/messages/php/src-generated/StepDefinitionPattern.php b/messages/php/src-generated/StepDefinitionPattern.php index dfe0c6489a6..05f35c81600 100644 --- a/messages/php/src-generated/StepDefinitionPattern.php +++ b/messages/php/src-generated/StepDefinitionPattern.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the StepDefinitionPattern message in Cucumber's message protocol diff --git a/messages/php/src-generated/StepMatchArgument.php b/messages/php/src-generated/StepMatchArgument.php index 08738dfa8d7..44269f42c91 100644 --- a/messages/php/src-generated/StepMatchArgument.php +++ b/messages/php/src-generated/StepMatchArgument.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the StepMatchArgument message in Cucumber's message protocol diff --git a/messages/php/src-generated/StepMatchArgumentsList.php b/messages/php/src-generated/StepMatchArgumentsList.php index 9bcced16101..2e471ca06dc 100644 --- a/messages/php/src-generated/StepMatchArgumentsList.php +++ b/messages/php/src-generated/StepMatchArgumentsList.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the StepMatchArgumentsList message in Cucumber's message protocol diff --git a/messages/php/src-generated/TableCell.php b/messages/php/src-generated/TableCell.php index 6374763dd64..24d548106d4 100644 --- a/messages/php/src-generated/TableCell.php +++ b/messages/php/src-generated/TableCell.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TableCell message in Cucumber's message protocol diff --git a/messages/php/src-generated/TableRow.php b/messages/php/src-generated/TableRow.php index c539632ebc4..a5c44f7f979 100644 --- a/messages/php/src-generated/TableRow.php +++ b/messages/php/src-generated/TableRow.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TableRow message in Cucumber's message protocol diff --git a/messages/php/src-generated/Tag.php b/messages/php/src-generated/Tag.php index d2960c6032c..953e0f8c1ff 100644 --- a/messages/php/src-generated/Tag.php +++ b/messages/php/src-generated/Tag.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Tag message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestCase.php b/messages/php/src-generated/TestCase.php index 2691db7e690..56ca12e6bc1 100644 --- a/messages/php/src-generated/TestCase.php +++ b/messages/php/src-generated/TestCase.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestCase message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestCaseFinished.php b/messages/php/src-generated/TestCaseFinished.php index 7c0a3ba0814..0969c0ec84a 100644 --- a/messages/php/src-generated/TestCaseFinished.php +++ b/messages/php/src-generated/TestCaseFinished.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestCaseFinished message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestCaseStarted.php b/messages/php/src-generated/TestCaseStarted.php index e243fa5c82c..7e1564b0339 100644 --- a/messages/php/src-generated/TestCaseStarted.php +++ b/messages/php/src-generated/TestCaseStarted.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestCaseStarted message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestRunFinished.php b/messages/php/src-generated/TestRunFinished.php index 47a83a3b398..c3acfccbdba 100644 --- a/messages/php/src-generated/TestRunFinished.php +++ b/messages/php/src-generated/TestRunFinished.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestRunFinished message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestRunStarted.php b/messages/php/src-generated/TestRunStarted.php index 17fb6dcdfe0..d9d566dca0c 100644 --- a/messages/php/src-generated/TestRunStarted.php +++ b/messages/php/src-generated/TestRunStarted.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestRunStarted message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestStep.php b/messages/php/src-generated/TestStep.php index eb900b23271..d984808319f 100644 --- a/messages/php/src-generated/TestStep.php +++ b/messages/php/src-generated/TestStep.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestStep message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestStepFinished.php b/messages/php/src-generated/TestStepFinished.php index 5948488c046..b462f8a64a7 100644 --- a/messages/php/src-generated/TestStepFinished.php +++ b/messages/php/src-generated/TestStepFinished.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestStepFinished message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestStepResult.php b/messages/php/src-generated/TestStepResult.php index 3f97e29dcd3..07859e55913 100644 --- a/messages/php/src-generated/TestStepResult.php +++ b/messages/php/src-generated/TestStepResult.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestStepResult message in Cucumber's message protocol diff --git a/messages/php/src-generated/TestStepStarted.php b/messages/php/src-generated/TestStepStarted.php index e5d623578f4..d98c571837f 100644 --- a/messages/php/src-generated/TestStepStarted.php +++ b/messages/php/src-generated/TestStepStarted.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the TestStepStarted message in Cucumber's message protocol diff --git a/messages/php/src-generated/Timestamp.php b/messages/php/src-generated/Timestamp.php index 7efc752a97b..ddce365922a 100644 --- a/messages/php/src-generated/Timestamp.php +++ b/messages/php/src-generated/Timestamp.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the Timestamp message in Cucumber's message protocol diff --git a/messages/php/src-generated/UndefinedParameterType.php b/messages/php/src-generated/UndefinedParameterType.php index 15d931691be..52bc009c143 100644 --- a/messages/php/src-generated/UndefinedParameterType.php +++ b/messages/php/src-generated/UndefinedParameterType.php @@ -7,6 +7,8 @@ namespace Cucumber\Messages; use \JsonSerializable; +use Cucumber\Messages\DecodingException\SchemaViolationException; + /** * Represents the UndefinedParameterType message in Cucumber's message protocol diff --git a/messages/php/src/SchemaViolationException.php b/messages/php/src/DecodingException/SchemaViolationException.php similarity index 65% rename from messages/php/src/SchemaViolationException.php rename to messages/php/src/DecodingException/SchemaViolationException.php index c31595fd7f3..05bf476f4a6 100644 --- a/messages/php/src/SchemaViolationException.php +++ b/messages/php/src/DecodingException/SchemaViolationException.php @@ -1,6 +1,8 @@ Date: Thu, 27 Jan 2022 15:07:12 +0000 Subject: [PATCH 16/37] Handle exceptions thrown by Message objects when deserialising --- ...ception.php => MalformedJsonException.php} | 4 +- .../UnexpectedDecodingException.php | 12 ++++ messages/php/src/JsonEncodingTrait.php | 14 +++-- messages/php/tests/JsonDecodingTraitTest.php | 61 +++++++++++++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) rename messages/php/src/DecodingException/{UnknownDecodingException.php => MalformedJsonException.php} (51%) create mode 100644 messages/php/src/DecodingException/UnexpectedDecodingException.php create mode 100644 messages/php/tests/JsonDecodingTraitTest.php diff --git a/messages/php/src/DecodingException/UnknownDecodingException.php b/messages/php/src/DecodingException/MalformedJsonException.php similarity index 51% rename from messages/php/src/DecodingException/UnknownDecodingException.php rename to messages/php/src/DecodingException/MalformedJsonException.php index 7178922ff65..9db64aa74a6 100644 --- a/messages/php/src/DecodingException/UnknownDecodingException.php +++ b/messages/php/src/DecodingException/MalformedJsonException.php @@ -5,8 +5,8 @@ use Cucumber\Messages\DecodingException; /** - * Something went wrong when decoding, but it is not clear what + * JSON decoding threw an exception */ -final class UnknownDecodingException extends DecodingException +final class MalformedJsonException extends DecodingException { } diff --git a/messages/php/src/DecodingException/UnexpectedDecodingException.php b/messages/php/src/DecodingException/UnexpectedDecodingException.php new file mode 100644 index 00000000000..ab6c94fb17d --- /dev/null +++ b/messages/php/src/DecodingException/UnexpectedDecodingException.php @@ -0,0 +1,12 @@ +getMessage().'"', previous: $t); + } } /** diff --git a/messages/php/tests/JsonDecodingTraitTest.php b/messages/php/tests/JsonDecodingTraitTest.php new file mode 100644 index 00000000000..d1d0924985b --- /dev/null +++ b/messages/php/tests/JsonDecodingTraitTest.php @@ -0,0 +1,61 @@ +expectException(MalformedJsonException::class); + + JsonDecodingTraitTestImpl::fromJson('{'); + } + + public function testItThrowsIfGivenValidJsonThatIsNotArray(): void + { + $this->expectException(SchemaViolationException::class); + + JsonDecodingTraitTestImpl::fromJson('true'); + } + + public function testItRethrowsUnexpectedErrors(): void + { + $this->expectException(UnexpectedDecodingException::class); + + // 'oops' is hardcoded in BrokenJsonDecodingTraitTestImpl + $this->expectExceptionMessage('Unexpected decoding error: "oops"'); + + BrokenJsonDecodingTraitTestImpl::fromJson('[]'); + } +} + +class JsonDecodingTraitTestImpl implements JsonSerializable +{ + use JsonEncodingTrait; + + public static function fromArray(array $arr): self + { + return new self(); + } +} + +class BrokenJsonDecodingTraitTestImpl implements JsonSerializable +{ + use JsonEncodingTrait; + + public static function fromArray(array $arr): self + { + throw new RuntimeException('oops'); + } +} From 32d078e453869d76ab5997480de6a366d649a5de Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Thu, 27 Jan 2022 15:45:13 +0000 Subject: [PATCH 17/37] Fix psalm-assert for optional scalar keys --- messages/jsonschema/scripts/templates/php.php.erb | 2 +- messages/php/src-generated/Attachment.php | 8 ++++---- messages/php/src-generated/Ci.php | 4 ++-- messages/php/src-generated/DocString.php | 2 +- messages/php/src-generated/GherkinDocument.php | 2 +- messages/php/src-generated/Git.php | 4 ++-- messages/php/src-generated/Group.php | 4 ++-- messages/php/src-generated/Hook.php | 2 +- messages/php/src-generated/Location.php | 2 +- messages/php/src-generated/PickleDocString.php | 2 +- messages/php/src-generated/Product.php | 2 +- messages/php/src-generated/SourceReference.php | 2 +- messages/php/src-generated/StepMatchArgument.php | 2 +- messages/php/src-generated/TestRunFinished.php | 2 +- messages/php/src-generated/TestStep.php | 4 ++-- messages/php/src-generated/TestStepResult.php | 2 +- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index 7f0fc7c723b..903f428b7e3 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -60,7 +60,7 @@ final class <%= class_name(key) %> implements JsonSerializable /** <%- if is_scalar(property) -%> - * @psalm-assert array{<%= property_name %>: string|int|bool} $arr + * @psalm-assert array{<%= property_name %><%- if (is_nullable(property_name, schema)) -%>?<%- end -%>: string|int|bool} $arr <%- else -%> * @psalm-assert array{<%= property_name %><%- if (is_nullable(property_name, schema)) -%>?<%- end -%>: array} $arr <%- end -%> diff --git a/messages/php/src-generated/Attachment.php b/messages/php/src-generated/Attachment.php index 8fc632025a8..7ff46980c4c 100644 --- a/messages/php/src-generated/Attachment.php +++ b/messages/php/src-generated/Attachment.php @@ -141,7 +141,7 @@ private static function ensureContentEncoding(array $arr): void } /** - * @psalm-assert array{fileName: string|int|bool} $arr + * @psalm-assert array{fileName?: string|int|bool} $arr */ private static function ensureFileName(array $arr): void { @@ -174,7 +174,7 @@ private static function ensureSource(array $arr): void } /** - * @psalm-assert array{testCaseStartedId: string|int|bool} $arr + * @psalm-assert array{testCaseStartedId?: string|int|bool} $arr */ private static function ensureTestCaseStartedId(array $arr): void { @@ -184,7 +184,7 @@ private static function ensureTestCaseStartedId(array $arr): void } /** - * @psalm-assert array{testStepId: string|int|bool} $arr + * @psalm-assert array{testStepId?: string|int|bool} $arr */ private static function ensureTestStepId(array $arr): void { @@ -194,7 +194,7 @@ private static function ensureTestStepId(array $arr): void } /** - * @psalm-assert array{url: string|int|bool} $arr + * @psalm-assert array{url?: string|int|bool} $arr */ private static function ensureUrl(array $arr): void { diff --git a/messages/php/src-generated/Ci.php b/messages/php/src-generated/Ci.php index de818b18da6..964859e85d3 100644 --- a/messages/php/src-generated/Ci.php +++ b/messages/php/src-generated/Ci.php @@ -74,7 +74,7 @@ private static function ensureName(array $arr): void } /** - * @psalm-assert array{url: string|int|bool} $arr + * @psalm-assert array{url?: string|int|bool} $arr */ private static function ensureUrl(array $arr): void { @@ -84,7 +84,7 @@ private static function ensureUrl(array $arr): void } /** - * @psalm-assert array{buildNumber: string|int|bool} $arr + * @psalm-assert array{buildNumber?: string|int|bool} $arr */ private static function ensureBuildNumber(array $arr): void { diff --git a/messages/php/src-generated/DocString.php b/messages/php/src-generated/DocString.php index 0d79cc607c1..af98df16b04 100644 --- a/messages/php/src-generated/DocString.php +++ b/messages/php/src-generated/DocString.php @@ -65,7 +65,7 @@ private static function ensureLocation(array $arr): void } /** - * @psalm-assert array{mediaType: string|int|bool} $arr + * @psalm-assert array{mediaType?: string|int|bool} $arr */ private static function ensureMediaType(array $arr): void { diff --git a/messages/php/src-generated/GherkinDocument.php b/messages/php/src-generated/GherkinDocument.php index be26200c00e..db589c09b73 100644 --- a/messages/php/src-generated/GherkinDocument.php +++ b/messages/php/src-generated/GherkinDocument.php @@ -61,7 +61,7 @@ public static function fromArray(array $arr) : self } /** - * @psalm-assert array{uri: string|int|bool} $arr + * @psalm-assert array{uri?: string|int|bool} $arr */ private static function ensureUri(array $arr): void { diff --git a/messages/php/src-generated/Git.php b/messages/php/src-generated/Git.php index 4c3a57ddf86..d65cb3b2018 100644 --- a/messages/php/src-generated/Git.php +++ b/messages/php/src-generated/Git.php @@ -79,7 +79,7 @@ private static function ensureRevision(array $arr): void } /** - * @psalm-assert array{branch: string|int|bool} $arr + * @psalm-assert array{branch?: string|int|bool} $arr */ private static function ensureBranch(array $arr): void { @@ -89,7 +89,7 @@ private static function ensureBranch(array $arr): void } /** - * @psalm-assert array{tag: string|int|bool} $arr + * @psalm-assert array{tag?: string|int|bool} $arr */ private static function ensureTag(array $arr): void { diff --git a/messages/php/src-generated/Group.php b/messages/php/src-generated/Group.php index fa39c8b7d94..80a7d6fce5c 100644 --- a/messages/php/src-generated/Group.php +++ b/messages/php/src-generated/Group.php @@ -64,7 +64,7 @@ private static function ensureChildren(array $arr): void } /** - * @psalm-assert array{start: string|int|bool} $arr + * @psalm-assert array{start?: string|int|bool} $arr */ private static function ensureStart(array $arr): void { @@ -74,7 +74,7 @@ private static function ensureStart(array $arr): void } /** - * @psalm-assert array{value: string|int|bool} $arr + * @psalm-assert array{value?: string|int|bool} $arr */ private static function ensureValue(array $arr): void { diff --git a/messages/php/src-generated/Hook.php b/messages/php/src-generated/Hook.php index 0314fb0f03f..e4927c6f87c 100644 --- a/messages/php/src-generated/Hook.php +++ b/messages/php/src-generated/Hook.php @@ -74,7 +74,7 @@ private static function ensureSourceReference(array $arr): void } /** - * @psalm-assert array{tagExpression: string|int|bool} $arr + * @psalm-assert array{tagExpression?: string|int|bool} $arr */ private static function ensureTagExpression(array $arr): void { diff --git a/messages/php/src-generated/Location.php b/messages/php/src-generated/Location.php index 065766a3e09..42a332e573a 100644 --- a/messages/php/src-generated/Location.php +++ b/messages/php/src-generated/Location.php @@ -57,7 +57,7 @@ private static function ensureLine(array $arr): void } /** - * @psalm-assert array{column: string|int|bool} $arr + * @psalm-assert array{column?: string|int|bool} $arr */ private static function ensureColumn(array $arr): void { diff --git a/messages/php/src-generated/PickleDocString.php b/messages/php/src-generated/PickleDocString.php index 996a863f2ac..a9f3a2cc161 100644 --- a/messages/php/src-generated/PickleDocString.php +++ b/messages/php/src-generated/PickleDocString.php @@ -44,7 +44,7 @@ public static function fromArray(array $arr) : self } /** - * @psalm-assert array{mediaType: string|int|bool} $arr + * @psalm-assert array{mediaType?: string|int|bool} $arr */ private static function ensureMediaType(array $arr): void { diff --git a/messages/php/src-generated/Product.php b/messages/php/src-generated/Product.php index 57dc330fe59..e6c5ded300d 100644 --- a/messages/php/src-generated/Product.php +++ b/messages/php/src-generated/Product.php @@ -63,7 +63,7 @@ private static function ensureName(array $arr): void } /** - * @psalm-assert array{version: string|int|bool} $arr + * @psalm-assert array{version?: string|int|bool} $arr */ private static function ensureVersion(array $arr): void { diff --git a/messages/php/src-generated/SourceReference.php b/messages/php/src-generated/SourceReference.php index 7a0bf9f0c45..41ff00d7432 100644 --- a/messages/php/src-generated/SourceReference.php +++ b/messages/php/src-generated/SourceReference.php @@ -53,7 +53,7 @@ public static function fromArray(array $arr) : self } /** - * @psalm-assert array{uri: string|int|bool} $arr + * @psalm-assert array{uri?: string|int|bool} $arr */ private static function ensureUri(array $arr): void { diff --git a/messages/php/src-generated/StepMatchArgument.php b/messages/php/src-generated/StepMatchArgument.php index 44269f42c91..9d8ad6c8bbc 100644 --- a/messages/php/src-generated/StepMatchArgument.php +++ b/messages/php/src-generated/StepMatchArgument.php @@ -66,7 +66,7 @@ private static function ensureGroup(array $arr): void } /** - * @psalm-assert array{parameterTypeName: string|int|bool} $arr + * @psalm-assert array{parameterTypeName?: string|int|bool} $arr */ private static function ensureParameterTypeName(array $arr): void { diff --git a/messages/php/src-generated/TestRunFinished.php b/messages/php/src-generated/TestRunFinished.php index c3acfccbdba..b5a57c92f1e 100644 --- a/messages/php/src-generated/TestRunFinished.php +++ b/messages/php/src-generated/TestRunFinished.php @@ -61,7 +61,7 @@ public static function fromArray(array $arr) : self } /** - * @psalm-assert array{message: string|int|bool} $arr + * @psalm-assert array{message?: string|int|bool} $arr */ private static function ensureMessage(array $arr): void { diff --git a/messages/php/src-generated/TestStep.php b/messages/php/src-generated/TestStep.php index d984808319f..f5133dd956b 100644 --- a/messages/php/src-generated/TestStep.php +++ b/messages/php/src-generated/TestStep.php @@ -73,7 +73,7 @@ public static function fromArray(array $arr) : self } /** - * @psalm-assert array{hookId: string|int|bool} $arr + * @psalm-assert array{hookId?: string|int|bool} $arr */ private static function ensureHookId(array $arr): void { @@ -96,7 +96,7 @@ private static function ensureId(array $arr): void } /** - * @psalm-assert array{pickleStepId: string|int|bool} $arr + * @psalm-assert array{pickleStepId?: string|int|bool} $arr */ private static function ensurePickleStepId(array $arr): void { diff --git a/messages/php/src-generated/TestStepResult.php b/messages/php/src-generated/TestStepResult.php index 07859e55913..2ba81633555 100644 --- a/messages/php/src-generated/TestStepResult.php +++ b/messages/php/src-generated/TestStepResult.php @@ -61,7 +61,7 @@ private static function ensureDuration(array $arr): void } /** - * @psalm-assert array{message: string|int|bool} $arr + * @psalm-assert array{message?: string|int|bool} $arr */ private static function ensureMessage(array $arr): void { From f7a5cdaa16d50c01b8df07ac0a72842976082bc2 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Thu, 27 Jan 2022 16:56:07 +0100 Subject: [PATCH 18/37] Add subrepo to messages/php --- messages/php/.subrepo | 1 + 1 file changed, 1 insertion(+) create mode 100644 messages/php/.subrepo diff --git a/messages/php/.subrepo b/messages/php/.subrepo new file mode 100644 index 00000000000..31d21b6c2a2 --- /dev/null +++ b/messages/php/.subrepo @@ -0,0 +1 @@ +cucumber/messages-php From 0689263de44bce88b117148153225012bd657d33 Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Thu, 27 Jan 2022 19:22:45 +0000 Subject: [PATCH 19/37] Allow Messages to be constructed --- messages/jsonschema/scripts/codegen.rb | 18 +++- .../jsonschema/scripts/templates/php.php.erb | 4 +- messages/php/README.md | 86 +++++++++++++++++-- messages/php/src-generated/Attachment.php | 18 ++-- messages/php/src-generated/Background.php | 14 +-- messages/php/src-generated/Ci.php | 10 +-- messages/php/src-generated/Comment.php | 6 +- messages/php/src-generated/DataTable.php | 6 +- messages/php/src-generated/DocString.php | 10 +-- messages/php/src-generated/Duration.php | 6 +- messages/php/src-generated/Envelope.php | 36 ++++---- messages/php/src-generated/Examples.php | 18 ++-- messages/php/src-generated/Feature.php | 16 ++-- messages/php/src-generated/FeatureChild.php | 8 +- .../php/src-generated/GherkinDocument.php | 8 +- messages/php/src-generated/Git.php | 10 +-- messages/php/src-generated/Group.php | 8 +- messages/php/src-generated/Hook.php | 8 +- messages/php/src-generated/JavaMethod.php | 8 +- .../src-generated/JavaStackTraceElement.php | 8 +- messages/php/src-generated/Location.php | 6 +- messages/php/src-generated/Meta.php | 14 +-- messages/php/src-generated/ParameterType.php | 12 +-- messages/php/src-generated/ParseError.php | 6 +- messages/php/src-generated/Pickle.php | 16 ++-- .../php/src-generated/PickleDocString.php | 6 +- messages/php/src-generated/PickleStep.php | 10 +-- .../php/src-generated/PickleStepArgument.php | 6 +- messages/php/src-generated/PickleTable.php | 4 +- .../php/src-generated/PickleTableCell.php | 4 +- messages/php/src-generated/PickleTableRow.php | 4 +- messages/php/src-generated/PickleTag.php | 6 +- messages/php/src-generated/Product.php | 6 +- messages/php/src-generated/Rule.php | 16 ++-- messages/php/src-generated/RuleChild.php | 6 +- messages/php/src-generated/Scenario.php | 18 ++-- messages/php/src-generated/Source.php | 8 +- .../php/src-generated/SourceReference.php | 10 +-- messages/php/src-generated/Step.php | 14 +-- messages/php/src-generated/StepDefinition.php | 8 +- .../src-generated/StepDefinitionPattern.php | 6 +- .../php/src-generated/StepMatchArgument.php | 6 +- .../src-generated/StepMatchArgumentsList.php | 4 +- messages/php/src-generated/TableCell.php | 6 +- messages/php/src-generated/TableRow.php | 8 +- messages/php/src-generated/Tag.php | 8 +- messages/php/src-generated/TestCase.php | 8 +- .../php/src-generated/TestCaseFinished.php | 8 +- .../php/src-generated/TestCaseStarted.php | 10 +-- .../php/src-generated/TestRunFinished.php | 8 +- messages/php/src-generated/TestRunStarted.php | 4 +- messages/php/src-generated/TestStep.php | 12 +-- .../php/src-generated/TestStepFinished.php | 10 +-- messages/php/src-generated/TestStepResult.php | 8 +- .../php/src-generated/TestStepStarted.php | 8 +- messages/php/src-generated/Timestamp.php | 6 +- .../src-generated/UndefinedParameterType.php | 6 +- messages/php/tests/EnvelopeTest.php | 36 ++++++++ 58 files changed, 386 insertions(+), 262 deletions(-) create mode 100644 messages/php/tests/EnvelopeTest.php diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 951042ea255..9763d2e7c17 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -51,7 +51,7 @@ def default_value(parent_type_name, property_name, property) elsif property['type'] == 'string' if property['enum'] enum_type_name = type_for(parent_type_name, property_name, property) - "#{enum_type_name}.#{enum_constant(property['enum'][0])}" + default_enum(enum_type_name, property) else "''" end @@ -67,6 +67,10 @@ def default_value(parent_type_name, property_name, property) end end + def default_enum(enum_type_name, property) + "#{enum_type_name}.#{enum_constant(property['enum'][0])}" + end + def enum_constant(value) value.gsub(/[\.\/\+]/, '_').upcase end @@ -378,6 +382,18 @@ def non_nullable_constructor_for(parent_type, property, property_name, schema, a end end end + + def default_value(class_name, property_name, property, schema) + if is_nullable(property_name, schema) + return 'null' + end + + super(class_name, property_name, property) + end + + def default_enum(enum_type_name, property) + "#{enum_type_name}::#{enum_constant(property['enum'][0])}" + end end diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index 903f428b7e3..e600d8b198b 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -20,7 +20,7 @@ final class <%= class_name(key) %> implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( <%- schema['properties'].each do |property_name, property| -%> <%- property_type = type_for(key, property_name, property) -%> @@ -34,7 +34,7 @@ final class <%= class_name(key) %> implements JsonSerializable <%- end -%> */ <%- end -%> - public readonly <%= is_nullable(property_name, schema) ? '?' : '' %><%= property_type %> $<%= property_name %>, + public readonly <%= is_nullable(property_name, schema) ? '?' : '' %><%= property_type %> $<%= property_name %> = <%= default_value(class_name(key), property_name, property, schema) -%>, <%- end -%> ){} diff --git a/messages/php/README.md b/messages/php/README.md index 60e47b8f4c9..a9df13d5f3a 100644 --- a/messages/php/README.md +++ b/messages/php/README.md @@ -17,9 +17,18 @@ composer require cucumber/messages ## Usage -### Envelopes +### Consuming messages -All cucumber messages are contained in an Envelope object. +Cucumber Messages are contained in a top-level Envelope object when serialised. Members are exposed via +public readonly properties. Because many properties are nullable, it may be convenient to use the [nullsafe +operator](https://www.php.net/releases/8.0/en.php#nullsafe-operator) to access them: + +```php +/** @var string|null $line */ +$line = $envelope->gherkinDocument?->feature?->keyword; +``` + +#### Decoding JSON strings You can construct an Envelope from a JSON string: @@ -35,27 +44,90 @@ catch (DecodingException $e) { } ``` -### Handling Streams +#### Handling NDJSON Streams Cucumber Messages are streamed as Newline Delimited JSON (NDJSON). -You can use the `NdJsonStreamReader` to obtain a Generator. It's important to remember that any decoding errors will -be thrown as the Envelope is created during the loop. +You can use the `NdJsonStreamReader` to obtain a Generator that produces Envelopes. It's important to remember that any +decoding errors will be thrown as the generator is consumed, not when it's returned. ```php +use Cucumber\Messages\DecodingException; use Cucumber\Messages\Streams\NdJson\NdJsonStreamReader; +use Cucumber\Messages\Envelope; $fh = fopen('messages.ndjson', 'r'); $reader = new NdJsonStreamReader($fh); +/** @var Generator $envelopes */ $envelopes = $reader->envelopes(); try { foreach ($envelopes as $envelope) { - // $envelope is an Envelope + /** @var Envelope $envelope */ + // process the message } } catch (DecodingException $e) { - // handle the error here + // handle any errors here } ``` + +### Producing messages + +All arguments of a Cucumber Message are optional, but any non-nullable fields will have default values. + +Because Messages tend to have a large number of arguments, it's recommended to use named fields to construct them: + +```php +use Cucumber\Messages\Envelope; +use Cucumber\Messages\TestCaseFinished; +use Cucumber\Messages\Timestamp; + +$envelope = new Envelope( + testCaseFinished: new TestCaseFinished( + timestamp: new Timestamp( + seconds: 100 + ) + ) +); +``` + +#### Encoding a JSON string + +An Envelope can be encoded as a JSON string: + +```php +$json = $envelope->asJson(); +``` + +Do _not_ `json_encode()` the object externally, as the correct encoding options may not be set. + +#### Producing JSON streams + +Cucumber Messages are streamed as Newline Delimited JSON (NDJSON). + +You can use the NdJsonStreamReader to write the contents of list of Envelopes to a stream. + +```php +use Cucumber\Messages\Streams\NdJson\NdJsonStreamReader; + +$fh = fopen('php://stdout', 'w'); + +$writer = new NdJsonStreamWriter($fh) + +// write a fixed array of envelopes +$envArray = [ + new Envelope('gherkinDocument': new GherkinDocument()), + new Envelope('gherkinDocument': new GherkinDocument()), +]; +$writer->write($envArray); + +// write lazily-evaluated envelopes +$envGenerator = (function() { + while ($envelope = Database::fetchNextEnvelope()) { + yield $envelope; + } +})(); +$writer->write($envGenerator); +``` diff --git a/messages/php/src-generated/Attachment.php b/messages/php/src-generated/Attachment.php index 7ff46980c4c..bb31261845f 100644 --- a/messages/php/src-generated/Attachment.php +++ b/messages/php/src-generated/Attachment.php @@ -29,14 +29,14 @@ final class Attachment implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The body of the attachment. If `contentEncoding` is `IDENTITY`, the attachment * is simply the string. If it's `BASE64`, the string should be Base64 decoded to * obtain the attachment. */ - public readonly string $body, + public readonly string $body = '', /** * Whether to interpret `body` "as-is" (IDENTITY) or if it needs to be Base64-decoded (BASE64). @@ -48,12 +48,12 @@ private function __construct( * - byte array => BASE64 * - stream => BASE64 */ - public readonly Attachment\ContentEncoding $contentEncoding, + public readonly Attachment\ContentEncoding $contentEncoding = Attachment\ContentEncoding::IDENTITY, /** * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) */ - public readonly ?string $fileName, + public readonly ?string $fileName = null, /** * The media type of the data. This can be any valid @@ -61,13 +61,13 @@ private function __construct( * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` * and `text/x.cucumber.stacktrace+plain` */ - public readonly string $mediaType, + public readonly string $mediaType = '', - public readonly ?Source $source, + public readonly ?Source $source = null, - public readonly ?string $testCaseStartedId, + public readonly ?string $testCaseStartedId = null, - public readonly ?string $testStepId, + public readonly ?string $testStepId = null, /** * A URL where the attachment can be retrieved. This field should not be set by Cucumber. @@ -82,7 +82,7 @@ private function __construct( * reduce bandwidth of message consumers. It also makes it easier to process and download attachments * separately from reports. */ - public readonly ?string $url, + public readonly ?string $url = null, ){} diff --git a/messages/php/src-generated/Background.php b/messages/php/src-generated/Background.php index 961b65d632a..296ee80fd5c 100644 --- a/messages/php/src-generated/Background.php +++ b/messages/php/src-generated/Background.php @@ -19,25 +19,25 @@ final class Background implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the `Background` keyword */ - public readonly Location $location, + public readonly Location $location = new Location(), - public readonly string $keyword, + public readonly string $keyword = '', - public readonly string $name, + public readonly string $name = '', - public readonly string $description, + public readonly string $description = '', /** * @param list $steps */ - public readonly array $steps, + public readonly array $steps = [], - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/Ci.php b/messages/php/src-generated/Ci.php index 964859e85d3..097ec7e4731 100644 --- a/messages/php/src-generated/Ci.php +++ b/messages/php/src-generated/Ci.php @@ -19,24 +19,24 @@ final class Ci implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * Name of the CI product, e.g. "Jenkins", "CircleCI" etc. */ - public readonly string $name, + public readonly string $name = '', /** * Link to the build */ - public readonly ?string $url, + public readonly ?string $url = null, /** * The build number. Some CI servers use non-numeric build numbers, which is why this is a string */ - public readonly ?string $buildNumber, + public readonly ?string $buildNumber = null, - public readonly ?Git $git, + public readonly ?Git $git = null, ){} diff --git a/messages/php/src-generated/Comment.php b/messages/php/src-generated/Comment.php index a2dd65f1b96..2e913bd6d95 100644 --- a/messages/php/src-generated/Comment.php +++ b/messages/php/src-generated/Comment.php @@ -19,17 +19,17 @@ final class Comment implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the comment */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * The text of the comment */ - public readonly string $text, + public readonly string $text = '', ){} diff --git a/messages/php/src-generated/DataTable.php b/messages/php/src-generated/DataTable.php index a7017c9c383..a1897a6993b 100644 --- a/messages/php/src-generated/DataTable.php +++ b/messages/php/src-generated/DataTable.php @@ -19,14 +19,14 @@ final class DataTable implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly Location $location, + public readonly Location $location = new Location(), /** * @param list $rows */ - public readonly array $rows, + public readonly array $rows = [], ){} diff --git a/messages/php/src-generated/DocString.php b/messages/php/src-generated/DocString.php index af98df16b04..8fa9423c1eb 100644 --- a/messages/php/src-generated/DocString.php +++ b/messages/php/src-generated/DocString.php @@ -19,15 +19,15 @@ final class DocString implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly Location $location, + public readonly Location $location = new Location(), - public readonly ?string $mediaType, + public readonly ?string $mediaType = null, - public readonly string $content, + public readonly string $content = '', - public readonly string $delimiter, + public readonly string $delimiter = '', ){} diff --git a/messages/php/src-generated/Duration.php b/messages/php/src-generated/Duration.php index ba2b1ad9c89..514530cf3d5 100644 --- a/messages/php/src-generated/Duration.php +++ b/messages/php/src-generated/Duration.php @@ -20,9 +20,9 @@ final class Duration implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly int $seconds, + public readonly int $seconds = 0, /** * Non-negative fractions of a second at nanosecond resolution. Negative @@ -30,7 +30,7 @@ private function __construct( * that count forward in time. Must be from 0 to 999,999,999 * inclusive. */ - public readonly int $nanos, + public readonly int $nanos = 0, ){} diff --git a/messages/php/src-generated/Envelope.php b/messages/php/src-generated/Envelope.php index 64e3e91baaa..f2d4d09f16c 100644 --- a/messages/php/src-generated/Envelope.php +++ b/messages/php/src-generated/Envelope.php @@ -24,41 +24,41 @@ final class Envelope implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?Attachment $attachment, + public readonly ?Attachment $attachment = null, - public readonly ?GherkinDocument $gherkinDocument, + public readonly ?GherkinDocument $gherkinDocument = null, - public readonly ?Hook $hook, + public readonly ?Hook $hook = null, - public readonly ?Meta $meta, + public readonly ?Meta $meta = null, - public readonly ?ParameterType $parameterType, + public readonly ?ParameterType $parameterType = null, - public readonly ?ParseError $parseError, + public readonly ?ParseError $parseError = null, - public readonly ?Pickle $pickle, + public readonly ?Pickle $pickle = null, - public readonly ?Source $source, + public readonly ?Source $source = null, - public readonly ?StepDefinition $stepDefinition, + public readonly ?StepDefinition $stepDefinition = null, - public readonly ?TestCase $testCase, + public readonly ?TestCase $testCase = null, - public readonly ?TestCaseFinished $testCaseFinished, + public readonly ?TestCaseFinished $testCaseFinished = null, - public readonly ?TestCaseStarted $testCaseStarted, + public readonly ?TestCaseStarted $testCaseStarted = null, - public readonly ?TestRunFinished $testRunFinished, + public readonly ?TestRunFinished $testRunFinished = null, - public readonly ?TestRunStarted $testRunStarted, + public readonly ?TestRunStarted $testRunStarted = null, - public readonly ?TestStepFinished $testStepFinished, + public readonly ?TestStepFinished $testStepFinished = null, - public readonly ?TestStepStarted $testStepStarted, + public readonly ?TestStepStarted $testStepStarted = null, - public readonly ?UndefinedParameterType $undefinedParameterType, + public readonly ?UndefinedParameterType $undefinedParameterType = null, ){} diff --git a/messages/php/src-generated/Examples.php b/messages/php/src-generated/Examples.php index 3eb3402ec56..98846a0ac0c 100644 --- a/messages/php/src-generated/Examples.php +++ b/messages/php/src-generated/Examples.php @@ -19,32 +19,32 @@ final class Examples implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the `Examples` keyword */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * @param list $tags */ - public readonly array $tags, + public readonly array $tags = [], - public readonly string $keyword, + public readonly string $keyword = '', - public readonly string $name, + public readonly string $name = '', - public readonly string $description, + public readonly string $description = '', - public readonly ?TableRow $tableHeader, + public readonly ?TableRow $tableHeader = null, /** * @param list $tableBody */ - public readonly array $tableBody, + public readonly array $tableBody = [], - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/Feature.php b/messages/php/src-generated/Feature.php index 63c0477398b..1c2a91413b2 100644 --- a/messages/php/src-generated/Feature.php +++ b/messages/php/src-generated/Feature.php @@ -19,44 +19,44 @@ final class Feature implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the `Feature` keyword */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * All the tags placed above the `Feature` keyword * @param list $tags */ - public readonly array $tags, + public readonly array $tags = [], /** * The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document */ - public readonly string $language, + public readonly string $language = '', /** * The text of the `Feature` keyword (in the language specified by `language`) */ - public readonly string $keyword, + public readonly string $keyword = '', /** * The name of the feature (the text following the `keyword`) */ - public readonly string $name, + public readonly string $name = '', /** * The line(s) underneath the line with the `keyword` that are used as description */ - public readonly string $description, + public readonly string $description = '', /** * Zero or more children * @param list $children */ - public readonly array $children, + public readonly array $children = [], ){} diff --git a/messages/php/src-generated/FeatureChild.php b/messages/php/src-generated/FeatureChild.php index 1be99201240..4cb90f79f33 100644 --- a/messages/php/src-generated/FeatureChild.php +++ b/messages/php/src-generated/FeatureChild.php @@ -19,13 +19,13 @@ final class FeatureChild implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?Rule $rule, + public readonly ?Rule $rule = null, - public readonly ?Background $background, + public readonly ?Background $background = null, - public readonly ?Scenario $scenario, + public readonly ?Scenario $scenario = null, ){} diff --git a/messages/php/src-generated/GherkinDocument.php b/messages/php/src-generated/GherkinDocument.php index db589c09b73..b065591755a 100644 --- a/messages/php/src-generated/GherkinDocument.php +++ b/messages/php/src-generated/GherkinDocument.php @@ -24,21 +24,21 @@ final class GherkinDocument implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) * of the source, typically a file path relative to the root directory */ - public readonly ?string $uri, + public readonly ?string $uri = null, - public readonly ?Feature $feature, + public readonly ?Feature $feature = null, /** * All the comments in the Gherkin document * @param list $comments */ - public readonly array $comments, + public readonly array $comments = [], ){} diff --git a/messages/php/src-generated/Git.php b/messages/php/src-generated/Git.php index d65cb3b2018..7e60e2b209b 100644 --- a/messages/php/src-generated/Git.php +++ b/messages/php/src-generated/Git.php @@ -20,15 +20,15 @@ final class Git implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $remote, + public readonly string $remote = '', - public readonly string $revision, + public readonly string $revision = '', - public readonly ?string $branch, + public readonly ?string $branch = null, - public readonly ?string $tag, + public readonly ?string $tag = null, ){} diff --git a/messages/php/src-generated/Group.php b/messages/php/src-generated/Group.php index 80a7d6fce5c..d6aeeb49592 100644 --- a/messages/php/src-generated/Group.php +++ b/messages/php/src-generated/Group.php @@ -19,16 +19,16 @@ final class Group implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * @param list $children */ - public readonly array $children, + public readonly array $children = [], - public readonly ?int $start, + public readonly ?int $start = null, - public readonly ?string $value, + public readonly ?string $value = null, ){} diff --git a/messages/php/src-generated/Hook.php b/messages/php/src-generated/Hook.php index e4927c6f87c..7a5fa84c3dc 100644 --- a/messages/php/src-generated/Hook.php +++ b/messages/php/src-generated/Hook.php @@ -19,13 +19,13 @@ final class Hook implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $id, + public readonly string $id = '', - public readonly SourceReference $sourceReference, + public readonly SourceReference $sourceReference = new SourceReference(), - public readonly ?string $tagExpression, + public readonly ?string $tagExpression = null, ){} diff --git a/messages/php/src-generated/JavaMethod.php b/messages/php/src-generated/JavaMethod.php index 346ded7c136..7b654fde127 100644 --- a/messages/php/src-generated/JavaMethod.php +++ b/messages/php/src-generated/JavaMethod.php @@ -19,16 +19,16 @@ final class JavaMethod implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $className, + public readonly string $className = '', - public readonly string $methodName, + public readonly string $methodName = '', /** * @param list $methodParameterTypes */ - public readonly array $methodParameterTypes, + public readonly array $methodParameterTypes = [], ){} diff --git a/messages/php/src-generated/JavaStackTraceElement.php b/messages/php/src-generated/JavaStackTraceElement.php index 79fee26b5e8..f8eab1a6661 100644 --- a/messages/php/src-generated/JavaStackTraceElement.php +++ b/messages/php/src-generated/JavaStackTraceElement.php @@ -19,13 +19,13 @@ final class JavaStackTraceElement implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $className, + public readonly string $className = '', - public readonly string $fileName, + public readonly string $fileName = '', - public readonly string $methodName, + public readonly string $methodName = '', ){} diff --git a/messages/php/src-generated/Location.php b/messages/php/src-generated/Location.php index 42a332e573a..01ce671561f 100644 --- a/messages/php/src-generated/Location.php +++ b/messages/php/src-generated/Location.php @@ -19,11 +19,11 @@ final class Location implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly int $line, + public readonly int $line = 0, - public readonly ?int $column, + public readonly ?int $column = null, ){} diff --git a/messages/php/src-generated/Meta.php b/messages/php/src-generated/Meta.php index 0e0fa37be26..308b9fb51a9 100644 --- a/messages/php/src-generated/Meta.php +++ b/messages/php/src-generated/Meta.php @@ -20,34 +20,34 @@ final class Meta implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The [SEMVER](https://semver.org/) version number of the protocol */ - public readonly string $protocolVersion, + public readonly string $protocolVersion = '', /** * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. */ - public readonly Product $implementation, + public readonly Product $implementation = new Product(), /** * Java, Ruby, Node.js etc */ - public readonly Product $runtime, + public readonly Product $runtime = new Product(), /** * Windows, Linux, MacOS etc */ - public readonly Product $os, + public readonly Product $os = new Product(), /** * 386, arm, amd64 etc */ - public readonly Product $cpu, + public readonly Product $cpu = new Product(), - public readonly ?Ci $ci, + public readonly ?Ci $ci = null, ){} diff --git a/messages/php/src-generated/ParameterType.php b/messages/php/src-generated/ParameterType.php index daae87987aa..d61a2a9befc 100644 --- a/messages/php/src-generated/ParameterType.php +++ b/messages/php/src-generated/ParameterType.php @@ -19,23 +19,23 @@ final class ParameterType implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The name is unique, so we don't need an id. */ - public readonly string $name, + public readonly string $name = '', /** * @param list $regularExpressions */ - public readonly array $regularExpressions, + public readonly array $regularExpressions = [], - public readonly bool $preferForRegularExpressionMatch, + public readonly bool $preferForRegularExpressionMatch = false, - public readonly bool $useForSnippets, + public readonly bool $useForSnippets = false, - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/ParseError.php b/messages/php/src-generated/ParseError.php index 359f658def6..b2565259820 100644 --- a/messages/php/src-generated/ParseError.php +++ b/messages/php/src-generated/ParseError.php @@ -19,11 +19,11 @@ final class ParseError implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly SourceReference $source, + public readonly SourceReference $source = new SourceReference(), - public readonly string $message, + public readonly string $message = '', ){} diff --git a/messages/php/src-generated/Pickle.php b/messages/php/src-generated/Pickle.php index 1b6455a349a..a70d862d0a6 100644 --- a/messages/php/src-generated/Pickle.php +++ b/messages/php/src-generated/Pickle.php @@ -30,42 +30,42 @@ final class Pickle implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash * from the source data and the `locations` of the pickle. * This ID will change if source the file is modified. */ - public readonly string $id, + public readonly string $id = '', /** * The uri of the source file */ - public readonly string $uri, + public readonly string $uri = '', /** * The name of the pickle */ - public readonly string $name, + public readonly string $name = '', /** * The language of the pickle */ - public readonly string $language, + public readonly string $language = '', /** * One or more steps * @param list $steps */ - public readonly array $steps, + public readonly array $steps = [], /** * One or more tags. If this pickle is constructed from a Gherkin document, * It includes inherited tags from the `Feature` as well. * @param list $tags */ - public readonly array $tags, + public readonly array $tags = [], /** * Points to the AST node locations of the pickle. The last one represents the unique @@ -73,7 +73,7 @@ private function __construct( * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. * @param list $astNodeIds */ - public readonly array $astNodeIds, + public readonly array $astNodeIds = [], ){} diff --git a/messages/php/src-generated/PickleDocString.php b/messages/php/src-generated/PickleDocString.php index a9f3a2cc161..07125ae82b2 100644 --- a/messages/php/src-generated/PickleDocString.php +++ b/messages/php/src-generated/PickleDocString.php @@ -19,11 +19,11 @@ final class PickleDocString implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?string $mediaType, + public readonly ?string $mediaType = null, - public readonly string $content, + public readonly string $content = '', ){} diff --git a/messages/php/src-generated/PickleStep.php b/messages/php/src-generated/PickleStep.php index c2ccc0940f2..fffb78f1936 100644 --- a/messages/php/src-generated/PickleStep.php +++ b/messages/php/src-generated/PickleStep.php @@ -19,23 +19,23 @@ final class PickleStep implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?PickleStepArgument $argument, + public readonly ?PickleStepArgument $argument = null, /** * References the IDs of the source of the step. For Gherkin, this can be * the ID of a Step, and possibly also the ID of a TableRow * @param list $astNodeIds */ - public readonly array $astNodeIds, + public readonly array $astNodeIds = [], /** * A unique ID for the PickleStep */ - public readonly string $id, + public readonly string $id = '', - public readonly string $text, + public readonly string $text = '', ){} diff --git a/messages/php/src-generated/PickleStepArgument.php b/messages/php/src-generated/PickleStepArgument.php index 7bc6fe425d2..ce56615b555 100644 --- a/messages/php/src-generated/PickleStepArgument.php +++ b/messages/php/src-generated/PickleStepArgument.php @@ -19,11 +19,11 @@ final class PickleStepArgument implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?PickleDocString $docString, + public readonly ?PickleDocString $docString = null, - public readonly ?PickleTable $dataTable, + public readonly ?PickleTable $dataTable = null, ){} diff --git a/messages/php/src-generated/PickleTable.php b/messages/php/src-generated/PickleTable.php index a3304cbbcde..10cbbc09ecb 100644 --- a/messages/php/src-generated/PickleTable.php +++ b/messages/php/src-generated/PickleTable.php @@ -19,12 +19,12 @@ final class PickleTable implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * @param list $rows */ - public readonly array $rows, + public readonly array $rows = [], ){} diff --git a/messages/php/src-generated/PickleTableCell.php b/messages/php/src-generated/PickleTableCell.php index be4da955628..04b2a75c6f8 100644 --- a/messages/php/src-generated/PickleTableCell.php +++ b/messages/php/src-generated/PickleTableCell.php @@ -19,9 +19,9 @@ final class PickleTableCell implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $value, + public readonly string $value = '', ){} diff --git a/messages/php/src-generated/PickleTableRow.php b/messages/php/src-generated/PickleTableRow.php index dac88249a23..714c7ec94cf 100644 --- a/messages/php/src-generated/PickleTableRow.php +++ b/messages/php/src-generated/PickleTableRow.php @@ -19,12 +19,12 @@ final class PickleTableRow implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * @param list $cells */ - public readonly array $cells, + public readonly array $cells = [], ){} diff --git a/messages/php/src-generated/PickleTag.php b/messages/php/src-generated/PickleTag.php index 325cdf353f9..20bb4ca31c5 100644 --- a/messages/php/src-generated/PickleTag.php +++ b/messages/php/src-generated/PickleTag.php @@ -19,14 +19,14 @@ final class PickleTag implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $name, + public readonly string $name = '', /** * Points to the AST node this was created from */ - public readonly string $astNodeId, + public readonly string $astNodeId = '', ){} diff --git a/messages/php/src-generated/Product.php b/messages/php/src-generated/Product.php index e6c5ded300d..3b41a4ddf80 100644 --- a/messages/php/src-generated/Product.php +++ b/messages/php/src-generated/Product.php @@ -19,17 +19,17 @@ final class Product implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The product name */ - public readonly string $name, + public readonly string $name = '', /** * The product version */ - public readonly ?string $version, + public readonly ?string $version = null, ){} diff --git a/messages/php/src-generated/Rule.php b/messages/php/src-generated/Rule.php index 6e74241325d..98f27acd465 100644 --- a/messages/php/src-generated/Rule.php +++ b/messages/php/src-generated/Rule.php @@ -19,31 +19,31 @@ final class Rule implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the `Rule` keyword */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * All the tags placed above the `Rule` keyword * @param list $tags */ - public readonly array $tags, + public readonly array $tags = [], - public readonly string $keyword, + public readonly string $keyword = '', - public readonly string $name, + public readonly string $name = '', - public readonly string $description, + public readonly string $description = '', /** * @param list $children */ - public readonly array $children, + public readonly array $children = [], - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/RuleChild.php b/messages/php/src-generated/RuleChild.php index 9609e9b5f12..c24fc96c6ee 100644 --- a/messages/php/src-generated/RuleChild.php +++ b/messages/php/src-generated/RuleChild.php @@ -19,11 +19,11 @@ final class RuleChild implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?Background $background, + public readonly ?Background $background = null, - public readonly ?Scenario $scenario, + public readonly ?Scenario $scenario = null, ){} diff --git a/messages/php/src-generated/Scenario.php b/messages/php/src-generated/Scenario.php index 968e0cac537..fc015125f0d 100644 --- a/messages/php/src-generated/Scenario.php +++ b/messages/php/src-generated/Scenario.php @@ -19,35 +19,35 @@ final class Scenario implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the `Scenario` keyword */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * @param list $tags */ - public readonly array $tags, + public readonly array $tags = [], - public readonly string $keyword, + public readonly string $keyword = '', - public readonly string $name, + public readonly string $name = '', - public readonly string $description, + public readonly string $description = '', /** * @param list $steps */ - public readonly array $steps, + public readonly array $steps = [], /** * @param list $examples */ - public readonly array $examples, + public readonly array $examples = [], - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/Source.php b/messages/php/src-generated/Source.php index 8f2b7f87dd9..92dffbabceb 100644 --- a/messages/php/src-generated/Source.php +++ b/messages/php/src-generated/Source.php @@ -21,24 +21,24 @@ final class Source implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) * of the source, typically a file path relative to the root directory */ - public readonly string $uri, + public readonly string $uri = '', /** * The contents of the file */ - public readonly string $data, + public readonly string $data = '', /** * The media type of the file. Can be used to specify custom types, such as * text/x.cucumber.gherkin+plain */ - public readonly Source\MediaType $mediaType, + public readonly Source\MediaType $mediaType = Source\MediaType::TEXT_X_CUCUMBER_GHERKIN_PLAIN, ){} diff --git a/messages/php/src-generated/SourceReference.php b/messages/php/src-generated/SourceReference.php index 41ff00d7432..9ec9e30f00b 100644 --- a/messages/php/src-generated/SourceReference.php +++ b/messages/php/src-generated/SourceReference.php @@ -20,15 +20,15 @@ final class SourceReference implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly ?string $uri, + public readonly ?string $uri = null, - public readonly ?JavaMethod $javaMethod, + public readonly ?JavaMethod $javaMethod = null, - public readonly ?JavaStackTraceElement $javaStackTraceElement, + public readonly ?JavaStackTraceElement $javaStackTraceElement = null, - public readonly ?Location $location, + public readonly ?Location $location = null, ){} diff --git a/messages/php/src-generated/Step.php b/messages/php/src-generated/Step.php index 0f8586d51f7..051bfe659a1 100644 --- a/messages/php/src-generated/Step.php +++ b/messages/php/src-generated/Step.php @@ -19,25 +19,25 @@ final class Step implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the steps' `keyword` */ - public readonly Location $location, + public readonly Location $location = new Location(), - public readonly string $keyword, + public readonly string $keyword = '', - public readonly string $text, + public readonly string $text = '', - public readonly ?DocString $docString, + public readonly ?DocString $docString = null, - public readonly ?DataTable $dataTable, + public readonly ?DataTable $dataTable = null, /** * Unique ID to be able to reference the Step from PickleStep */ - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/StepDefinition.php b/messages/php/src-generated/StepDefinition.php index 78e0bb26313..919dbb24891 100644 --- a/messages/php/src-generated/StepDefinition.php +++ b/messages/php/src-generated/StepDefinition.php @@ -19,13 +19,13 @@ final class StepDefinition implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $id, + public readonly string $id = '', - public readonly StepDefinitionPattern $pattern, + public readonly StepDefinitionPattern $pattern = new StepDefinitionPattern(), - public readonly SourceReference $sourceReference, + public readonly SourceReference $sourceReference = new SourceReference(), ){} diff --git a/messages/php/src-generated/StepDefinitionPattern.php b/messages/php/src-generated/StepDefinitionPattern.php index 05f35c81600..8e11f2f3f54 100644 --- a/messages/php/src-generated/StepDefinitionPattern.php +++ b/messages/php/src-generated/StepDefinitionPattern.php @@ -19,11 +19,11 @@ final class StepDefinitionPattern implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $source, + public readonly string $source = '', - public readonly StepDefinitionPattern\Type $type, + public readonly StepDefinitionPattern\Type $type = StepDefinitionPattern\Type::CUCUMBER_EXPRESSION, ){} diff --git a/messages/php/src-generated/StepMatchArgument.php b/messages/php/src-generated/StepMatchArgument.php index 9d8ad6c8bbc..bd3cfd61805 100644 --- a/messages/php/src-generated/StepMatchArgument.php +++ b/messages/php/src-generated/StepMatchArgument.php @@ -24,15 +24,15 @@ final class StepMatchArgument implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * Represents the outermost capture group of an argument. This message closely matches the * `Group` class in the `cucumber-expressions` library. */ - public readonly Group $group, + public readonly Group $group = new Group(), - public readonly ?string $parameterTypeName, + public readonly ?string $parameterTypeName = null, ){} diff --git a/messages/php/src-generated/StepMatchArgumentsList.php b/messages/php/src-generated/StepMatchArgumentsList.php index 2e471ca06dc..111b2ed134e 100644 --- a/messages/php/src-generated/StepMatchArgumentsList.php +++ b/messages/php/src-generated/StepMatchArgumentsList.php @@ -19,12 +19,12 @@ final class StepMatchArgumentsList implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * @param list $stepMatchArguments */ - public readonly array $stepMatchArguments, + public readonly array $stepMatchArguments = [], ){} diff --git a/messages/php/src-generated/TableCell.php b/messages/php/src-generated/TableCell.php index 24d548106d4..8b36958ec79 100644 --- a/messages/php/src-generated/TableCell.php +++ b/messages/php/src-generated/TableCell.php @@ -19,17 +19,17 @@ final class TableCell implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the cell */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * The value of the cell */ - public readonly string $value, + public readonly string $value = '', ){} diff --git a/messages/php/src-generated/TableRow.php b/messages/php/src-generated/TableRow.php index a5c44f7f979..df47636a40c 100644 --- a/messages/php/src-generated/TableRow.php +++ b/messages/php/src-generated/TableRow.php @@ -19,20 +19,20 @@ final class TableRow implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The location of the first cell in the row */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * Cells in the row * @param list $cells */ - public readonly array $cells, + public readonly array $cells = [], - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/Tag.php b/messages/php/src-generated/Tag.php index 953e0f8c1ff..2dc14a37c4e 100644 --- a/messages/php/src-generated/Tag.php +++ b/messages/php/src-generated/Tag.php @@ -19,22 +19,22 @@ final class Tag implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * Location of the tag */ - public readonly Location $location, + public readonly Location $location = new Location(), /** * The name of the tag (including the leading `@`) */ - public readonly string $name, + public readonly string $name = '', /** * Unique ID to be able to reference the Tag from PickleTag */ - public readonly string $id, + public readonly string $id = '', ){} diff --git a/messages/php/src-generated/TestCase.php b/messages/php/src-generated/TestCase.php index 56ca12e6bc1..f652422042a 100644 --- a/messages/php/src-generated/TestCase.php +++ b/messages/php/src-generated/TestCase.php @@ -21,19 +21,19 @@ final class TestCase implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $id, + public readonly string $id = '', /** * The ID of the `Pickle` this `TestCase` is derived from. */ - public readonly string $pickleId, + public readonly string $pickleId = '', /** * @param list $testSteps */ - public readonly array $testSteps, + public readonly array $testSteps = [], ){} diff --git a/messages/php/src-generated/TestCaseFinished.php b/messages/php/src-generated/TestCaseFinished.php index 0969c0ec84a..680d1edf619 100644 --- a/messages/php/src-generated/TestCaseFinished.php +++ b/messages/php/src-generated/TestCaseFinished.php @@ -19,13 +19,13 @@ final class TestCaseFinished implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $testCaseStartedId, + public readonly string $testCaseStartedId = '', - public readonly Timestamp $timestamp, + public readonly Timestamp $timestamp = new Timestamp(), - public readonly bool $willBeRetried, + public readonly bool $willBeRetried = false, ){} diff --git a/messages/php/src-generated/TestCaseStarted.php b/messages/php/src-generated/TestCaseStarted.php index 7e1564b0339..7277da2e5a6 100644 --- a/messages/php/src-generated/TestCaseStarted.php +++ b/messages/php/src-generated/TestCaseStarted.php @@ -19,23 +19,23 @@ final class TestCaseStarted implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * The first attempt should have value 0, and for each retry the value * should increase by 1. */ - public readonly int $attempt, + public readonly int $attempt = 0, /** * Because a `TestCase` can be run multiple times (in case of a retry), * we use this field to group messages relating to the same attempt. */ - public readonly string $id, + public readonly string $id = '', - public readonly string $testCaseId, + public readonly string $testCaseId = '', - public readonly Timestamp $timestamp, + public readonly Timestamp $timestamp = new Timestamp(), ){} diff --git a/messages/php/src-generated/TestRunFinished.php b/messages/php/src-generated/TestRunFinished.php index b5a57c92f1e..3fef9580b0c 100644 --- a/messages/php/src-generated/TestRunFinished.php +++ b/messages/php/src-generated/TestRunFinished.php @@ -19,7 +19,7 @@ final class TestRunFinished implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. @@ -28,17 +28,17 @@ private function __construct( * The independent `UndefinedParameterType` messages can be used to generate * snippets for those parameter types. */ - public readonly ?string $message, + public readonly ?string $message = null, /** * success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) */ - public readonly bool $success, + public readonly bool $success = false, /** * Timestamp when the TestRun is finished */ - public readonly Timestamp $timestamp, + public readonly Timestamp $timestamp = new Timestamp(), ){} diff --git a/messages/php/src-generated/TestRunStarted.php b/messages/php/src-generated/TestRunStarted.php index d9d566dca0c..45df5d901df 100644 --- a/messages/php/src-generated/TestRunStarted.php +++ b/messages/php/src-generated/TestRunStarted.php @@ -19,9 +19,9 @@ final class TestRunStarted implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly Timestamp $timestamp, + public readonly Timestamp $timestamp = new Timestamp(), ){} diff --git a/messages/php/src-generated/TestStep.php b/messages/php/src-generated/TestStep.php index f5133dd956b..25f31fe8b94 100644 --- a/messages/php/src-generated/TestStep.php +++ b/messages/php/src-generated/TestStep.php @@ -20,25 +20,25 @@ final class TestStep implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * Pointer to the `Hook` (if derived from a Hook) */ - public readonly ?string $hookId, + public readonly ?string $hookId = null, - public readonly string $id, + public readonly string $id = '', /** * Pointer to the `PickleStep` (if derived from a `PickleStep`) */ - public readonly ?string $pickleStepId, + public readonly ?string $pickleStepId = null, /** * Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) * @param ?list $stepDefinitionIds */ - public readonly ?array $stepDefinitionIds, + public readonly ?array $stepDefinitionIds = null, /** * A list of list of StepMatchArgument (if derived from a `PickleStep`). @@ -46,7 +46,7 @@ private function __construct( * and a size of 2+ means `AMBIGUOUS` * @param ?list $stepMatchArgumentsLists */ - public readonly ?array $stepMatchArgumentsLists, + public readonly ?array $stepMatchArgumentsLists = null, ){} diff --git a/messages/php/src-generated/TestStepFinished.php b/messages/php/src-generated/TestStepFinished.php index b462f8a64a7..c0cf411cd00 100644 --- a/messages/php/src-generated/TestStepFinished.php +++ b/messages/php/src-generated/TestStepFinished.php @@ -19,15 +19,15 @@ final class TestStepFinished implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $testCaseStartedId, + public readonly string $testCaseStartedId = '', - public readonly string $testStepId, + public readonly string $testStepId = '', - public readonly TestStepResult $testStepResult, + public readonly TestStepResult $testStepResult = new TestStepResult(), - public readonly Timestamp $timestamp, + public readonly Timestamp $timestamp = new Timestamp(), ){} diff --git a/messages/php/src-generated/TestStepResult.php b/messages/php/src-generated/TestStepResult.php index 2ba81633555..221f438e805 100644 --- a/messages/php/src-generated/TestStepResult.php +++ b/messages/php/src-generated/TestStepResult.php @@ -19,13 +19,13 @@ final class TestStepResult implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly Duration $duration, + public readonly Duration $duration = new Duration(), - public readonly ?string $message, + public readonly ?string $message = null, - public readonly TestStepResult\Status $status, + public readonly TestStepResult\Status $status = TestStepResult\Status::UNKNOWN, ){} diff --git a/messages/php/src-generated/TestStepStarted.php b/messages/php/src-generated/TestStepStarted.php index d98c571837f..c3735500a08 100644 --- a/messages/php/src-generated/TestStepStarted.php +++ b/messages/php/src-generated/TestStepStarted.php @@ -19,13 +19,13 @@ final class TestStepStarted implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $testCaseStartedId, + public readonly string $testCaseStartedId = '', - public readonly string $testStepId, + public readonly string $testStepId = '', - public readonly Timestamp $timestamp, + public readonly Timestamp $timestamp = new Timestamp(), ){} diff --git a/messages/php/src-generated/Timestamp.php b/messages/php/src-generated/Timestamp.php index ddce365922a..c2ee9a2065e 100644 --- a/messages/php/src-generated/Timestamp.php +++ b/messages/php/src-generated/Timestamp.php @@ -19,14 +19,14 @@ final class Timestamp implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( /** * Represents seconds of UTC time since Unix epoch * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to * 9999-12-31T23:59:59Z inclusive. */ - public readonly int $seconds, + public readonly int $seconds = 0, /** * Non-negative fractions of a second at nanosecond resolution. Negative @@ -34,7 +34,7 @@ private function __construct( * that count forward in time. Must be from 0 to 999,999,999 * inclusive. */ - public readonly int $nanos, + public readonly int $nanos = 0, ){} diff --git a/messages/php/src-generated/UndefinedParameterType.php b/messages/php/src-generated/UndefinedParameterType.php index 52bc009c143..56a43fb3826 100644 --- a/messages/php/src-generated/UndefinedParameterType.php +++ b/messages/php/src-generated/UndefinedParameterType.php @@ -19,11 +19,11 @@ final class UndefinedParameterType implements JsonSerializable { use JsonEncodingTrait; - private function __construct( + public function __construct( - public readonly string $expression, + public readonly string $expression = '', - public readonly string $name, + public readonly string $name = '', ){} diff --git a/messages/php/tests/EnvelopeTest.php b/messages/php/tests/EnvelopeTest.php new file mode 100644 index 00000000000..1ec3c37857d --- /dev/null +++ b/messages/php/tests/EnvelopeTest.php @@ -0,0 +1,36 @@ +gherkinDocument); + } + + public function testItCanBeConstructedWithASubsetOfProperties() : void + { + $envelope = new Envelope( + gherkinDocument: new GherkinDocument( + feature: new Feature( + location: new Location( + line: 21 + ) + ) + ) + ); + + self::assertSame( + 21, + $envelope->gherkinDocument?->feature?->location?->line + ); + } + +} From ce973c87dc86d14c5c6097949ec73efab940845a Mon Sep 17 00:00:00 2001 From: Ciaran McNulty Date: Thu, 27 Jan 2022 19:34:28 +0000 Subject: [PATCH 20/37] Add coding style rules --- messages/jsonschema/scripts/codegen.rb | 2 +- .../jsonschema/scripts/templates/php.php.erb | 16 +++++----- messages/php/.gitignore | 1 + messages/php/Makefile | 4 +++ messages/php/composer.json | 3 +- messages/php/src-generated/Attachment.php | 31 ++++++++---------- .../Attachment/ContentEncoding.php | 5 +-- messages/php/src-generated/Background.php | 20 +++++------- messages/php/src-generated/Ci.php | 15 ++++----- messages/php/src-generated/Comment.php | 14 ++++---- messages/php/src-generated/DataTable.php | 16 +++++----- messages/php/src-generated/DocString.php | 17 ++++------ messages/php/src-generated/Duration.php | 14 ++++---- messages/php/src-generated/Envelope.php | 32 +++++-------------- messages/php/src-generated/Examples.php | 23 ++++++------- messages/php/src-generated/Feature.php | 18 +++++------ messages/php/src-generated/FeatureChild.php | 16 ++++------ .../php/src-generated/GherkinDocument.php | 19 ++++++----- messages/php/src-generated/Git.php | 17 ++++------ messages/php/src-generated/Group.php | 18 +++++------ messages/php/src-generated/Hook.php | 16 ++++------ messages/php/src-generated/JavaMethod.php | 17 +++++----- .../src-generated/JavaStackTraceElement.php | 16 ++++------ messages/php/src-generated/Location.php | 15 ++++----- messages/php/src-generated/Meta.php | 15 ++++----- messages/php/src-generated/ParameterType.php | 19 +++++------ messages/php/src-generated/ParseError.php | 15 ++++----- messages/php/src-generated/Pickle.php | 26 +++++++-------- .../php/src-generated/PickleDocString.php | 15 ++++----- messages/php/src-generated/PickleStep.php | 17 +++++----- .../php/src-generated/PickleStepArgument.php | 15 ++++----- messages/php/src-generated/PickleTable.php | 16 +++++----- .../php/src-generated/PickleTableCell.php | 14 ++++---- messages/php/src-generated/PickleTableRow.php | 16 +++++----- messages/php/src-generated/PickleTag.php | 14 ++++---- messages/php/src-generated/Product.php | 14 ++++---- messages/php/src-generated/Rule.php | 22 ++++++------- messages/php/src-generated/RuleChild.php | 15 ++++----- messages/php/src-generated/Scenario.php | 24 ++++++-------- messages/php/src-generated/Source.php | 16 +++++----- .../php/src-generated/Source/MediaType.php | 5 +-- .../php/src-generated/SourceReference.php | 17 ++++------ messages/php/src-generated/Step.php | 18 ++++------- messages/php/src-generated/StepDefinition.php | 16 ++++------ .../src-generated/StepDefinitionPattern.php | 15 ++++----- .../StepDefinitionPattern/Type.php | 5 +-- .../php/src-generated/StepMatchArgument.php | 17 +++++----- .../src-generated/StepMatchArgumentsList.php | 16 +++++----- messages/php/src-generated/TableCell.php | 14 ++++---- messages/php/src-generated/TableRow.php | 17 +++++----- messages/php/src-generated/Tag.php | 14 ++++---- messages/php/src-generated/TestCase.php | 18 +++++------ .../php/src-generated/TestCaseFinished.php | 16 ++++------ .../php/src-generated/TestCaseStarted.php | 16 ++++------ .../php/src-generated/TestRunFinished.php | 14 ++++---- messages/php/src-generated/TestRunStarted.php | 14 ++++---- messages/php/src-generated/TestStep.php | 19 ++++++----- .../php/src-generated/TestStepFinished.php | 17 ++++------ messages/php/src-generated/TestStepResult.php | 16 ++++------ .../src-generated/TestStepResult/Status.php | 5 +-- .../php/src-generated/TestStepStarted.php | 16 ++++------ messages/php/src-generated/Timestamp.php | 14 ++++---- .../src-generated/UndefinedParameterType.php | 15 ++++----- messages/php/src/DecodingException.php | 4 ++- .../MalformedJsonException.php | 4 ++- .../SchemaViolationException.php | 4 ++- .../UnexpectedDecodingException.php | 4 ++- messages/php/src/JsonEncodingTrait.php | 16 +++++----- .../src/Streams/NdJson/NdJsonStreamReader.php | 6 ++-- .../src/Streams/NdJson/NdJsonStreamWriter.php | 8 +++-- messages/php/src/Streams/StreamReader.php | 4 ++- messages/php/src/Streams/StreamWriter.php | 4 ++- .../php/src/Streams/WithFileHandleTrait.php | 7 ++-- messages/php/tests/AcceptanceTest.php | 19 ++++++----- messages/php/tests/EnvelopeTest.php | 5 ++- 75 files changed, 498 insertions(+), 559 deletions(-) diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 9763d2e7c17..2ac32cf35fb 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -376,7 +376,7 @@ def non_nullable_constructor_for(parent_type, property, property_name, schema, a if type == 'array' constructor = non_nullable_constructor_for(parent_type, property['items'], nil, schema, "member") member_type = (property['items']['type'] ? 'mixed' : 'array') - "array_map(fn(#{member_type} $member) => #{constructor} , $#{source})" + "array_map(fn (#{member_type} $member) => #{constructor}, $#{source})" else "#{type_for(parent_type, property_name, property)}::fromArray($#{source})" end diff --git a/messages/jsonschema/scripts/templates/php.php.erb b/messages/jsonschema/scripts/templates/php.php.erb index e600d8b198b..0b7101abf58 100644 --- a/messages/jsonschema/scripts/templates/php.php.erb +++ b/messages/jsonschema/scripts/templates/php.php.erb @@ -1,4 +1,6 @@ - // CLASS_START <%= class_name(key) %>.php /** @@ -20,8 +21,7 @@ final class <%= class_name(key) %> implements JsonSerializable { use JsonEncodingTrait; - public function __construct( - <%- schema['properties'].each do |property_name, property| -%> + public function __construct(<%- schema['properties'].each do |property_name, property| -%> <%- property_type = type_for(key, property_name, property) -%> <%- if property['description'] or property_type == 'array' -%> @@ -36,15 +36,15 @@ final class <%= class_name(key) %> implements JsonSerializable <%- end -%> public readonly <%= is_nullable(property_name, schema) ? '?' : '' %><%= property_type %> $<%= property_name %> = <%= default_value(class_name(key), property_name, property, schema) -%>, <%- end -%> - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { <%- schema['properties'].each do |property_name, property| -%> self::ensure<%= capitalize(property_name)%>($arr); diff --git a/messages/php/.gitignore b/messages/php/.gitignore index 999d5412dc2..a6c661f618b 100644 --- a/messages/php/.gitignore +++ b/messages/php/.gitignore @@ -1,3 +1,4 @@ vendor composer.lock .phpunit.cache +.php-cs-fixer.cache diff --git a/messages/php/Makefile b/messages/php/Makefile index 22415e7a967..1585f80e339 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -13,7 +13,11 @@ build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschem ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ rm -rf src-generated/* php split_classes.php + vendor/bin/php-cs-fixer --diff fix src-generated test: .deps + vendor/bin/php-cs-fixer --dry-run --diff fix src + vendor/bin/php-cs-fixer --dry-run --diff fix src-generated + vendor/bin/php-cs-fixer --dry-run --diff fix tests vendor/bin/psalm vendor/bin/phpunit diff --git a/messages/php/composer.json b/messages/php/composer.json index d57a60be320..5a6fff16d8d 100644 --- a/messages/php/composer.json +++ b/messages/php/composer.json @@ -11,7 +11,8 @@ "require-dev": { "vimeo/psalm": "^4.18", "phpunit/phpunit": "^9.5", - "psalm/plugin-phpunit": "^0.16.1" + "psalm/plugin-phpunit": "^0.16.1", + "friendsofphp/php-cs-fixer": "^3.5" }, "autoload": { "psr-4": { diff --git a/messages/php/src-generated/Attachment.php b/messages/php/src-generated/Attachment.php index bb31261845f..e55eb66926e 100644 --- a/messages/php/src-generated/Attachment.php +++ b/messages/php/src-generated/Attachment.php @@ -1,4 +1,6 @@ - IDENTITY * - byte array => BASE64 * - stream => BASE64 @@ -62,36 +62,33 @@ public function __construct( * and `text/x.cucumber.stacktrace+plain` */ public readonly string $mediaType = '', - public readonly ?Source $source = null, - public readonly ?string $testCaseStartedId = null, - public readonly ?string $testStepId = null, /** * A URL where the attachment can be retrieved. This field should not be set by Cucumber. * It should be set by a program that reads a message stream and does the following for * each Attachment message: - * + * * - Writes the body (after base64 decoding if necessary) to a new file. * - Sets `body` and `contentEncoding` to `null` * - Writes out the new attachment message - * + * * This will result in a smaller message stream, which can improve performance and * reduce bandwidth of message consumers. It also makes it easier to process and download attachments * separately from reports. */ public readonly ?string $url = null, - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureBody($arr); self::ensureContentEncoding($arr); diff --git a/messages/php/src-generated/Attachment/ContentEncoding.php b/messages/php/src-generated/Attachment/ContentEncoding.php index 2e24a48b055..d44580b93c3 100644 --- a/messages/php/src-generated/Attachment/ContentEncoding.php +++ b/messages/php/src-generated/Attachment/ContentEncoding.php @@ -1,4 +1,6 @@ - $steps */ public readonly array $steps = [], - public readonly string $id = '', - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureKeyword($arr); @@ -60,7 +56,7 @@ public static function fromArray(array $arr) : self (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), + array_map(fn (array $member) => Step::fromArray($member), $arr['steps']), (string) $arr['id'], ); } diff --git a/messages/php/src-generated/Ci.php b/messages/php/src-generated/Ci.php index 097ec7e4731..6be25275210 100644 --- a/messages/php/src-generated/Ci.php +++ b/messages/php/src-generated/Ci.php @@ -1,4 +1,6 @@ - $rows */ public readonly array $rows = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureRows($arr); return new self( Location::fromArray($arr['location']), - array_map(fn(array $member) => TableRow::fromArray($member) , $arr['rows']), + array_map(fn (array $member) => TableRow::fromArray($member), $arr['rows']), ); } diff --git a/messages/php/src-generated/DocString.php b/messages/php/src-generated/DocString.php index 8fa9423c1eb..f6fe10b6295 100644 --- a/messages/php/src-generated/DocString.php +++ b/messages/php/src-generated/DocString.php @@ -1,4 +1,6 @@ - $tags */ public readonly array $tags = [], - public readonly string $keyword = '', - public readonly string $name = '', - public readonly string $description = '', - public readonly ?TableRow $tableHeader = null, /** * @param list $tableBody */ public readonly array $tableBody = [], - public readonly string $id = '', - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureTags($arr); @@ -66,12 +61,12 @@ public static function fromArray(array $arr) : self return new self( Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn (array $member) => Tag::fromArray($member), $arr['tags']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], isset($arr['tableHeader']) ? TableRow::fromArray($arr['tableHeader']) : null, - array_map(fn(array $member) => TableRow::fromArray($member) , $arr['tableBody']), + array_map(fn (array $member) => TableRow::fromArray($member), $arr['tableBody']), (string) $arr['id'], ); } diff --git a/messages/php/src-generated/Feature.php b/messages/php/src-generated/Feature.php index 1c2a91413b2..3ff20579d27 100644 --- a/messages/php/src-generated/Feature.php +++ b/messages/php/src-generated/Feature.php @@ -1,4 +1,6 @@ - $children */ public readonly array $children = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureTags($arr); @@ -77,12 +77,12 @@ public static function fromArray(array $arr) : self return new self( Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn (array $member) => Tag::fromArray($member), $arr['tags']), (string) $arr['language'], (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(array $member) => FeatureChild::fromArray($member) , $arr['children']), + array_map(fn (array $member) => FeatureChild::fromArray($member), $arr['children']), ); } diff --git a/messages/php/src-generated/FeatureChild.php b/messages/php/src-generated/FeatureChild.php index 4cb90f79f33..90cdfec786f 100644 --- a/messages/php/src-generated/FeatureChild.php +++ b/messages/php/src-generated/FeatureChild.php @@ -1,4 +1,6 @@ - $comments */ public readonly array $comments = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureUri($arr); self::ensureFeature($arr); @@ -56,7 +55,7 @@ public static function fromArray(array $arr) : self return new self( isset($arr['uri']) ? (string) $arr['uri'] : null, isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null, - array_map(fn(array $member) => Comment::fromArray($member) , $arr['comments']), + array_map(fn (array $member) => Comment::fromArray($member), $arr['comments']), ); } diff --git a/messages/php/src-generated/Git.php b/messages/php/src-generated/Git.php index 7e60e2b209b..8fbc8ac9246 100644 --- a/messages/php/src-generated/Git.php +++ b/messages/php/src-generated/Git.php @@ -1,4 +1,6 @@ - $children */ public readonly array $children = [], - public readonly ?int $start = null, - public readonly ?string $value = null, - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureChildren($arr); self::ensureStart($arr); self::ensureValue($arr); return new self( - array_map(fn(array $member) => Group::fromArray($member) , $arr['children']), + array_map(fn (array $member) => Group::fromArray($member), $arr['children']), isset($arr['start']) ? (int) $arr['start'] : null, isset($arr['value']) ? (string) $arr['value'] : null, ); diff --git a/messages/php/src-generated/Hook.php b/messages/php/src-generated/Hook.php index 7a5fa84c3dc..81621290860 100644 --- a/messages/php/src-generated/Hook.php +++ b/messages/php/src-generated/Hook.php @@ -1,4 +1,6 @@ - $methodParameterTypes */ public readonly array $methodParameterTypes = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureClassName($arr); self::ensureMethodName($arr); @@ -46,7 +45,7 @@ public static function fromArray(array $arr) : self return new self( (string) $arr['className'], (string) $arr['methodName'], - array_map(fn(mixed $member) => (string) $member , $arr['methodParameterTypes']), + array_map(fn (mixed $member) => (string) $member, $arr['methodParameterTypes']), ); } diff --git a/messages/php/src-generated/JavaStackTraceElement.php b/messages/php/src-generated/JavaStackTraceElement.php index f8eab1a6661..959f6c5b770 100644 --- a/messages/php/src-generated/JavaStackTraceElement.php +++ b/messages/php/src-generated/JavaStackTraceElement.php @@ -1,4 +1,6 @@ - $regularExpressions */ public readonly array $regularExpressions = [], - public readonly bool $preferForRegularExpressionMatch = false, - public readonly bool $useForSnippets = false, - public readonly string $id = '', - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureName($arr); self::ensureRegularExpressions($arr); @@ -54,7 +51,7 @@ public static function fromArray(array $arr) : self return new self( (string) $arr['name'], - array_map(fn(mixed $member) => (string) $member , $arr['regularExpressions']), + array_map(fn (mixed $member) => (string) $member, $arr['regularExpressions']), (bool) $arr['preferForRegularExpressionMatch'], (bool) $arr['useForSnippets'], (string) $arr['id'], diff --git a/messages/php/src-generated/ParseError.php b/messages/php/src-generated/ParseError.php index b2565259820..af44e9ea622 100644 --- a/messages/php/src-generated/ParseError.php +++ b/messages/php/src-generated/ParseError.php @@ -1,4 +1,6 @@ - $astNodeIds */ public readonly array $astNodeIds = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureId($arr); self::ensureUri($arr); @@ -97,9 +97,9 @@ public static function fromArray(array $arr) : self (string) $arr['uri'], (string) $arr['name'], (string) $arr['language'], - array_map(fn(array $member) => PickleStep::fromArray($member) , $arr['steps']), - array_map(fn(array $member) => PickleTag::fromArray($member) , $arr['tags']), - array_map(fn(mixed $member) => (string) $member , $arr['astNodeIds']), + array_map(fn (array $member) => PickleStep::fromArray($member), $arr['steps']), + array_map(fn (array $member) => PickleTag::fromArray($member), $arr['tags']), + array_map(fn (mixed $member) => (string) $member, $arr['astNodeIds']), ); } diff --git a/messages/php/src-generated/PickleDocString.php b/messages/php/src-generated/PickleDocString.php index 07125ae82b2..eefebce3137 100644 --- a/messages/php/src-generated/PickleDocString.php +++ b/messages/php/src-generated/PickleDocString.php @@ -1,4 +1,6 @@ - (string) $member , $arr['astNodeIds']), + array_map(fn (mixed $member) => (string) $member, $arr['astNodeIds']), (string) $arr['id'], (string) $arr['text'], ); diff --git a/messages/php/src-generated/PickleStepArgument.php b/messages/php/src-generated/PickleStepArgument.php index ce56615b555..31ad323a0ea 100644 --- a/messages/php/src-generated/PickleStepArgument.php +++ b/messages/php/src-generated/PickleStepArgument.php @@ -1,4 +1,6 @@ - $rows */ public readonly array $rows = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureRows($arr); return new self( - array_map(fn(array $member) => PickleTableRow::fromArray($member) , $arr['rows']), + array_map(fn (array $member) => PickleTableRow::fromArray($member), $arr['rows']), ); } diff --git a/messages/php/src-generated/PickleTableCell.php b/messages/php/src-generated/PickleTableCell.php index 04b2a75c6f8..f12d9bfd495 100644 --- a/messages/php/src-generated/PickleTableCell.php +++ b/messages/php/src-generated/PickleTableCell.php @@ -1,4 +1,6 @@ - $cells */ public readonly array $cells = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureCells($arr); return new self( - array_map(fn(array $member) => PickleTableCell::fromArray($member) , $arr['cells']), + array_map(fn (array $member) => PickleTableCell::fromArray($member), $arr['cells']), ); } diff --git a/messages/php/src-generated/PickleTag.php b/messages/php/src-generated/PickleTag.php index 20bb4ca31c5..5ee7f1fd0a4 100644 --- a/messages/php/src-generated/PickleTag.php +++ b/messages/php/src-generated/PickleTag.php @@ -1,4 +1,6 @@ - $tags */ public readonly array $tags = [], - public readonly string $keyword = '', - public readonly string $name = '', - public readonly string $description = '', /** * @param list $children */ public readonly array $children = [], - public readonly string $id = '', - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureTags($arr); @@ -64,11 +60,11 @@ public static function fromArray(array $arr) : self return new self( Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn (array $member) => Tag::fromArray($member), $arr['tags']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(array $member) => RuleChild::fromArray($member) , $arr['children']), + array_map(fn (array $member) => RuleChild::fromArray($member), $arr['children']), (string) $arr['id'], ); } diff --git a/messages/php/src-generated/RuleChild.php b/messages/php/src-generated/RuleChild.php index c24fc96c6ee..51e3edfccff 100644 --- a/messages/php/src-generated/RuleChild.php +++ b/messages/php/src-generated/RuleChild.php @@ -1,4 +1,6 @@ - $tags */ public readonly array $tags = [], - public readonly string $keyword = '', - public readonly string $name = '', - public readonly string $description = '', /** @@ -46,17 +43,16 @@ public function __construct( * @param list $examples */ public readonly array $examples = [], - public readonly string $id = '', - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureTags($arr); @@ -69,12 +65,12 @@ public static function fromArray(array $arr) : self return new self( Location::fromArray($arr['location']), - array_map(fn(array $member) => Tag::fromArray($member) , $arr['tags']), + array_map(fn (array $member) => Tag::fromArray($member), $arr['tags']), (string) $arr['keyword'], (string) $arr['name'], (string) $arr['description'], - array_map(fn(array $member) => Step::fromArray($member) , $arr['steps']), - array_map(fn(array $member) => Examples::fromArray($member) , $arr['examples']), + array_map(fn (array $member) => Step::fromArray($member), $arr['steps']), + array_map(fn (array $member) => Examples::fromArray($member), $arr['examples']), (string) $arr['id'], ); } diff --git a/messages/php/src-generated/Source.php b/messages/php/src-generated/Source.php index 92dffbabceb..2db93879ab8 100644 --- a/messages/php/src-generated/Source.php +++ b/messages/php/src-generated/Source.php @@ -1,4 +1,6 @@ - $stepMatchArguments */ public readonly array $stepMatchArguments = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureStepMatchArguments($arr); return new self( - array_map(fn(array $member) => StepMatchArgument::fromArray($member) , $arr['stepMatchArguments']), + array_map(fn (array $member) => StepMatchArgument::fromArray($member), $arr['stepMatchArguments']), ); } diff --git a/messages/php/src-generated/TableCell.php b/messages/php/src-generated/TableCell.php index 8b36958ec79..bcd2d49dcb4 100644 --- a/messages/php/src-generated/TableCell.php +++ b/messages/php/src-generated/TableCell.php @@ -1,4 +1,6 @@ - $cells */ public readonly array $cells = [], - public readonly string $id = '', - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureLocation($arr); self::ensureCells($arr); @@ -49,7 +48,7 @@ public static function fromArray(array $arr) : self return new self( Location::fromArray($arr['location']), - array_map(fn(array $member) => TableCell::fromArray($member) , $arr['cells']), + array_map(fn (array $member) => TableCell::fromArray($member), $arr['cells']), (string) $arr['id'], ); } diff --git a/messages/php/src-generated/Tag.php b/messages/php/src-generated/Tag.php index 2dc14a37c4e..adafca054fb 100644 --- a/messages/php/src-generated/Tag.php +++ b/messages/php/src-generated/Tag.php @@ -1,4 +1,6 @@ - $testSteps */ public readonly array $testSteps = [], - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureId($arr); self::ensurePickleId($arr); @@ -51,7 +51,7 @@ public static function fromArray(array $arr) : self return new self( (string) $arr['id'], (string) $arr['pickleId'], - array_map(fn(array $member) => TestStep::fromArray($member) , $arr['testSteps']), + array_map(fn (array $member) => TestStep::fromArray($member), $arr['testSteps']), ); } diff --git a/messages/php/src-generated/TestCaseFinished.php b/messages/php/src-generated/TestCaseFinished.php index 680d1edf619..35ba6a63d40 100644 --- a/messages/php/src-generated/TestCaseFinished.php +++ b/messages/php/src-generated/TestCaseFinished.php @@ -1,4 +1,6 @@ - $stepMatchArgumentsLists */ public readonly ?array $stepMatchArgumentsLists = null, - - ){} + ) { + } /** * @throws SchemaViolationException * * @internal */ - public static function fromArray(array $arr) : self + public static function fromArray(array $arr): self { self::ensureHookId($arr); self::ensureId($arr); @@ -67,8 +66,8 @@ public static function fromArray(array $arr) : self isset($arr['hookId']) ? (string) $arr['hookId'] : null, (string) $arr['id'], isset($arr['pickleStepId']) ? (string) $arr['pickleStepId'] : null, - isset($arr['stepDefinitionIds']) ? array_map(fn(mixed $member) => (string) $member , $arr['stepDefinitionIds']) : null, - isset($arr['stepMatchArgumentsLists']) ? array_map(fn(array $member) => StepMatchArgumentsList::fromArray($member) , $arr['stepMatchArgumentsLists']) : null, + isset($arr['stepDefinitionIds']) ? array_map(fn (mixed $member) => (string) $member, $arr['stepDefinitionIds']) : null, + isset($arr['stepMatchArgumentsLists']) ? array_map(fn (array $member) => StepMatchArgumentsList::fromArray($member), $arr['stepMatchArgumentsLists']) : null, ); } diff --git a/messages/php/src-generated/TestStepFinished.php b/messages/php/src-generated/TestStepFinished.php index c0cf411cd00..8d4f73fa956 100644 --- a/messages/php/src-generated/TestStepFinished.php +++ b/messages/php/src-generated/TestStepFinished.php @@ -1,4 +1,6 @@ -getMessage().'"', previous: $t); } } @@ -43,7 +43,7 @@ public static function fromJson(string $json) : self /** * Serialise the message into a JSON string */ - public function asJson() : string + public function asJson(): string { return json_encode($this, JSON_THROW_ON_ERROR); } @@ -59,7 +59,7 @@ public function jsonSerialize(): array { return array_filter( (array)$this, - fn(mixed $x) => !is_null($x) + fn (mixed $x) => !is_null($x) ); } } diff --git a/messages/php/src/Streams/NdJson/NdJsonStreamReader.php b/messages/php/src/Streams/NdJson/NdJsonStreamReader.php index 10ae6eb43a1..0573b16c717 100644 --- a/messages/php/src/Streams/NdJson/NdJsonStreamReader.php +++ b/messages/php/src/Streams/NdJson/NdJsonStreamReader.php @@ -1,4 +1,6 @@ - */ - public function envelopes() : Generator + public function envelopes(): Generator { while (!feof($this->fileHandle) && ($line = fgets($this->fileHandle))) { yield Envelope::fromJson($line); diff --git a/messages/php/src/Streams/NdJson/NdJsonStreamWriter.php b/messages/php/src/Streams/NdJson/NdJsonStreamWriter.php index ffc197000cc..140c39beac4 100644 --- a/messages/php/src/Streams/NdJson/NdJsonStreamWriter.php +++ b/messages/php/src/Streams/NdJson/NdJsonStreamWriter.php @@ -1,4 +1,6 @@ - $envelopes */ - public function writeEnvelopes(iterable $envelopes) : void + public function writeEnvelopes(iterable $envelopes): void { - foreach($envelopes as $envelope) { + foreach ($envelopes as $envelope) { fputs($this->fileHandle, $envelope->asJson() . "\n"); } } diff --git a/messages/php/src/Streams/StreamReader.php b/messages/php/src/Streams/StreamReader.php index 09744516774..3877b4aa634 100644 --- a/messages/php/src/Streams/StreamReader.php +++ b/messages/php/src/Streams/StreamReader.php @@ -1,4 +1,6 @@ -asJson(); + $envelope = Envelope::fromJson($json); + $newJson = $envelope->asJson(); - self::assertJsonStringEqualsJsonString($json, $newJson); + self::assertJsonStringEqualsJsonString($json, $newJson); } /** @dataProvider provideNdJsonFilenames */ - public function testAllFileStreamsSurviveDecodingThenEncoding(string $filename) : void + public function testAllFileStreamsSurviveDecodingThenEncoding(string $filename): void { $sourceHandle = fopen($filename, 'r'); $destHandle = fopen('php://memory', 'w'); @@ -48,10 +48,10 @@ public function testAllFileStreamsSurviveDecodingThenEncoding(string $filename) /** * @return Generator */ - public function provideJsonLines() : Generator + public function provideJsonLines(): Generator { foreach ($this->getSampleFiles() as $filename) { - foreach(file($filename) ?: [] as $lineNumber => $line) { + foreach (file($filename) ?: [] as $lineNumber => $line) { // key is provided for better error messages $key = realpath($filename) . ':' . $lineNumber; yield $key => [$line]; @@ -62,10 +62,9 @@ public function provideJsonLines() : Generator /** * @return Generator */ - public function provideNdJsonFilenames() : Generator + public function provideNdJsonFilenames(): Generator { - foreach($this->getSampleFiles() as $filename) - { + foreach ($this->getSampleFiles() as $filename) { yield $filename => [$filename]; } } diff --git a/messages/php/tests/EnvelopeTest.php b/messages/php/tests/EnvelopeTest.php index 1ec3c37857d..a47922e658b 100644 --- a/messages/php/tests/EnvelopeTest.php +++ b/messages/php/tests/EnvelopeTest.php @@ -8,14 +8,14 @@ class EnvelopeTest extends TestCase { - public function testItCanBeConstructedWithDefaultProperties() : void + public function testItCanBeConstructedWithDefaultProperties(): void { $envelope = new Envelope(); self::assertNull($envelope->gherkinDocument); } - public function testItCanBeConstructedWithASubsetOfProperties() : void + public function testItCanBeConstructedWithASubsetOfProperties(): void { $envelope = new Envelope( gherkinDocument: new GherkinDocument( @@ -32,5 +32,4 @@ public function testItCanBeConstructedWithASubsetOfProperties() : void $envelope->gherkinDocument?->feature?->location?->line ); } - } From 971a2f2df0ee2fcc6a0cea75951e37d37d412388 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 08:46:14 +0100 Subject: [PATCH 21/37] Add templates for php projects --- .templates/php/.gitignore | 4 +++ .templates/php/default.mk | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .templates/php/.gitignore create mode 100644 .templates/php/default.mk diff --git a/.templates/php/.gitignore b/.templates/php/.gitignore new file mode 100644 index 00000000000..a6c661f618b --- /dev/null +++ b/.templates/php/.gitignore @@ -0,0 +1,4 @@ +vendor +composer.lock +.phpunit.cache +.php-cs-fixer.cache diff --git a/.templates/php/default.mk b/.templates/php/default.mk new file mode 100644 index 00000000000..e7dd81d55a8 --- /dev/null +++ b/.templates/php/default.mk @@ -0,0 +1,51 @@ +# Please update /.templates/php/default.mk and sync: +# +# source scripts/functions.sh && rsync_files +# +SHELL := /usr/bin/env bash +PHP_SOURCE_FILES = $(shell find . -name "*.php") + +### COMMON stuff for all platforms + +### Common targets for all functionalities implemented on php + +default: .tested +.PHONY: default + +pre-release: update-version update-dependencies +.PHONY: pre-release + +update-version: +ifdef NEW_VERSION +# TODO: something here? +endif +.PHONY: update-version + +update-dependencies: +.PHONY: update-dependencies + +publish: +# TODO: how to publish? +.PHONY: publish + +post-release: +# no-op +.PHONY: post-release + +clean: + rm -rf vendor +.PHONY: clean + +.tested: .deps .codegen $(PHP_SOURCE_FILES) + vendor/bin/phpunit +.PHONY: .tested + +.deps: composer.lock + touch $@ + +.codegen: + touch $@ + +composer.lock: composer.json + composer install + touch $@ From 02240b17df7e3decd1143dccc7749c8dc348a62c Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 08:46:42 +0100 Subject: [PATCH 22/37] Add .rsync into messages/php --- messages/php/.rsync | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 messages/php/.rsync diff --git a/messages/php/.rsync b/messages/php/.rsync new file mode 100644 index 00000000000..22c58c2b4a8 --- /dev/null +++ b/messages/php/.rsync @@ -0,0 +1,3 @@ +../../LICENSE LICENSE +../../.templates/github/ .github/ +../../.templates/php/ . From faa8b0c9bfbb13fcfbfb0262c440c8a3cfb58e1b Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 09:04:57 +0100 Subject: [PATCH 23/37] Update default.mk php template --- .templates/php/default.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.templates/php/default.mk b/.templates/php/default.mk index e7dd81d55a8..def4ca36eda 100644 --- a/.templates/php/default.mk +++ b/.templates/php/default.mk @@ -33,7 +33,7 @@ post-release: .PHONY: post-release clean: - rm -rf vendor + rm -rf vendor composer.lock .PHONY: clean .tested: .deps .codegen $(PHP_SOURCE_FILES) From abd427dba2b688bd15fca6fb2219394570cb63ab Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 09:06:09 +0100 Subject: [PATCH 24/37] Synchronize messages/php files --- messages/Makefile | 2 +- messages/php/.github/ISSUE_TEMPLATE.md | 5 ++ messages/php/.github/PULL_REQUEST_TEMPLATE.md | 5 ++ messages/php/LICENSE | 21 ++++++++ messages/php/Makefile | 11 ++-- messages/php/default.mk | 51 +++++++++++++++++++ 6 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 messages/php/.github/ISSUE_TEMPLATE.md create mode 100644 messages/php/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 messages/php/LICENSE create mode 100644 messages/php/default.mk diff --git a/messages/Makefile b/messages/Makefile index cad0fee5019..6ada3ae0107 100644 --- a/messages/Makefile +++ b/messages/Makefile @@ -1,3 +1,3 @@ -LANGUAGES ?= jsonschema javascript go java ruby python perl +LANGUAGES ?= jsonschema javascript go java ruby python perl php include default.mk diff --git a/messages/php/.github/ISSUE_TEMPLATE.md b/messages/php/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..fac96198160 --- /dev/null +++ b/messages/php/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,5 @@ +PLEASE DO NOT CREATE ISSUES IN THIS REPO. +THIS REPO IS A READ-ONLY MIRROR. + +Create your issue in the Cucumber monorepo instead: +https://github.com/cucumber/cucumber/issues diff --git a/messages/php/.github/PULL_REQUEST_TEMPLATE.md b/messages/php/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..2c0c878d976 --- /dev/null +++ b/messages/php/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +PLEASE DO NOT CREATE PULL REAUESTS IN THIS REPO. +THIS REPO IS A READ-ONLY MIRROR. + +Create your pull request in the Cucumber monorepo instead: +https://github.com/cucumber/cucumber/pulls diff --git a/messages/php/LICENSE b/messages/php/LICENSE new file mode 100644 index 00000000000..725ba9f4ac7 --- /dev/null +++ b/messages/php/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Cucumber Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/messages/php/Makefile b/messages/php/Makefile index 1585f80e339..d286e717d85 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -1,13 +1,11 @@ +include default.mk JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") clean: - rm -rf vendor composer.lock build/messages.php + rm -rf build -.deps: composer.lock build/messages.php - -composer.lock: - composer install +.codegen: build/messages.php build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ @@ -15,9 +13,8 @@ build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschem php split_classes.php vendor/bin/php-cs-fixer --diff fix src-generated -test: .deps +.tested: vendor/bin/php-cs-fixer --dry-run --diff fix src vendor/bin/php-cs-fixer --dry-run --diff fix src-generated vendor/bin/php-cs-fixer --dry-run --diff fix tests vendor/bin/psalm - vendor/bin/phpunit diff --git a/messages/php/default.mk b/messages/php/default.mk new file mode 100644 index 00000000000..def4ca36eda --- /dev/null +++ b/messages/php/default.mk @@ -0,0 +1,51 @@ +# Please update /.templates/php/default.mk and sync: +# +# source scripts/functions.sh && rsync_files +# +SHELL := /usr/bin/env bash +PHP_SOURCE_FILES = $(shell find . -name "*.php") + +### COMMON stuff for all platforms + +### Common targets for all functionalities implemented on php + +default: .tested +.PHONY: default + +pre-release: update-version update-dependencies +.PHONY: pre-release + +update-version: +ifdef NEW_VERSION +# TODO: something here? +endif +.PHONY: update-version + +update-dependencies: +.PHONY: update-dependencies + +publish: +# TODO: how to publish? +.PHONY: publish + +post-release: +# no-op +.PHONY: post-release + +clean: + rm -rf vendor composer.lock +.PHONY: clean + +.tested: .deps .codegen $(PHP_SOURCE_FILES) + vendor/bin/phpunit +.PHONY: .tested + +.deps: composer.lock + touch $@ + +.codegen: + touch $@ + +composer.lock: composer.json + composer install + touch $@ From 3f791dbc347943ecb9c2ffe94117fa142da8fe4c Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 09:11:21 +0100 Subject: [PATCH 25/37] Add messages/php to CI in parallel jobs (not serial) --- .circleci/config.yml | 25 +++++++++++++++++++++++++ messages/Makefile | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 38d8c52eb82..b9f04a3e9a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -66,6 +66,12 @@ executors: docker: - image: cimg/python:3.10.2 working_directory: ~/cucumber + + docker-circleci-php: + docker: + -image: cimg/php:8.1.2 + working_directory: ~/cucumber + ### ### Jobs ### ### @@ -677,6 +683,19 @@ jobs: # cd gherkin/elixir # make + ### PHP + + messages-php: + executor: docker-circleci-php + steps: + - attach_workspace: + at: '~/cucumber' + - run: + name: messages/php + command: | + cd messages/php + make + ### ### Workflows ### ### @@ -888,3 +907,9 @@ workflows: # - gherkin-elixir: # requires: # - messages-elixir + + ### PHP + + - messages-php: + requires: + - prepare-parallel diff --git a/messages/Makefile b/messages/Makefile index 6ada3ae0107..cad0fee5019 100644 --- a/messages/Makefile +++ b/messages/Makefile @@ -1,3 +1,3 @@ -LANGUAGES ?= jsonschema javascript go java ruby python perl php +LANGUAGES ?= jsonschema javascript go java ruby python perl include default.mk From e09bbfa27ed2fc3ef29b1a2c081eb3f1cd3775ec Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 09:14:12 +0100 Subject: [PATCH 26/37] Fix circleci config.yml file --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b9f04a3e9a6..0b2299a84e6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,9 +67,10 @@ executors: - image: cimg/python:3.10.2 working_directory: ~/cucumber + # Php docker-circleci-php: docker: - -image: cimg/php:8.1.2 + - image: cimg/php:8.1.2 working_directory: ~/cucumber ### From ea9bbab603be11259d2b3cab044f391699434ae3 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 14:56:13 +0100 Subject: [PATCH 27/37] Update php makefile --- .templates/php/default.mk | 5 +++-- messages/php/Makefile | 1 - messages/php/default.mk | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.templates/php/default.mk b/.templates/php/default.mk index def4ca36eda..3751d2ff1f4 100644 --- a/.templates/php/default.mk +++ b/.templates/php/default.mk @@ -17,7 +17,7 @@ pre-release: update-version update-dependencies update-version: ifdef NEW_VERSION -# TODO: something here? +# no-op: composer rely on git tags endif .PHONY: update-version @@ -25,7 +25,7 @@ update-dependencies: .PHONY: update-dependencies publish: -# TODO: how to publish? +# no-op: composer will rely on the subrepo tag .PHONY: publish post-release: @@ -38,6 +38,7 @@ clean: .tested: .deps .codegen $(PHP_SOURCE_FILES) vendor/bin/phpunit + vendor/bin/psalm .PHONY: .tested .deps: composer.lock diff --git a/messages/php/Makefile b/messages/php/Makefile index d286e717d85..263e6956d41 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -17,4 +17,3 @@ build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschem vendor/bin/php-cs-fixer --dry-run --diff fix src vendor/bin/php-cs-fixer --dry-run --diff fix src-generated vendor/bin/php-cs-fixer --dry-run --diff fix tests - vendor/bin/psalm diff --git a/messages/php/default.mk b/messages/php/default.mk index def4ca36eda..3751d2ff1f4 100644 --- a/messages/php/default.mk +++ b/messages/php/default.mk @@ -17,7 +17,7 @@ pre-release: update-version update-dependencies update-version: ifdef NEW_VERSION -# TODO: something here? +# no-op: composer rely on git tags endif .PHONY: update-version @@ -25,7 +25,7 @@ update-dependencies: .PHONY: update-dependencies publish: -# TODO: how to publish? +# no-op: composer will rely on the subrepo tag .PHONY: publish post-release: @@ -38,6 +38,7 @@ clean: .tested: .deps .codegen $(PHP_SOURCE_FILES) vendor/bin/phpunit + vendor/bin/psalm .PHONY: .tested .deps: composer.lock From bd9f470394f24361cec98b550a94e48d8da123d2 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 15:27:53 +0100 Subject: [PATCH 28/37] Fix make .tested target --- messages/php/.gitignore | 2 ++ messages/php/Makefile | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/messages/php/.gitignore b/messages/php/.gitignore index a6c661f618b..e99e7592a11 100644 --- a/messages/php/.gitignore +++ b/messages/php/.gitignore @@ -2,3 +2,5 @@ vendor composer.lock .phpunit.cache .php-cs-fixer.cache +.codegen +.deps diff --git a/messages/php/Makefile b/messages/php/Makefile index 263e6956d41..20e56b08c19 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -13,7 +13,9 @@ build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschem php split_classes.php vendor/bin/php-cs-fixer --diff fix src-generated -.tested: +.tested: .cs-fixer + +.cs-fixer: vendor/bin/php-cs-fixer --dry-run --diff fix src vendor/bin/php-cs-fixer --dry-run --diff fix src-generated vendor/bin/php-cs-fixer --dry-run --diff fix tests From 4512643b61b6b74117265c64f562ad0604f5bc66 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 15:29:42 +0100 Subject: [PATCH 29/37] Use cucumber build image v0.12 --- .circleci/config.yml | 4 ++-- messages/Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0b2299a84e6..a52271da5dd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ executors: # the build nodes (faster builds). docker-cucumber-build: docker: - - image: cucumber/cucumber-build:0.11.0 + - image: cucumber/cucumber-build:0.12.0 working_directory: ~/cucumber environment: # nvm, node and npm are installed locally rather globally. @@ -687,7 +687,7 @@ jobs: ### PHP messages-php: - executor: docker-circleci-php + executor: docker-cucumber-build steps: - attach_workspace: at: '~/cucumber' diff --git a/messages/Makefile b/messages/Makefile index cad0fee5019..6ada3ae0107 100644 --- a/messages/Makefile +++ b/messages/Makefile @@ -1,3 +1,3 @@ -LANGUAGES ?= jsonschema javascript go java ruby python perl +LANGUAGES ?= jsonschema javascript go java ruby python perl php include default.mk From da2c254b02332a8cf3ddfcf4ee6e83ae75d91b9e Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 22:20:53 +0100 Subject: [PATCH 30/37] Use new cucumber-build 0.11.0 Due to an issue while releasing cucumber-build 0.12, it has actually been tagged 0.11.0 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a52271da5dd..14574a72ea2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ executors: # the build nodes (faster builds). docker-cucumber-build: docker: - - image: cucumber/cucumber-build:0.12.0 + - image: cucumber/cucumber-build:0.11.0 working_directory: ~/cucumber environment: # nvm, node and npm are installed locally rather globally. From c9ae102edae7a7b625e48a90194c8e1e84fef082 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 22:25:20 +0100 Subject: [PATCH 31/37] Update php template .gitignore --- .templates/php/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.templates/php/.gitignore b/.templates/php/.gitignore index a6c661f618b..e99e7592a11 100644 --- a/.templates/php/.gitignore +++ b/.templates/php/.gitignore @@ -2,3 +2,5 @@ vendor composer.lock .phpunit.cache .php-cs-fixer.cache +.codegen +.deps From 506938cf62fe592b5f77ea03d5a64276744d0d97 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Fri, 28 Jan 2022 22:47:18 +0100 Subject: [PATCH 32/37] Prevent overriding 'clean' make target --- messages/php/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messages/php/Makefile b/messages/php/Makefile index 20e56b08c19..d8d918e4e40 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -2,11 +2,13 @@ include default.mk JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") -clean: - rm -rf build +clean: clean-build .codegen: build/messages.php +clean-build: + rm -rf build + build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ rm -rf src-generated/* From 94308992cdc0995d20544e837f486f680e4d6470 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Mon, 31 Jan 2022 09:53:00 +0100 Subject: [PATCH 33/37] Fix codegen after merging main --- .circleci/config.yml | 2 +- messages/jsonschema/scripts/codegen.rb | 7 ++----- messages/php/Makefile | 3 ++- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 14574a72ea2..a52271da5dd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ executors: # the build nodes (faster builds). docker-cucumber-build: docker: - - image: cucumber/cucumber-build:0.11.0 + - image: cucumber/cucumber-build:0.12.0 working_directory: ~/cucumber environment: # nvm, node and npm are installed locally rather globally. diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index c80d1e9594b..490e0ff09dc 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -312,15 +312,12 @@ def array_type_for(type_name) class Php < Codegen def initialize(paths) - template = File.read("#{TEMPLATES_DIRECTORY}/php.php.erb") - enum_template = File.read("#{TEMPLATES_DIRECTORY}/php.enum.php.erb") - language_type_by_schema_type = { 'string' => 'string', 'integer' => 'int', 'boolean' => 'bool', } - super(paths, template, enum_template, language_type_by_schema_type) + super(paths, language_type_by_schema_type) end def format_description(raw_description, indent_string: " ") @@ -340,7 +337,7 @@ def array_type_for(type_name) def enum_name(parent_type_name, property_name, enum) enum_type_name = "#{class_name(parent_type_name)}\\#{capitalize(property_name)}" - @enums.add({ name: enum_type_name, values: enum }) + @enum_set.add({ name: enum_type_name, values: enum }) enum_type_name end diff --git a/messages/php/Makefile b/messages/php/Makefile index d8d918e4e40..bc4ba012e16 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -10,7 +10,8 @@ clean-build: rm -rf build build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb - ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema > $@ + ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.php.erb > $@ + ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.enum.php.erb > $@ rm -rf src-generated/* php split_classes.php vendor/bin/php-cs-fixer --diff fix src-generated From 4c6f647be23b7d73bbdfd9d232b49dcff313a6ab Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Mon, 31 Jan 2022 10:01:39 +0100 Subject: [PATCH 34/37] Fix php enum codegen --- messages/jsonschema/scripts/templates/php.enum.php.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages/jsonschema/scripts/templates/php.enum.php.erb b/messages/jsonschema/scripts/templates/php.enum.php.erb index 47cc0cd90f7..74030d28223 100644 --- a/messages/jsonschema/scripts/templates/php.enum.php.erb +++ b/messages/jsonschema/scripts/templates/php.enum.php.erb @@ -1,3 +1,4 @@ +<% @enums.each do |enum| -%> <%- namespaces = enum[:name].split('\\') -%> namespace Cucumber\Messages\<%= namespaces.slice(0,1)[0] %>; @@ -8,5 +9,5 @@ enum <%= namespaces[-1] %> : string case <%= enum_constant(value) %> = '<%= value %>'; <%- end -%> } - +<%- end -%> <%# block to preserve linebreaks %> From 977c9f68dc626539063549b66b34161f7e8fd005 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Mon, 31 Jan 2022 10:53:21 +0100 Subject: [PATCH 35/37] Fix codegen after merging the one from main --- messages/jsonschema/scripts/codegen.rb | 7 +++---- messages/php/Makefile | 5 ++++- messages/php/build/.gitignore | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 messages/php/build/.gitignore diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 490e0ff09dc..31da8e2a1cc 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -44,8 +44,7 @@ def add_schema(key, schema) enum = property['enum'] if enum parent_type_name = class_name(key) - enum_type_name = "#{parent_type_name}#{capitalize(property_name)}" - @enum_set.add({ name: enum_type_name, values: enum }) + enum_name(parent_type_name, property_name, enum) end end end @@ -99,7 +98,7 @@ def type_for(parent_type_name, property_name, property) else raise "No type mapping for JSONSchema type #{type}. Schema:\n#{JSON.pretty_generate(property)}" unless @language_type_by_schema_type[type] if enum - enum_type_name = "#{parent_type_name}#{capitalize(property_name)}" + enum_type_name = enum_name(parent_type_name, property_name, enum) property_type_from_enum(enum_type_name) else @language_type_by_schema_type[type] @@ -121,7 +120,7 @@ def property_type_from_enum(enum) def enum_name(parent_type_name, property_name, enum) enum_type_name = "#{parent_type_name}#{capitalize(property_name)}" - @enums.add({ name: enum_type_name, values: enum }) + @enum_set.add({ name: enum_type_name, values: enum }) enum_type_name end diff --git a/messages/php/Makefile b/messages/php/Makefile index bc4ba012e16..17582bceeb5 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -8,10 +8,13 @@ clean: clean-build clean-build: rm -rf build + rm -rf src-generated build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb + mkdir -p build + mkdir -p src-generated ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.php.erb > $@ - ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.enum.php.erb > $@ + ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.enum.php.erb >> $@ rm -rf src-generated/* php split_classes.php vendor/bin/php-cs-fixer --diff fix src-generated diff --git a/messages/php/build/.gitignore b/messages/php/build/.gitignore deleted file mode 100644 index 2ff0a6e5325..00000000000 --- a/messages/php/build/.gitignore +++ /dev/null @@ -1 +0,0 @@ -messages.php From b9e420ffc138c88c4e4f130ce17ebff87d44cf7b Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Mon, 31 Jan 2022 10:53:40 +0100 Subject: [PATCH 36/37] Ignoring the whole 'build' directory from git --- messages/php/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/messages/php/.gitignore b/messages/php/.gitignore index e99e7592a11..88d5af6422f 100644 --- a/messages/php/.gitignore +++ b/messages/php/.gitignore @@ -4,3 +4,4 @@ composer.lock .php-cs-fixer.cache .codegen .deps +build From 0e1097f30e20749fe0e45946bfd7d12a2ab38575 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Mon, 31 Jan 2022 10:58:25 +0100 Subject: [PATCH 37/37] Fix gitignore sync issue --- messages/php/.gitignore | 1 - messages/php/Makefile | 6 ++---- messages/php/build/.gitignore | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 messages/php/build/.gitignore diff --git a/messages/php/.gitignore b/messages/php/.gitignore index 88d5af6422f..e99e7592a11 100644 --- a/messages/php/.gitignore +++ b/messages/php/.gitignore @@ -4,4 +4,3 @@ composer.lock .php-cs-fixer.cache .codegen .deps -build diff --git a/messages/php/Makefile b/messages/php/Makefile index 17582bceeb5..a4b2effb9a7 100644 --- a/messages/php/Makefile +++ b/messages/php/Makefile @@ -7,12 +7,10 @@ clean: clean-build .codegen: build/messages.php clean-build: - rm -rf build - rm -rf src-generated + rm -rf build/messages.php + rm -rf src-generated/* build/messages.php: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/php.php.erb - mkdir -p build - mkdir -p src-generated ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.php.erb > $@ ruby ../jsonschema/scripts/codegen.rb Php ../jsonschema php.enum.php.erb >> $@ rm -rf src-generated/* diff --git a/messages/php/build/.gitignore b/messages/php/build/.gitignore new file mode 100644 index 00000000000..55390591648 --- /dev/null +++ b/messages/php/build/.gitignore @@ -0,0 +1 @@ +messages.php \ No newline at end of file