Getting started

Installation

The recommended way to install php-json-model-generator is through Composer:

composer require --dev wol-soft/php-json-schema-model-generator
composer require wol-soft/php-json-schema-model-generator-production

To avoid adding all dependencies of the php-json-model-generator to your production dependencies it’s recommended to add the library as a dev-dependency and include the php-json-model-generator-production library. The production library provides all classes to run the generated code. Generating the classes should either be a step done in the development environment or as a build step of your application (which is the recommended workflow).

Generating classes

The base object for generating models is the Generator. After you have created a Generator you can use the object to generate your model classes without any further configuration:

(new Generator())
    ->generateModels(new RecursiveDirectoryProvider(__DIR__ . '/schema'), __DIR__ . '/result');

The first parameter of the generateModels method must be a class implementing the SchemaProviderInterface. The provider fetches the JSON schema files and provides them for the generator. The following providers are available:

Provider Description
RecursiveDirectoryProvider Fetches all *.json files from the given source directory. Each file must contain a JSON Schema object definition on the top level
OpenAPIv3Provider Fetches all objects defined in the #/components/schemas section of an Open API v3 spec file

The second parameter must point to an existing and empty directory (you may use the generateModelDirectory helper method to create your destination directory). This directory will contain the generated PHP classes after the generator is finished.

As an optional parameter you can set up a GeneratorConfiguration object to configure your Generator and/or use the method generateModelDirectory to generate your model directory (will generate the directory if it doesn’t exist; if it exists, all contained files and folders will be removed for a clean generation process):

$generator = new Generator(
    (new GeneratorConfiguration())
        ->setNamespacePrefix('MyApp\Model')
        ->setImmutable(false)
);

$generator
    ->generateModelDirectory(__DIR__ . '/result');
    ->generateModels(new RecursiveDirectoryProvider(__DIR__ . '/schema'), __DIR__ . '/result');

The generator will check the given source directory recursive and convert all found *.json files to models. All JSON-Schema files inside the source directory must provide a schema of an object.

Default interface of configured classes

The constructor of a generated model takes an array as argument. The array must contain the structure defined in the JSON-Schema which is afterwards validated and applied to the state of the model.

{
    "$id": "person",
    "type": "object",
    "properties": {
        "name": {
            "type": "string"
        },
        "age": {
            "type": "integer",
            "minimum": 0
        }
    },
    "required": [
        "name"
    ]
}

After generating a class with this JSON-Schema our class with the name Person will provide the following interface (immutability disabled via GeneratorConfiguration):

// the constructor takes an array with data which is validated and applied to the model
public function __construct(array $modelData);

// the method getRawModelDataInput always delivers the raw input which was provided on instantiation
public function getRawModelDataInput(): array;

// getters to fetch the validated properties. Age is nullable as it's not required
public function getName(): string;
public function getAge(): ?int;

// setters to change the values of the model after instantiation
public function setName(string $name): Person;
public function setAge(int ?$age): Person;

Now let’s have a look at the behaviour of the generated model:

// Throws an exception as the required name isn't provided.
// Exception: 'Missing required value for name'
$person = new Person([]);

// Throws an exception as the name provides an invalid value.
// Exception: 'Invalid type for name. Requires string, got int'
$person = new Person(['name' => 12]);

// Throws an exception as the age contains an invalid value due to the minimum definition.
// Exception: 'Value for age must not be smaller than 0'
$person = new Person(['name' => 'Albert', 'age' => -1]);

// A valid example as the age isn't required
$person = new Person(['name' => 'Albert']);
$person->getName(); // returns 'Albert'
$person->getAge(); // returns NULL
$person->getRawModelDataInput(); // returns ['name' => 'Albert']

// If setters are generated the setters also perform validations.
// Exception: 'Value for age must not be smaller than 0'
$person->setAge(-10);

Each generated class will implement the interface PHPModelGenerator\Interfaces\JSONModelInterface implemented in the php-json-schema-model-generator-production repository and thus provide the method getRawModelDataInput.

Configuring the generator

The GeneratorConfiguration object offers methods to configure the generator in a fluid interface.

Namespace prefix

setNamespacePrefix(string $prefix);

Configures a namespace prefix for all generated classes. By default no namespace prefix will be set. Generated namespaces are PSR-4 compatible. Further information about the generated namespaces can be found at Namespaces.

(new GeneratorConfiguration())
    ->setNamespacePrefix('MyApp\Model');

Immutable classes

setImmutable(bool $immutable);

If set to true the generated model classes will be delivered without setter methods for the object properties. By default the classes are generated without setter methods. Each setter will validate the provided value and throw either a specific exception or a collection exception depending on the error collection configuration. If all validations pass the internal value will be updated as well as the value which will be returned when getRawModelDataInput is called.

(new GeneratorConfiguration())
    ->setImmutable(false);

Implicit null

By default the properties are strictly checked against their defined types. Consequently if you want a property to accept null you have to extend the type of your property explicitly (eg. [‘string’, ‘null’]).

By setting the implicit null option to true all of your object properties which aren’t required will implicitly accept null. All properties which are required and don’t explicitly allow null in the type definition will still reject null.

If the implicit null option is enabled the interface of your classes may change. If you have disabled immutability the type hints of your optional property setters will be nullable (eg. a string property will be type hinted with ?string).

setImplicitNull(bool $allowImplicitNull);
(new GeneratorConfiguration())
    ->setImplicitNull(true);

Default arrays to empty arrays

By default optional properties which contain an array will contain null if no array is provided (or null is provided with the implicit null setting enabled). For using the generated getter methods for those properties without a fallback the generator can be configured to default not provided arrays and null values to an empty array (by default this setting is disabled). By enabling this setting it’s ensured that all optional arrays will always contain an array even if no default value or null is provided.

// accessing an array property which may contain null may require a fallback
foreach ($generatedObject->getItems() ?? [] as $item) {

// by enabling the default to empty array setting the value returned by getItems will always contain an array
// consequently no fallback is necessary
foreach ($generatedObject->getItems() as $item) {

Hint

This setting affects only optional properties.

setDefaultArraysToEmptyArray(bool $defaultArraysToEmptyArray);
(new GeneratorConfiguration())
    ->setDefaultArraysToEmptyArray(true);

Deny additional properties

By default each generated object accepts additional properties. For strict property checks which error if undefined properties are provided each object must contain the additionalProperties key set to false.

By setting the denyAdditionalProperties option each object which doesn’t specify a value for additionalProperties is restricted to the defined properties.

setDenyAdditionalProperties(bool $denyAdditionalProperties);
(new GeneratorConfiguration())
    ->setDenyAdditionalProperties(true);

Collect errors vs. early return

setCollectErrors(bool $collectErrors);

By default the complete input is validated and in case of failing validations all error messages will be thrown in a single exception implementing the ErrorRegistryExceptionInterface interface. If set to false the first failing validation will throw an exception.

The exceptions are implemented in the php-json-schema-model-generator-production repository. Default exceptions:

  • Error collection enabled: PHPModelGenerator\Exception\ErrorRegistryException
  • Error collection disabled: specific exceptions extending the PHPModelGenerator\Exception\ValidationException

All collected exceptions from an ErrorRegistryException are accessible via the getErrors method. The collected errors are the specific exceptions extending the PHPModelGenerator\Exception\ValidationException which would be thrown directly if error collection is disabled. Each exception provides various specific details about the validation violation.

(new GeneratorConfiguration())
    ->setCollectErrors(false);

Hint

All builtin exceptions provide serialization methods (compare serialization). By default sensitive data (file and line) of the exception will not be serialized. The serialization methods provide another parameter $stripSensitiveData. When this parameter is set to false file and line information will be included.

Custom exception classes

setErrorRegistryClass(string $exceptionClass);

If you want to customize the exception handling you can set an own ErrorRegistryClass to collect all exceptions via setErrorRegistryClass. This setting will only affect the generated code if you have enabled error collection. The exception provided via setErrorRegistryClass must implement the ErrorRegistryExceptionInterface.

(new GeneratorConfiguration())
    ->setErrorRegistryClass(MyCustomException::class);

Serialization methods

setSerialization(bool $serialization);

If set to true the serialization methods toArray, toJSON and jsonSerialize will be added to the public interface of the generated classes. By default no serialization methods are added.

(new GeneratorConfiguration())
    ->setSerialization(true);

Generated interface:

public function toArray([array $except = [] [, int $depth = 512]]): array;
public function toJSON([array $except = [] [, int $options = 0 [, int $depth = 512]]]): string;
public function jsonSerialize(): array;

The generated class will implement the interface PHPModelGenerator\Interfaces\SerializationInterface implemented in the php-json-schema-model-generator-production repository. This interface can be used to write additional generic modules to handle the generated models. Via the $except parameter you can pass an array of properties which will not be serialized (eg. password data for a user object). The $depth parameter defines the maximum amount of nested objects which are serialized. The $options parameter for the toJSON method provides access to the underlying option bitmask of json_encode.

Additionally the class will implement the PHP builtin interface JsonSerializable which allows the direct usage of the generated classes in a custom json_encode.

Warning

If you provide additional properties you may want to use the AdditionalPropertiesAccessorPostProcessor as the additional properties by default aren’t included into the serialization result.

Output generation process

setOutputEnabled(bool $outputEnabled);

Enable or disable output of the generation process to STDOUT. By default the output is enabled.

(new GeneratorConfiguration())
    ->setOutputEnabled(false);

The output contains information about generated classes, rendered classes, hints and warnings concerning the internal handling or the given schema files. The output of a generation process may look like:

Generated class MyApp\User\Response\Login
Generated class MyApp\User\Response\Register
Duplicated signature 444fd086d8d1f186145a6f81a3ac3f7a for class Register_Message. Redirecting to Login_Message
Rendered class MyApp\User\Response\Login
Rendered class MyApp\User\Response\Register

Custom filter

addFilter(FilterInterface $customFilter);

Add a custom filter to the generator. For more details see Filter.

Post Processors

Additionally to the described generator configuration options you can add post processors to your model generator object to change or extend the generated code. For more details see post processors.