├── .env ├── .env.dist ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── autoload.php ├── cache └── .gitkeep ├── composer.json ├── docker-compose.yml ├── docker ├── hhvm │ ├── Dockerfile │ └── entrypoint.sh └── php │ ├── Dockerfile │ ├── config │ └── php.ini │ └── entrypoint.sh ├── phpbench.json.dist └── src ├── AbstractBench.php ├── Bench ├── BetterSerializerBenchmark.php ├── GsonBenchmark.php ├── IvoryBenchmark.php ├── JmsBenchmark.php ├── JmsMinimalBenchmark.php ├── JsonSerializableBenchmark.php ├── SerializardClosureBenchmark.php ├── SerializardReflectionBenchmark.php ├── SymfonyGetSetNormalizerBenchmark.php ├── SymfonyObjectNormalizerBenchmark.php └── TSantosBenchmark.php ├── BenchExtension.php ├── BenchFinder.php ├── Command └── InfoCommand.php ├── DataProvider.php └── Model ├── Category.php ├── Comment.php ├── Forum.php ├── Thread.php ├── TimestampableTrait.php └── User.php /.env: -------------------------------------------------------------------------------- 1 | # Permissions 2 | GROUP_ID=1000 3 | USER_ID=1000 4 | -------------------------------------------------------------------------------- /.env.dist: -------------------------------------------------------------------------------- 1 | # Permissions 2 | GROUP_ID=1000 3 | USER_ID=1000 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.env 2 | /cache/* 3 | !/cache/.gitkeep 4 | /vendor 5 | /composer.lock 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | # 3 | #branches: 4 | # only: master 5 | 6 | matrix: 7 | fast_finish: true 8 | include: 9 | - php: 7.2 10 | 11 | install: 12 | - phpenv config-rm xdebug.ini || echo 'xdebug not available' 13 | - phpenv config-add docker/php/config/php.ini 14 | - composer install --classmap-authoritative 15 | 16 | script: 17 | - ./vendor/bin/phpbench info 18 | - ./vendor/bin/phpbench run --report=bench --progress=travis 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2017 Eric GELOEN 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 6 | persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 9 | Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This project benchmarks the most popular & feature rich PHP serializers. It measures the time consumed during the 4 | serialization of an object graph and give you a report of the execution. 5 | 6 | The result of the benchmark is directly available on travis: https://travis-ci.org/php-serializers/ivory-serializer-benchmark 7 | 8 | This repository is a fork of [egeloen/ivory-serializer-benchmark](https://github.com/egeloen/ivory-serializer-benchmark), 9 | the project was looking not maintained for a while, please refer to this as the next reference point when benchmarking 10 | PHP serializers. 11 | 12 | ## Documentation 13 | 14 | If you're interesting to use the project locally, follow the next steps. 15 | 16 | ### Set up the project 17 | 18 | The easiest way to set up the project is to install [Docker](https://www.docker.com) and 19 | [Docker Composer](https://docs.docker.com/compose/) and build the project. The configuration is shipped with a 20 | distribution environment file allowing you to customize your current user/group ID: 21 | 22 | ``` bash 23 | $ cp .env.dist .env 24 | ``` 25 | 26 | **The most important part is the `USER_ID` and `GROUP_ID` which should match your current user/group.** 27 | 28 | Once you have configured your environment, you can build the project: 29 | 30 | ``` bash 31 | $ docker-compose build 32 | ``` 33 | 34 | ### Install dependencies 35 | 36 | Install the dependencies via [Composer](https://getcomposer.org/): 37 | 38 | ``` bash 39 | $ docker-compose run --rm php composer install 40 | ``` 41 | 42 | ### Benchmark 43 | 44 | We use [PHPBench](https://phpbench.readthedocs.io/) internally, with sane defaults setup for benchmarking the serializers. 45 | 46 | To benchmark serialization, you can use: 47 | 48 | ``` bash 49 | $ docker-compose run --rm php ./vendor/bin/phpbench run --report=bench 50 | ``` 51 | 52 | By default, the benchmark runs 5 [Revolutions](https://phpbench.readthedocs.io/en/latest/writing-benchmarks.html#improving-precision-revolutions) and 5 [Iterations](https://phpbench.readthedocs.io/en/latest/writing-benchmarks.html#verifying-and-improving-stability-iterations). 53 | You can override either with the `iterations` and `revs` options. 54 | 55 | ``` bash 56 | $ docker-compose run --rm php ./vendor/bin/phpbench run --report=bench --iterations=10 --revs=10 57 | ``` 58 | 59 | By default, the benchmark runs different configurations of horizontal and vertical complexity. 60 | If you wish to specify your own, you can use the `parameters` option which is an array of two 61 | integers, the first representing the horizontal and the second representing the vertical complexity. 62 | 63 | ``` bash 64 | $ docker-compose run --rm php ./vendor/bin/phpbench run --report=bench --parameters='[1,2]' 65 | ``` 66 | 67 | If you want to run the benchmark only for a specific or subset of serializers, you can use the `filter` option: 68 | 69 | ``` bash 70 | $ docker-compose run --rm php ./vendor/bin/phpbench run --report=bench --filter=Symfony 71 | ``` 72 | 73 | ### Available serializers 74 | 75 | You can see a list of the serializer available and its current version by running the following command: 76 | 77 | ``` bash 78 | $ docker-compose run --rm php ./vendor/bin/phpbench info 79 | ``` 80 | 81 | Available implementations: 82 | 83 | * `BetterSerializer` 84 | * `Ivory` 85 | * `JaneAutomapper` 86 | * `Jms` 87 | * `JmsMinimal` 88 | * `JsonSerializable` 89 | * `SerializardClosure` 90 | * `SerializardReflection` 91 | * `SymfonyGetSetNormalizer` 92 | * `SymfonyObjectNormalizer` 93 | * `TSantos` 94 | * `Gson` 95 | 96 | 97 | ## Contribute 98 | 99 | We love contributors! PhpSerializers is an open source project. If you'd like to contribute, feel free to propose a PR!. 100 | 101 | ## License 102 | 103 | The Php Serializers Benchmark is under the MIT license. For the full copyright and license information, please read the 104 | [LICENSE](/LICENSE) file that was distributed with this source code. 105 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | > /etc/hhvm/php.ini 12 | fi 13 | 14 | # Remove XDebug temporary configuration 15 | rm -f /var/www/xdebug.ini 16 | 17 | # Permissions 18 | groupmod -g ${GROUP_ID} www-data 19 | usermod -u ${USER_ID} www-data 20 | 21 | # Start bash or forward command 22 | if [ "$1" = "bash" ]; then 23 | su www-data 24 | else 25 | su www-data -c "$*" 26 | fi 27 | -------------------------------------------------------------------------------- /docker/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.2 2 | 3 | # APT packages 4 | RUN apt-get update && apt-get install -y \ 5 | zlib1g-dev \ 6 | git \ 7 | && rm -rf /var/lib/apt/lists/* 8 | 9 | # APCU extension 10 | RUN pecl install apcu \ 11 | && docker-php-ext-enable apcu \ 12 | && rm -rf /tmp/pear 13 | 14 | ## PHP configuration 15 | COPY config/php.ini /usr/local/etc/php/php.ini 16 | 17 | # Composer 18 | RUN curl -sS https://getcomposer.org/installer | php -- --filename=composer --install-dir=/usr/local/bin 19 | 20 | # Bash 21 | RUN chsh -s /bin/bash www-data 22 | 23 | # Workdir 24 | WORKDIR /var/www/html 25 | 26 | # Entrypoint 27 | COPY entrypoint.sh /usr/local/bin/entrypoint.sh 28 | ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] 29 | -------------------------------------------------------------------------------- /docker/php/config/php.ini: -------------------------------------------------------------------------------- 1 | apc.enabled=1 2 | apc.enable_cli=1 3 | memory_limit = -1 4 | opcache.enable_cli = 1 5 | session.gc_probability = 0 6 | extension = apcu.so 7 | -------------------------------------------------------------------------------- /docker/php/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | GROUP_ID=${GROUP_ID-1000} 6 | USER_ID=${USER_ID-1000} 7 | XDEBUG=${XDEBUG-0} 8 | 9 | # Disable XDebug 10 | if [ ! "$XDEBUG" = 1 ]; then 11 | rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini 12 | fi 13 | 14 | # Permissions 15 | groupmod -g ${GROUP_ID} www-data 16 | usermod -u ${USER_ID} www-data 17 | 18 | # Start bash or forward command 19 | if [ "$1" = "bash" ]; then 20 | su www-data 21 | else 22 | su www-data -c "$*" 23 | fi 24 | -------------------------------------------------------------------------------- /phpbench.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrap": "autoload.php", 3 | "path": "src/Bench", 4 | "reports": { 5 | "bench": { 6 | "extends": "aggregate", 7 | "break": ["params"], 8 | "cols": [ "benchmark", "mean", "diff", "worst", "best", "mem_real" ], 9 | "sort": {"mean": "asc"} 10 | } 11 | }, 12 | "extensions": [ 13 | "PhpSerializers\\Benchmarks\\BenchExtension" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/AbstractBench.php: -------------------------------------------------------------------------------- 1 | dataProvider = new DataProvider(); 26 | } 27 | 28 | final public function initData(array $params): void 29 | { 30 | $this->data = $this->dataProvider->getData(...$params); 31 | } 32 | 33 | /** 34 | * @ParamProviders("provideComplexity") 35 | */ 36 | final public function benchSerialize(): void 37 | { 38 | $this->serialize($this->data); 39 | } 40 | 41 | public function provideComplexity(): array 42 | { 43 | return [ 44 | [10, 10], 45 | [1, 60], 46 | [60, 1], 47 | [60, 60], 48 | ]; 49 | } 50 | 51 | abstract public function initSerializer(): void; 52 | 53 | abstract protected function serialize(Forum $data): void; 54 | 55 | abstract public function getPackageName(): string; 56 | 57 | abstract public function getNote(): string; 58 | } 59 | -------------------------------------------------------------------------------- /src/Bench/BetterSerializerBenchmark.php: -------------------------------------------------------------------------------- 1 | enableApcuCache(); 31 | } else { 32 | $builder->enableFilesystemCache(dirname(__DIR__, 1) . '/cache/better-serializer'); 33 | } 34 | 35 | $this->serializer = $builder->createSerializer(); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function serialize(Forum $data): void 42 | { 43 | $this->serializer->serialize( 44 | $data, 45 | SerializationType::JSON() 46 | ); 47 | } 48 | 49 | public function getPackageName(): string 50 | { 51 | return 'better-serializer/better-serializer'; 52 | } 53 | 54 | public function getNote(): string 55 | { 56 | return <<<'NOTE' 57 | Serializer for PHP 7.2+ supporting JSON 58 | NOTE; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Bench/GsonBenchmark.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class GsonBenchmark extends AbstractBench 17 | { 18 | /** 19 | * @var Gson 20 | */ 21 | private $gson; 22 | 23 | public function initSerializer(): void 24 | { 25 | $cache = new ApcuCache(); 26 | $this->gson = Gson::builder() 27 | ->enableCache(true) 28 | ->setCache($cache) 29 | ->setEnableScalarAdapters(false) 30 | ->setPropertyNamingPolicy(PropertyNamingPolicy::IDENTITY) 31 | ->build(); 32 | } 33 | 34 | public function serialize(Forum $data): void 35 | { 36 | $this->gson->toJson($data); 37 | } 38 | 39 | public function getPackageName(): string 40 | { 41 | return 'tebru/gson-php'; 42 | } 43 | 44 | public function getNote(): string 45 | { 46 | return <<<'NOTE' 47 | Serialize object graphs 48 | NOTE; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Bench/IvoryBenchmark.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class IvoryBenchmark extends AbstractBench 22 | { 23 | /** 24 | * @var Serializer 25 | */ 26 | private $serializer; 27 | 28 | public function initSerializer(): void 29 | { 30 | $classMetadataFactory = new CacheClassMetadataFactory( 31 | ClassMetadataFactory::create(), 32 | new ApcuAdapter('IvoryMetadata') 33 | ); 34 | 35 | $typeRegistry = TypeRegistry::create([ 36 | Type::OBJECT => new ObjectType($classMetadataFactory), 37 | ]); 38 | 39 | $this->serializer = new Serializer(new Navigator($typeRegistry)); 40 | } 41 | 42 | public function serialize(Forum $data): void 43 | { 44 | $this->serializer->serialize($data, 'json'); 45 | } 46 | 47 | public function getPackageName(): string 48 | { 49 | return 'egeloen/serializer'; 50 | } 51 | 52 | public function getNote(): string 53 | { 54 | return <<<'NOTE' 55 | Serializer for PHP 5.6+ supporting JSON, XML, YAML & CSV 56 | NOTE; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Bench/JmsBenchmark.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class JmsBenchmark extends AbstractBench 20 | { 21 | /** 22 | * @var Serializer 23 | */ 24 | private $serializer; 25 | 26 | public function initSerializer(): void 27 | { 28 | $cache = new ApcuCache(); 29 | $this->serializer = SerializerBuilder::create() 30 | ->setAnnotationReader(new CachedReader(new AnnotationReader(), $cache, false)) 31 | ->setMetadataCache(new DoctrineCacheAdapter(__CLASS__, $cache)) 32 | ->build(); 33 | } 34 | 35 | public function serialize(Forum $data): void 36 | { 37 | $this->serializer->serialize($data, 'json'); 38 | } 39 | 40 | public function getPackageName(): string 41 | { 42 | return 'jms/serializer'; 43 | } 44 | 45 | public function getNote(): string 46 | { 47 | return <<<'NOTE' 48 | Serialize object graphs with default handlers enabled 49 | NOTE; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Bench/JmsMinimalBenchmark.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class JmsMinimalBenchmark extends AbstractBench 20 | { 21 | /** 22 | * @var Serializer 23 | */ 24 | private $serializer; 25 | 26 | public function initSerializer(): void 27 | { 28 | $cache = new ApcuCache(); 29 | $this->serializer = SerializerBuilder::create() 30 | ->setAnnotationReader(new CachedReader(new AnnotationReader(), $cache, false)) 31 | ->setMetadataCache(new DoctrineCacheAdapter(__CLASS__, $cache)) 32 | ->configureListeners(function () { 33 | }) 34 | ->configureHandlers(function () { 35 | }) 36 | ->build(); 37 | } 38 | 39 | public function serialize(Forum $data): void 40 | { 41 | $this->serializer->serialize($data, 'json'); 42 | } 43 | 44 | public function getPackageName(): string 45 | { 46 | return 'jms/serializer'; 47 | } 48 | 49 | public function getNote(): string 50 | { 51 | return <<<'NOTE' 52 | Serialize object graphs with a minimal set of handlers 53 | NOTE; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Bench/JsonSerializableBenchmark.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class JsonSerializableBenchmark extends AbstractBench 13 | { 14 | public function initSerializer(): void 15 | { 16 | 17 | } 18 | 19 | public function serialize($data): void 20 | { 21 | json_encode( 22 | $data 23 | ); 24 | } 25 | 26 | public function getPackageName(): string 27 | { 28 | return 'php/json-serializable'; 29 | } 30 | 31 | public function getNote(): string 32 | { 33 | return <<<'NOTE' 34 | Serialize object graphs using the native JsonSerializable interface 35 | NOTE; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Bench/SerializardClosureBenchmark.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class SerializardClosureBenchmark extends AbstractBench 22 | { 23 | /** 24 | * @var Serializard 25 | */ 26 | private $serializer; 27 | 28 | public function initSerializer(): void 29 | { 30 | $formats = new FormatContainer(); 31 | $formats->add('json', new JsonFormat()); 32 | 33 | $normalizers = new FallbackNormalizerContainer(); 34 | $normalizers->add(Forum::class, function (Forum $forum) { 35 | return [ 36 | 'id' => $forum->getId(), 37 | 'name' => $forum->getName(), 38 | 'threads' => $forum->getThreads(), 39 | 'category' => $forum->getCategory(), 40 | 'createdAt' => $forum->getCreatedAt(), 41 | 'updatedAt' => $forum->getUpdatedAt(), 42 | ]; 43 | }); 44 | $normalizers->add(Thread::class, function (Thread $thread) { 45 | return [ 46 | 'id' => $thread->getId(), 47 | 'popularity' => $thread->getPopularity(), 48 | 'title' => $thread->getTitle(), 49 | 'comments' => $thread->getComments(), 50 | 'description' => $thread->getDescription(), 51 | 'createdAt' => $thread->getCreatedAt(), 52 | 'updatedAt' => $thread->getUpdatedAt(), 53 | ]; 54 | }); 55 | $normalizers->add(Comment::class, function (Comment $comment) { 56 | return [ 57 | 'id' => $comment->getId(), 58 | 'content' => $comment->getContent(), 59 | 'author' => $comment->getAuthor(), 60 | 'createdAt' => $comment->getCreatedAt(), 61 | 'updatedAt' => $comment->getUpdatedAt(), 62 | ]; 63 | }); 64 | $normalizers->add(\DateTimeImmutable::class, function (\DateTimeImmutable $dt) { 65 | return $dt->format(\DATE_ATOM); 66 | }); 67 | $normalizers->add(Category::class, function (Category $category) { 68 | return [ 69 | 'id' => $category->getId(), 70 | 'parent' => $category->getParent(), 71 | 'children' => $category->getChildren(), 72 | 'createdAt' => $category->getCreatedAt(), 73 | 'updatedAt' => $category->getUpdatedAt(), 74 | ]; 75 | }); 76 | 77 | $hydrators = new FallbackHydratorContainer(); 78 | 79 | $this->serializer = new Serializard($formats, $normalizers, $hydrators); 80 | } 81 | 82 | public function serialize(Forum $data): void 83 | { 84 | $this->serializer->serialize($data, 'json'); 85 | } 86 | 87 | public function getPackageName(): string 88 | { 89 | return 'thunderer/serializard'; 90 | } 91 | 92 | public function getNote(): string 93 | { 94 | return <<<'NOTE' 95 | Serialize object graphs using closures as custom normalizers 96 | NOTE; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Bench/SerializardReflectionBenchmark.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class SerializardReflectionBenchmark extends AbstractBench 23 | { 24 | /** 25 | * @var Serializard 26 | */ 27 | private $serializer; 28 | 29 | public function initSerializer(): void 30 | { 31 | $formats = new FormatContainer(); 32 | $formats->add('json', new JsonFormat()); 33 | 34 | $normalizers = new FallbackNormalizerContainer(); 35 | $normalizers->add(Forum::class, new ReflectionNormalizer()); 36 | $normalizers->add(Thread::class, new ReflectionNormalizer()); 37 | $normalizers->add(Comment::class, new ReflectionNormalizer()); 38 | $normalizers->add(Category::class, new ReflectionNormalizer()); 39 | $normalizers->add(\DateTimeImmutable::class, function (\DateTimeImmutable $dt) { 40 | return $dt->format(\DATE_ATOM); 41 | }); 42 | 43 | $hydrators = new FallbackHydratorContainer(); 44 | 45 | $this->serializer = new Serializard($formats, $normalizers, $hydrators); 46 | } 47 | 48 | public function serialize(Forum $data): void 49 | { 50 | $this->serializer->serialize($data, 'json'); 51 | } 52 | 53 | public function getPackageName(): string 54 | { 55 | return 'thunderer/serializard'; 56 | } 57 | 58 | public function getNote(): string 59 | { 60 | return <<<'NOTE' 61 | Serialize object graphs extracting its value through property reflection 62 | NOTE; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Bench/SymfonyGetSetNormalizerBenchmark.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class SymfonyGetSetNormalizerBenchmark extends AbstractBench 23 | { 24 | /** 25 | * @var Serializer 26 | */ 27 | private $serializer; 28 | 29 | public function initSerializer(): void 30 | { 31 | $classMetadataFactory = new CacheClassMetadataFactory( 32 | new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())), 33 | new ApcuAdapter('SymfonyMetadata') 34 | ); 35 | 36 | $this->serializer = new Serializer( 37 | [new DateTimeNormalizer(), new GetSetMethodNormalizer($classMetadataFactory)], 38 | [new JsonEncoder()] 39 | ); 40 | } 41 | 42 | public function serialize(Forum $data): void 43 | { 44 | $this->serializer->serialize($data, 'json'); 45 | } 46 | 47 | public function getPackageName(): string 48 | { 49 | return 'symfony/serializer'; 50 | } 51 | 52 | public function getNote(): string 53 | { 54 | return <<<'NOTE' 55 | Serialize object graphs extracting the property values through its getter method 56 | NOTE; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Bench/SymfonyObjectNormalizerBenchmark.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class SymfonyObjectNormalizerBenchmark extends AbstractBench 24 | { 25 | /** 26 | * @var Serializer 27 | */ 28 | private $serializer; 29 | 30 | public function initSerializer(): void 31 | { 32 | $classMetadataFactory = new CacheClassMetadataFactory( 33 | new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())), 34 | new ApcuAdapter('SymfonyMetadata') 35 | ); 36 | 37 | $propertyAccessor = PropertyAccess::createPropertyAccessorBuilder() 38 | ->setCacheItemPool(new ApcuAdapter('SymfonyPropertyAccessor')) 39 | ->getPropertyAccessor(); 40 | 41 | $this->serializer = new Serializer( 42 | [new DateTimeNormalizer(), new ObjectNormalizer($classMetadataFactory, null, $propertyAccessor)], 43 | [new JsonEncoder()] 44 | ); 45 | } 46 | 47 | public function serialize(Forum $data): void 48 | { 49 | $this->serializer->serialize($data, 'json'); 50 | } 51 | 52 | public function getPackageName(): string 53 | { 54 | return 'symfony/serializer'; 55 | } 56 | 57 | public function getNote(): string 58 | { 59 | return <<<'NOTE' 60 | Serialize object graphs using Symfony Property Access component 61 | NOTE; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Bench/TSantosBenchmark.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class TSantosBenchmark extends AbstractBench 21 | { 22 | /** 23 | * @var \TSantos\Serializer\Serializer 24 | */ 25 | private $serializer; 26 | 27 | public function initSerializer(): void 28 | { 29 | if (!file_exists($cache = __DIR__. '/../../cache/hydrators')) { 30 | mkdir($cache); 31 | } 32 | 33 | $this->serializer = (new SerializerBuilder()) 34 | ->setMetadataDriver(new AnnotationDriver(new AnnotationReader(), new ApcuCache(), false)) 35 | ->setHydratorDir($cache) 36 | ->setMetadataCache(new PsrCacheAdapter('TSantosMetadata', new ApcuAdapter())) 37 | ->enableBuiltInNormalizers() 38 | ->setDebug(false) 39 | ->build(); 40 | } 41 | 42 | public function serialize(Forum $data): void 43 | { 44 | $this->serializer->serialize($data); 45 | } 46 | 47 | public function getPackageName(): string 48 | { 49 | return 'tsantos/serializer'; 50 | } 51 | 52 | public function getNote(): string 53 | { 54 | return <<<'NOTE' 55 | Serializes an object graphs using generated data extractor for each class 56 | NOTE; 57 | 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/BenchExtension.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class BenchExtension implements ExtensionInterface 13 | { 14 | public function load(Container $container) 15 | { 16 | $container->register('ivory_benchmark.bench_finder', function (Container $container) { 17 | return new BenchFinder( 18 | $container->get('benchmark.benchmark_finder'), 19 | $container->getParameter('path') 20 | ); 21 | }); 22 | 23 | $container->register('ivory_benchmark.info_command', function (Container $container) { 24 | return new InfoCommand($container->get('ivory_benchmark.bench_finder')); 25 | }, ['console.command' => []]); 26 | } 27 | 28 | public function getDefaultConfig() 29 | { 30 | return []; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/BenchFinder.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class BenchFinder 14 | { 15 | const DEFAULT_VERSION = 'N/A'; 16 | 17 | /** 18 | * @var \PhpBench\Benchmark\BenchmarkFinder 19 | */ 20 | private $internalFinder; 21 | 22 | /** 23 | * @var string 24 | */ 25 | private $path; 26 | 27 | /** 28 | * BenchmarkFinder constructor. 29 | * @param \PhpBench\Benchmark\BenchmarkFinder $internalFinder 30 | * @param string $path 31 | */ 32 | public function __construct(\PhpBench\Benchmark\BenchmarkFinder $internalFinder, string $path) 33 | { 34 | $this->internalFinder = $internalFinder; 35 | $this->path = $path; 36 | } 37 | 38 | public function findAll(): array 39 | { 40 | $benchmarks = []; 41 | 42 | /** @var BenchmarkMetadata $metadata */ 43 | foreach ($this->internalFinder->findBenchmarks($this->path) as $metadata) { 44 | $data = $this->createBenchData($metadata); 45 | $benchmarks[$data['package']][] = $this->createBenchData($metadata); 46 | } 47 | 48 | return $benchmarks; 49 | } 50 | 51 | public function findOne(string $name): array 52 | { 53 | $benchmarksFound = $this->internalFinder->findBenchmarks($this->path, [$name]); 54 | 55 | if (empty($benchmarksFound)) { 56 | throw new \InvalidArgumentException('There is no benchmark with name "' . $name . '"'); 57 | } 58 | 59 | $bench = current($benchmarksFound); 60 | 61 | return $this->createBenchData($bench); 62 | } 63 | 64 | private function createBenchData(BenchmarkMetadata $metadata): array 65 | { 66 | $ref = new \ReflectionClass($metadata->getClass()); 67 | 68 | $bench = $ref->newInstance(); 69 | 70 | try { 71 | $versionParts = explode('@', Versions::getVersion($bench->getPackageName())); 72 | $version = $versionParts[0]; 73 | } catch (\OutOfBoundsException $boundsException) { 74 | $version = self::DEFAULT_VERSION; 75 | } 76 | 77 | return [ 78 | 'bench' => $bench, 79 | 'name' => $ref->getShortName(), 80 | 'package' => $bench->getPackageName(), 81 | 'version' => $version, 82 | 'note' => $bench->getNote() 83 | ]; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Command/InfoCommand.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class InfoCommand extends Command 18 | { 19 | /** 20 | * @var BenchFinder 21 | */ 22 | private $finder; 23 | 24 | /** 25 | * InfoCommand constructor. 26 | * @param BenchFinder $finder 27 | */ 28 | public function __construct(BenchFinder $finder) 29 | { 30 | parent::__construct(); 31 | $this->finder = $finder; 32 | } 33 | 34 | protected function configure() 35 | { 36 | $this 37 | ->setName('info') 38 | ->addArgument('serializer', InputArgument::OPTIONAL, 'Serializer name to inspect') 39 | ->setDescription('Show information about available serializers.') 40 | ->setHelp( 41 | 'The %command.name% displays detailed information about a serializer (e.g. version, capabilities and notes), 42 | or lists all serialized available' 43 | ); 44 | } 45 | 46 | protected function execute(InputInterface $input, OutputInterface $output) 47 | { 48 | if (null === $benchmark = $input->getArgument('serializer')) { 49 | $this->displayAll($output); 50 | return; 51 | } 52 | 53 | $this->displayOne($output, $benchmark); 54 | } 55 | 56 | private function displayAll(OutputInterface $output): void 57 | { 58 | $rows = []; 59 | 60 | foreach ($this->finder->findAll() as $benchmarks) { 61 | foreach ($benchmarks as $i => $benchmark) { 62 | $row = [ 63 | $benchmark['name'], 64 | ]; 65 | 66 | if ($i === 0) { 67 | $row[] = new TableCell($benchmark['version'], ['rowspan' => count($benchmarks)]); 68 | } 69 | 70 | $row[] = $benchmark['note']; 71 | $rows[] = $row; 72 | } 73 | 74 | $rows[] = new TableSeparator(); 75 | } 76 | 77 | array_pop($rows); 78 | 79 | $table= new Table($output); 80 | $table->setHeaders(['Benchmark', 'Version', 'Note']); 81 | $table->setRows($rows); 82 | $table->render(); 83 | } 84 | 85 | private function displayOne(OutputInterface $output, string $benchmark): void 86 | { 87 | $foundBenchmark = $this->finder->findOne($benchmark); 88 | $output->writeln(sprintf('name : %s', $foundBenchmark['name'])); 89 | $output->writeln(sprintf('package : %s', $foundBenchmark['package'])); 90 | $output->writeln(sprintf('version : %s', $foundBenchmark['version'])); 91 | $output->writeln(sprintf('note : %s', $foundBenchmark['note'])); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/DataProvider.php: -------------------------------------------------------------------------------- 1 | setCategory($this->createCategory($verticalComplexity)); 19 | 20 | for ($i = 0; $i < $horizontalComplexity * 2; ++$i) { 21 | $forum->addThread($this->createThread($i, $horizontalComplexity)); 22 | } 23 | 24 | return $forum; 25 | } 26 | 27 | private function createCategory(int $verticalComplexity = 1): Category 28 | { 29 | $original = $category = new Category(1); 30 | 31 | for ($i = 0; $i < $verticalComplexity * 2; ++$i) { 32 | $category->setParent($parent = new Category($i + 1)); 33 | $category = $parent; 34 | } 35 | 36 | return $original; 37 | } 38 | 39 | private function createThread(int $index, int $horizontalComplexity = 1): Thread 40 | { 41 | $thread = new Thread($index, 'Great thread ' . $index . '!', 'Great description ' . $index, $index / 100); 42 | 43 | for ($i = 0; $i < $horizontalComplexity * 5; ++$i) { 44 | $thread->addComment($this->createComment($index * $i + $i)); 45 | } 46 | 47 | return $thread; 48 | } 49 | 50 | private function createComment(int $index): Comment 51 | { 52 | return new Comment($index, 'Great comment ' . $index . '!'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Model/Category.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Category implements \JsonSerializable 15 | { 16 | use TimestampableTrait; 17 | 18 | /** 19 | * @Ivory\Type("int") 20 | * @Jms\Type("integer") 21 | * @TSantos\Type("integer") 22 | * 23 | * @var int 24 | */ 25 | private $id; 26 | 27 | /** 28 | * @Ivory\Type("PhpSerializers\Benchmarks\Model\Category") 29 | * @Jms\Type("PhpSerializers\Benchmarks\Model\Category") 30 | * @TSantos\Type("PhpSerializers\Benchmarks\Model\Category") 31 | * 32 | * @var Category|null 33 | */ 34 | private $parent; 35 | 36 | /** 37 | * @Ivory\Type("array") 38 | * @Jms\Type("array") 39 | * @TSantos\Type("PhpSerializers\Benchmarks\Model\Category[]") 40 | * 41 | * @var Category[] 42 | */ 43 | private $children = []; 44 | 45 | /** 46 | * @param int $id 47 | * @param Category|null $parent 48 | * @param Category[] $children 49 | */ 50 | public function __construct(int $id, Category $parent = null, array $children = []) 51 | { 52 | $this->setId($id); 53 | $this->setParent($parent); 54 | $this->setChildren($children); 55 | $this->initializeTimestampable(); 56 | } 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getId(): int 62 | { 63 | return $this->id; 64 | } 65 | 66 | /** 67 | * @param int $id 68 | */ 69 | public function setId(int $id) 70 | { 71 | $this->id = $id; 72 | } 73 | 74 | /** 75 | * @return Category|null 76 | */ 77 | public function getParent(): ?Category 78 | { 79 | return $this->parent; 80 | } 81 | 82 | /** 83 | * @param Category|null $parent 84 | */ 85 | public function setParent(Category $parent = null) 86 | { 87 | $this->parent = $parent; 88 | } 89 | 90 | /** 91 | * @return Category[] 92 | */ 93 | public function getChildren(): array 94 | { 95 | return $this->children; 96 | } 97 | 98 | /** 99 | * @param Category[] $children 100 | */ 101 | public function setChildren(array $children) 102 | { 103 | $this->children = $children; 104 | } 105 | 106 | /** 107 | * @param Category $child 108 | */ 109 | public function addChild(Category $child) 110 | { 111 | $this->children[] = $child; 112 | } 113 | 114 | /** 115 | * {@inheritdoc} 116 | */ 117 | public function jsonSerialize() 118 | { 119 | return [ 120 | 'id' => $this->id, 121 | 'parent' => $this->parent, 122 | 'children' => $this->children, 123 | 'createdAt' => $this->createdAt instanceof \DateTimeInterface ? $this->createdAt->format(\DateTime::ATOM) : null, 124 | 'updatedAt' => $this->updatedAt instanceof \DateTimeInterface ? $this->updatedAt->format(\DateTime::ATOM) : null, 125 | ]; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Model/Comment.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Comment implements \JsonSerializable 15 | { 16 | use TimestampableTrait; 17 | 18 | /** 19 | * @Ivory\Type("int") 20 | * @Jms\Type("integer") 21 | * @TSantos\Type("integer") 22 | * 23 | * @var int 24 | */ 25 | private $id; 26 | 27 | /** 28 | * @Ivory\Type("string") 29 | * @Jms\Type("string") 30 | * @TSantos\Type("string") 31 | * 32 | * @var string 33 | */ 34 | private $content; 35 | 36 | /** 37 | * @Ivory\Type("PhpSerializers\Benchmarks\Model\User") 38 | * @Jms\Type("PhpSerializers\Benchmarks\Model\User") 39 | * @TSantos\Type("PhpSerializers\Benchmarks\Model\User") 40 | * 41 | * @var User 42 | */ 43 | private $author; 44 | 45 | /** 46 | * @param int $id 47 | * @param string $content 48 | * @param User|null $author 49 | */ 50 | public function __construct(int $id, string $content, User $author = null) 51 | { 52 | $this->setId($id); 53 | $this->setContent($content); 54 | $this->setAuthor($author); 55 | $this->initializeTimestampable(); 56 | } 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getId(): int 62 | { 63 | return $this->id; 64 | } 65 | 66 | /** 67 | * @param int $id 68 | */ 69 | public function setId(int $id) 70 | { 71 | $this->id = $id; 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getContent(): string 78 | { 79 | return $this->content; 80 | } 81 | 82 | /** 83 | * @param string $content 84 | */ 85 | public function setContent(string $content) 86 | { 87 | $this->content = $content; 88 | } 89 | 90 | /** 91 | * @return User|null 92 | */ 93 | public function getAuthor(): ?User 94 | { 95 | return $this->author; 96 | } 97 | 98 | /** 99 | * @param User|null $author 100 | */ 101 | public function setAuthor(User $author = null) 102 | { 103 | $this->author = $author; 104 | } 105 | 106 | /** 107 | * {@inheritdoc} 108 | */ 109 | public function jsonSerialize() 110 | { 111 | return [ 112 | 'id' => $this->id, 113 | 'content' => $this->content, 114 | 'author' => $this->author, 115 | 'createdAt' => $this->createdAt instanceof \DateTimeInterface ? $this->createdAt->format(\DateTime::ATOM) : null, 116 | 'updatedAt' => $this->updatedAt instanceof \DateTimeInterface ? $this->updatedAt->format(\DateTime::ATOM) : null, 117 | ]; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Model/Forum.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Forum implements \JsonSerializable 15 | { 16 | use TimestampableTrait; 17 | 18 | /** 19 | * @Ivory\Type("int") 20 | * @Jms\Type("integer") 21 | * @TSantos\Type("integer") 22 | * 23 | * @var int 24 | */ 25 | private $id; 26 | 27 | /** 28 | * @Ivory\Type("string") 29 | * @Jms\Type("string") 30 | * @TSantos\Type("string") 31 | * 32 | * @var string 33 | */ 34 | private $name; 35 | 36 | /** 37 | * @Ivory\Type("PhpSerializers\Benchmarks\Model\Category") 38 | * @Jms\Type("PhpSerializers\Benchmarks\Model\Category") 39 | * @TSantos\Type("PhpSerializers\Benchmarks\Model\Category") 40 | * 41 | * @var Category 42 | */ 43 | private $category; 44 | 45 | /** 46 | * @Ivory\Type("array") 47 | * @Jms\Type("array") 48 | * @TSantos\Type("PhpSerializers\Benchmarks\Model\Thread[]") 49 | * 50 | * @var Thread[] 51 | */ 52 | private $threads; 53 | 54 | /** 55 | * @param int $id 56 | * @param string $name 57 | * @param Category|null $category 58 | * @param Thread[] $threads 59 | */ 60 | public function __construct(int $id, string $name, Category $category = null, array $threads = []) 61 | { 62 | $this->setId($id); 63 | $this->setName($name); 64 | $this->setThreads($threads); 65 | $this->initializeTimestampable(); 66 | } 67 | 68 | /** 69 | * @return int 70 | */ 71 | public function getId(): int 72 | { 73 | return $this->id; 74 | } 75 | 76 | /** 77 | * @param int $id 78 | */ 79 | public function setId(int $id) 80 | { 81 | $this->id = $id; 82 | } 83 | 84 | /** 85 | * @return string 86 | */ 87 | public function getName(): string 88 | { 89 | return $this->name; 90 | } 91 | 92 | /** 93 | * @param string $name 94 | */ 95 | public function setName(string $name) 96 | { 97 | $this->name = $name; 98 | } 99 | 100 | /** 101 | * @return Category 102 | */ 103 | public function getCategory(): Category 104 | { 105 | return $this->category; 106 | } 107 | 108 | /** 109 | * @param Category|null $category 110 | */ 111 | public function setCategory(Category $category = null) 112 | { 113 | $this->category = $category; 114 | } 115 | 116 | /** 117 | * @return Thread[] 118 | */ 119 | public function getThreads(): array 120 | { 121 | return $this->threads; 122 | } 123 | 124 | /** 125 | * @param Thread[] $threads 126 | */ 127 | public function setThreads(array $threads) 128 | { 129 | $this->threads = $threads; 130 | } 131 | 132 | /** 133 | * @param Thread $thread 134 | */ 135 | public function addThread(Thread $thread) 136 | { 137 | $this->threads[] = $thread; 138 | } 139 | 140 | /** 141 | * {@inheritdoc} 142 | */ 143 | public function jsonSerialize() 144 | { 145 | return [ 146 | 'id' => $this->id, 147 | 'name' => $this->name, 148 | 'category' => $this->category, 149 | 'threads' => $this->threads, 150 | 'createdAt' => $this->createdAt instanceof \DateTimeInterface ? $this->createdAt->format(\DateTime::ATOM) : null, 151 | 'updatedAt' => $this->updatedAt instanceof \DateTimeInterface ? $this->updatedAt->format(\DateTime::ATOM) : null, 152 | ]; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Model/Thread.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Thread implements \JsonSerializable 15 | { 16 | use TimestampableTrait; 17 | 18 | /** 19 | * @Ivory\Type("int") 20 | * @Jms\Type("integer") 21 | * @TSantos\Type("integer") 22 | * 23 | * @var int 24 | */ 25 | private $id; 26 | 27 | /** 28 | * @Ivory\Type("string") 29 | * @Jms\Type("string") 30 | * @TSantos\Type("string") 31 | * 32 | * @var string 33 | */ 34 | private $title; 35 | 36 | /** 37 | * @Ivory\Type("string") 38 | * @Jms\Type("string") 39 | * @TSantos\Type("string") 40 | * 41 | * @var string 42 | */ 43 | private $description; 44 | 45 | /** 46 | * @Ivory\Type("float") 47 | * @Jms\Type("float") 48 | * @TSantos\Type("float") 49 | * 50 | * @var float 51 | */ 52 | private $popularity; 53 | 54 | /** 55 | * @Ivory\Type("array") 56 | * @Jms\Type("array") 57 | * @TSantos\Type("PhpSerializers\Benchmarks\Model\Comment[]") 58 | * 59 | * @var Comment[] 60 | */ 61 | private $comments; 62 | 63 | /** 64 | * @param int $id 65 | * @param string $title 66 | * @param string $description 67 | * @param float $popularity 68 | * @param Comment[] $comments 69 | */ 70 | public function __construct(int $id, string $title, string $description, float $popularity, array $comments = []) 71 | { 72 | $this->setId($id); 73 | $this->setTitle($title); 74 | $this->setDescription($description); 75 | $this->setPopularity($popularity); 76 | $this->setComments($comments); 77 | $this->initializeTimestampable(); 78 | } 79 | 80 | /** 81 | * @return int 82 | */ 83 | public function getId(): int 84 | { 85 | return $this->id; 86 | } 87 | 88 | /** 89 | * @param int $id 90 | */ 91 | public function setId(int $id) 92 | { 93 | $this->id = $id; 94 | } 95 | 96 | /** 97 | * @return string 98 | */ 99 | public function getTitle(): string 100 | { 101 | return $this->title; 102 | } 103 | 104 | /** 105 | * @param string $title 106 | */ 107 | public function setTitle(string $title) 108 | { 109 | $this->title = $title; 110 | } 111 | 112 | /** 113 | * @return string 114 | */ 115 | public function getDescription(): string 116 | { 117 | return $this->description; 118 | } 119 | 120 | /** 121 | * @param string $description 122 | */ 123 | public function setDescription(string $description) 124 | { 125 | $this->description = $description; 126 | } 127 | 128 | /** 129 | * @return float 130 | */ 131 | public function getPopularity(): float 132 | { 133 | return $this->popularity; 134 | } 135 | 136 | /** 137 | * @param float $popularity 138 | */ 139 | public function setPopularity(float $popularity) 140 | { 141 | $this->popularity = $popularity; 142 | } 143 | 144 | /** 145 | * @return Comment[] 146 | */ 147 | public function getComments(): array 148 | { 149 | return $this->comments; 150 | } 151 | 152 | /** 153 | * @param Comment[] $comments 154 | */ 155 | public function setComments(array $comments) 156 | { 157 | $this->comments = $comments; 158 | } 159 | 160 | /** 161 | * @param Comment $comment 162 | */ 163 | public function addComment(Comment $comment) 164 | { 165 | $this->comments[] = $comment; 166 | } 167 | 168 | /** 169 | * {@inheritdoc} 170 | */ 171 | public function jsonSerialize() 172 | { 173 | return [ 174 | 'id' => $this->id, 175 | 'title' => $this->title, 176 | 'description' => $this->description, 177 | 'popularity' => $this->popularity, 178 | 'comments' => $this->comments, 179 | 'createdAt' => $this->createdAt instanceof \DateTimeInterface ? $this->createdAt->format(\DateTime::ATOM) : null, 180 | 'updatedAt' => $this->updatedAt instanceof \DateTimeInterface ? $this->updatedAt->format(\DateTime::ATOM) : null, 181 | ]; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/Model/TimestampableTrait.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | trait TimestampableTrait 11 | { 12 | /** 13 | * @Ivory\Type("DateTimeImmutable") 14 | * 15 | * @var \DateTimeImmutable 16 | */ 17 | private $createdAt; 18 | 19 | /** 20 | * @Ivory\Type("DateTime") 21 | * 22 | * @var \DateTime|null 23 | */ 24 | private $updatedAt; 25 | 26 | /** 27 | * @return \DateTimeImmutable 28 | */ 29 | public function getCreatedAt(): \DateTimeImmutable 30 | { 31 | return $this->createdAt; 32 | } 33 | 34 | /** 35 | * @param \DateTimeImmutable $createdAt 36 | */ 37 | public function setCreatedAt(\DateTimeImmutable $createdAt) 38 | { 39 | $this->createdAt = $createdAt; 40 | } 41 | 42 | /** 43 | * @return \DateTime|null 44 | */ 45 | public function getUpdatedAt(): ?\DateTime 46 | { 47 | return $this->updatedAt; 48 | } 49 | 50 | /** 51 | * @param \DateTime $updatedAt 52 | */ 53 | public function setUpdatedAt(\DateTime $updatedAt) 54 | { 55 | $this->updatedAt = $updatedAt; 56 | } 57 | 58 | private function initializeTimestampable() 59 | { 60 | $this->createdAt = new \DateTimeImmutable(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Model/User.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class User implements \JsonSerializable 15 | { 16 | use TimestampableTrait; 17 | 18 | /** 19 | * @Ivory\Type("int") 20 | * @Jms\Type("integer") 21 | * @TSantos\Type("integer") 22 | * 23 | * @var int 24 | */ 25 | private $id; 26 | 27 | /** 28 | * @Ivory\Type("string") 29 | * @Jms\Type("string") 30 | * @TSantos\Type("string") 31 | * 32 | * @var string 33 | */ 34 | private $firstname; 35 | 36 | /** 37 | * @Ivory\Type("string") 38 | * @Jms\Type("string") 39 | * @TSantos\Type("string") 40 | * 41 | * @var string 42 | */ 43 | private $lastname; 44 | 45 | /** 46 | * @Ivory\Type("bool") 47 | * @Jms\Type("boolean") 48 | * @TSantos\Type("boolean") 49 | * 50 | * @var bool 51 | */ 52 | private $newsletter; 53 | 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function jsonSerialize() 59 | { 60 | return [ 61 | 'id' => $this->id, 62 | 'firstname' => $this->firstname, 63 | 'lastname' => $this->lastname, 64 | 'newsletter' => $this->newsletter, 65 | 'createdAt' => $this->createdAt instanceof \DateTimeInterface ? $this->createdAt->format(\DateTime::ATOM) : null, 66 | 'updatedAt' => $this->updatedAt instanceof \DateTimeInterface ? $this->updatedAt->format(\DateTime::ATOM) : null, 67 | ]; 68 | } 69 | } 70 | --------------------------------------------------------------------------------