diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b4ab685 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +.gitignore +.dockerignore +Dockerfile +docker-compose.yml +Makefile +rendered/ +vendor/ +*.md diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..b0d2dc0 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,59 @@ +# Docker Environment for Odin + +This document describes how to use the Docker environment set up for the Odin Celestial Planet Generator. + +## Requirements + +- Docker +- Docker Compose + +## Getting Started + +The project includes a Makefile with shortcuts for common operations: + +```bash +# Show all available commands +make help + +# Build the Docker image +make build + +# Start the Docker container +make up + +# Open a shell in the Docker container +make shell + +# Run the tests +make test + +# Generate an example planet image in the rendered directory +make example + +# Stop the Docker container +make down + +# Clean the rendered directory +make clean +``` + +## Configuration + +- Images will be rendered in the `rendered` directory +- PHP with GD extension is configured in the Docker environment +- The environment variable `ODIN_RENDER_DIR` is set to `/app/rendered` + +## Custom Usage + +You can also use Docker Compose commands directly: + +```bash +# Build and start the container +docker-compose up -d + +# Execute a PHP script +docker-compose exec odin php your-script.php + +# Stop and remove the container +docker-compose down +``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..495547b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM php:8.3-cli + +# Install dependencies +RUN apt-get update && apt-get install -y \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + git \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +# Configure and install GD extension +RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j$(nproc) gd + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /app + +# Copy project files +COPY . /app/ + +# Install dependencies +RUN composer --ignore-platform-req=ext-gd -W update symfony/filesystem phpspec/phpspec +RUN composer install --no-interaction --no-progress + +# Create rendered directory +RUN mkdir -p /app/rendered + +# Set permissions +RUN chmod -R 777 /app/rendered + +# Set environment variable for rendered directory +ENV ODIN_RENDER_DIR=/app/rendered + +CMD ["php", "-a"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b4a4437 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +.PHONY: build up down shell test clean help + +# Colors for terminal output +YELLOW=\033[0;33m +GREEN=\033[0;32m +NC=\033[0m # No Color + +help: ## Show this help + @echo "" + @echo "${YELLOW}Odin - The Celestial Planet Generator${NC}" + @echo "${YELLOW}====================================${NC}" + @echo "" + @echo "Available commands:" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "${GREEN}make %-15s${NC} %s\n", $$1, $$2}' + @echo "" + +build: ## Build the Docker image + docker compose build + +up: ## Start the Docker container + docker compose up -d + +down: ## Stop the Docker container + docker compose down + +shell: ## Open a shell in the Docker container + docker compose exec odin bash + +test: ## Run the tests + docker compose exec odin ./vendor/bin/phpspec run + +example: ## Generate an example planet image + docker compose exec odin php examples.php + +clean: ## Remove all generated images + rm -rf rendered/* + mkdir -p rendered + @echo "Rendered directory cleaned" diff --git a/README.md b/README.md index 0a3ad24..4314117 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Odin aims to render randomly generated planets, moons and star fields as PNG ima Here is an picture entirely generated by Odin: ![](example.png) +To discover how to use Odin, you can check the [examples](examples.php) files. Launch the `make example` command to generate various celestial objects in the `rendered` directory. + ## Render a planet ```php diff --git a/composer.json b/composer.json index 1303ed1..adf87a6 100644 --- a/composer.json +++ b/composer.json @@ -3,10 +3,10 @@ "type": "library", "description": "Odin - The Celestial Planet Generator", "require": { - "php": ">=7.1.0", + "php": ">=8.3.0", "ext-gd": "@stable", "a1essandro/perlin-noise": "@stable", - "symfony/filesystem": "^4.1" + "symfony/filesystem": "^7.3" }, "autoload": { "psr-4": { @@ -25,6 +25,6 @@ } ], "require-dev": { - "phpspec/phpspec": "^5.1" + "phpspec/phpspec": "^7.6" } } diff --git a/composer.lock b/composer.lock index adf3f61..f65a6ae 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0bbf73819eb476c07e3c80b82e565796", + "content-hash": "921da3a16bec10975a6684c88522ef01", "packages": [ { "name": "a1essandro/perlin-noise", @@ -44,32 +44,35 @@ } ], "description": "Algorithm for generating heightmaps", + "support": { + "issues": "https://github.com/A1essandro/perlin-noise-generator/issues", + "source": "https://github.com/A1essandro/perlin-noise-generator/tree/master" + }, "time": "2016-05-15T11:24:01+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.7", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd7bd6535beb1f0a0a9e3ee960666d0598546981", - "reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } + "require-dev": { + "symfony/process": "^6.4|^7.0" }, + "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" @@ -92,56 +95,77 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "time": "2018-10-30T13:18:25+00:00" + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.10.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.9-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -152,40 +176,184 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" } ], "packages-dev": [ + { + "name": "doctrine/deprecations", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + }, + "time": "2025-04-07T20:06:18+00:00" + }, { "name": "doctrine/instantiator", - "version": "1.1.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^8.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -199,48 +367,61 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2017-07-22T11:58:36+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -262,44 +443,53 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "5.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -310,44 +500,58 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + }, + "time": "2025-04-13T19:20:35+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -360,20 +564,25 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" }, { "name": "phpspec/php-diff", - "version": "v1.1.0", + "version": "v1.1.3", "source": { "type": "git", "url": "https://github.com/phpspec/php-diff.git", - "reference": "0464787bfa7cd13576c5a1e318709768798bec6a" + "reference": "fc1156187f9f6c8395886fe85ed88a0a245d72e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/php-diff/zipball/0464787bfa7cd13576c5a1e318709768798bec6a", - "reference": "0464787bfa7cd13576c5a1e318709768798bec6a", + "url": "https://api.github.com/repos/phpspec/php-diff/zipball/fc1156187f9f6c8395886fe85ed88a0a245d72e9", + "reference": "fc1156187f9f6c8395886fe85ed88a0a245d72e9", "shasum": "" }, "type": "library", @@ -398,39 +607,46 @@ } ], "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", - "time": "2016-04-07T12:29:16+00:00" + "support": { + "source": "https://github.com/phpspec/php-diff/tree/v1.1.3" + }, + "time": "2020-09-18T13:47:07+00:00" }, { "name": "phpspec/phpspec", - "version": "5.1.0", + "version": "7.6.0", "source": { "type": "git", "url": "https://github.com/phpspec/phpspec.git", - "reference": "4badea737c34a6c8e2921fca0f6a1cbe4f724f2f" + "reference": "107b1597cef2db4d81702e51d159afcfde1b1247" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/phpspec/zipball/4badea737c34a6c8e2921fca0f6a1cbe4f724f2f", - "reference": "4badea737c34a6c8e2921fca0f6a1cbe4f724f2f", + "url": "https://api.github.com/repos/phpspec/phpspec/zipball/107b1597cef2db4d81702e51d159afcfde1b1247", + "reference": "107b1597cef2db4d81702e51d159afcfde1b1247", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.5", + "doctrine/instantiator": "^1.0.5 || ^2", "ext-tokenizer": "*", - "php": "^7.1, <7.4", + "php": "^7.3 || 8.0.* || 8.1.* || 8.2.* || 8.3.*", "phpspec/php-diff": "^1.0.0", - "phpspec/prophecy": "^1.7", - "sebastian/exporter": "^1.0 || ^2.0 || ^3.0", - "symfony/console": "^3.4 || ^4.0", - "symfony/event-dispatcher": "^3.4 || ^4.0", - "symfony/finder": "^3.4 || ^4.0", - "symfony/process": "^3.4 || ^4.0", - "symfony/yaml": "^3.4 || ^4.0" + "phpspec/prophecy": "^1.9", + "sebastian/exporter": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/finder": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/process": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0" + }, + "conflict": { + "sebastian/comparator": "<1.2.4" }, "require-dev": { "behat/behat": "^3.3", - "phpunit/phpunit": "^5.7 || ^6.0", - "symfony/filesystem": "^3.4 || ^4.0" + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "symfony/filesystem": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "^4.3 || ^5.2" }, "suggest": { "phpspec/nyan-formatters": "Adds Nyan formatters" @@ -441,7 +657,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1.x-dev" + "dev-main": "7.5.x-dev" } }, "autoload": { @@ -468,7 +684,7 @@ "homepage": "https://ciaranmcnulty.com/" } ], - "description": "Specification-oriented BDD framework for PHP 5.6+", + "description": "Specification-oriented BDD framework for PHP 7.1+", "homepage": "http://phpspec.net/", "keywords": [ "BDD", @@ -479,42 +695,48 @@ "testing", "tests" ], - "time": "2018-10-29T08:12:52+00:00" + "support": { + "issues": "https://github.com/phpspec/phpspec/issues", + "source": "https://github.com/phpspec/phpspec/tree/7.6.0" + }, + "time": "2025-05-03T10:03:36+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/35f1adb388946d92e6edab2aa2cb2b60e132ebd5", + "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.4 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "friendsofphp/php-cs-fixer": "^3.40", + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^2.1.13", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -537,159 +759,199 @@ "keywords": [ "Double", "Dummy", + "dev", "fake", "mock", "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.22.0" + }, + "time": "2025-04-29T14:58:06+00:00" }, { - "name": "sebastian/comparator", - "version": "3.0.2", + "name": "phpstan/phpdoc-parser", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", "shasum": "" }, "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" + }, + "time": "2025-07-13T07:04:09+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Container\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "comparator", - "compare", - "equality" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" }, { - "name": "sebastian/diff", - "version": "3.0.1", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "366541b989927187c4ca70490a35615d3fef2dce" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", - "reference": "366541b989927187c4ca70490a35615d3fef2dce", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0", - "symfony/process": "^2 || ^3.3 || ^4" + "php": ">=7.2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "Standard interfaces for event handling.", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "events", + "psr", + "psr-14" ], - "time": "2018-06-10T07:54:39+00:00" + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "sebastian/exporter", - "version": "3.1.0", + "name": "sebastian/comparator", + "version": "7.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f", + "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -702,6 +964,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -713,48 +979,209 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-06-17T07:41:58+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:42+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -767,65 +1194,79 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:01+00:00" }, { "name": "symfony/console", - "version": "v4.1.7", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "432122af37d8cd52fba1b294b11976e0d20df595" + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/432122af37d8cd52fba1b294b11976e0d20df595", - "reference": "432122af37d8cd52fba1b294b11976e0d20df595", + "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" }, "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, - "suggest": { - "psr/log-implementation": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" @@ -848,47 +1289,137 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T19:55:54+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", - "time": "2018-10-31T09:30:44+00:00" + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.7", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "552541dad078c85d9414b09c041ede488b456cd5" + "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/552541dad078c85d9414b09c041ede488b456cd5", - "reference": "552541dad078c85d9414b09c041ede488b456cd5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", + "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<3.4" + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" @@ -911,33 +1442,124 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", - "time": "2018-10-10T13:52:42+00:00" + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-22T09:11:45+00:00" }, { - "name": "symfony/finder", - "version": "v4.1.7", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "1f17195b44543017a9c9b2d437c670627e96ad06" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06", - "reference": "1f17195b44543017a9c9b2d437c670627e96ad06", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1", + "psr/event-dispatcher": "^1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "4.1-dev" + "dev-main": "3.6-dev" } }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" @@ -960,42 +1582,141 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "time": "2018-10-03T08:47:56+00:00" + "support": { + "source": "https://github.com/symfony/finder/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-30T19:00:26+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.2" }, "suggest": { - "ext-mbstring": "For best performance" + "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.9-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { "files": [ "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1012,43 +1733,218 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", + "intl", + "normalizer", "polyfill", "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v4.1.7", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3e83acef94d979b1de946599ef86b3a352abcdc9" + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3e83acef94d979b1de946599ef86b3a352abcdc9", - "reference": "3e83acef94d979b1de946599ef86b3a352abcdc9", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-17T09:11:12+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "4.1-dev" + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/string", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1060,51 +1956,72 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", - "time": "2018-10-14T20:48:13+00:00" + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-20T20:19:01+00:00" }, { "name": "symfony/yaml", - "version": "v4.1.7", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "367e689b2fdc19965be435337b50bc8adf2746c9" + "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/367e689b2fdc19965be435337b50bc8adf2746c9", - "reference": "367e689b2fdc19965be435337b50bc8adf2746c9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb", + "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<3.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^6.4|^7.0" }, + "bin": [ + "Resources/bin/yaml-lint" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" @@ -1127,35 +2044,56 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", - "time": "2018-10-02T16:36:10+00:00" + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-03T06:57:57+00:00" }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -1179,20 +2117,25 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "ext-gd": 0, - "a1essandro/perlin-noise": 0 + "a1essandro/perlin-noise": 0, + "ext-gd": 0 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.1.0", + "php": ">=8.3.0", "ext-gd": "@stable" }, - "platform-dev": [] + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2a31dee --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.7' + +services: + odin: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/app + - ./rendered:/app/rendered + - /app/vendor + environment: + - ODIN_RENDER_DIR=/app/rendered + tty: true diff --git a/examples.php b/examples.php new file mode 100644 index 0000000..d8bea3b --- /dev/null +++ b/examples.php @@ -0,0 +1,108 @@ + 'Lava Planet', + 'toxic' => 'Toxic Planet', + 'coldGaz' => 'Cold Gas Planet', + 'hotGaz' => 'Hot Gas Planet', + 'hydroGaz' => 'Hydro Gas Planet', + 'atoll' => 'Atoll Planet', + 'violet' => 'Violet Planet', + 'ashes' => 'Ashes Planet', + 'forest' => 'Forest Planet', +]; + +foreach ($planetTypes as $method => $description) { + echo " - Creating $description...\n"; + $planet = new Planet($config); + $planet->diameter(300)->$method(); + $file = $planet->render(); + echo " Generated: " . $file->getPathname() . "\n"; +} + +// STARS +echo "\nGenerating stars...\n"; + +$starTypes = [ + 'regular' => 'Regular Star', + 'redGiant' => 'Red Giant Star', + 'whiteDwarf' => 'White Dwarf Star', +]; + +foreach ($starTypes as $method => $description) { + echo " - Creating $description...\n"; + $star = new Star($config); + $star->diameter(300)->$method(); + $file = $star->render(); + echo " Generated: " . $file->getPathname() . "\n"; +} + +// MOON +echo "\nGenerating moon...\n"; +$moon = new Moon($config); +$moon->diameter(300); +$file = $moon->render(); +echo " Generated: " . $file->getPathname() . "\n"; + +// SYSTEM EXAMPLE +echo "\nGenerating a complete solar system...\n"; + +// Create a star +$systemStar = new Star($config); +$systemStar->diameter(500)->regular(); +$starFile = $systemStar->render(); +echo " Generated star: " . $starFile->getPathname() . "\n"; + +// Create some planets with different sizes +$planetSizes = [200, 150, 300, 250, 180]; +$planetMethods = array_keys($planetTypes); +for ($i = 0; $i < 5; $i++) { + $systemPlanet = new Planet($config); + $method = $planetMethods[$i % count($planetMethods)]; + $systemPlanet->diameter($planetSizes[$i])->$method(); + $planetFile = $systemPlanet->render(); + echo " Generated planet: " . $planetFile->getPathname() . "\n"; + + // Add a moon to some planets + if ($i % 2 == 0) { + $planetMoon = new Moon($config); + $planetMoon->diameter($planetSizes[$i] / 3); + $moonFile = $planetMoon->render(); + echo " With moon: " . $moonFile->getPathname() . "\n"; + } +} + +echo "\nAll examples generated successfully!\n"; +echo "Check the 'rendered' directory for the output images.\n"; diff --git a/src/Odin/Astronomical/Star/Star.php b/src/Odin/Astronomical/Star/Star.php new file mode 100644 index 0000000..c9eecaf --- /dev/null +++ b/src/Odin/Astronomical/Star/Star.php @@ -0,0 +1,204 @@ +type = $type; + + if (null !== $starSize) { + $this->starSize = $starSize; + } else { + $this->starSize = $this->makeEven(rand(150, 250)); + } + + $this->layerWidth = $this->starSize * 2; + $this->layerHeight = $this->starSize * 2; + } + + public function render() + { + $generatorRegistry = new StarSurfaceGeneratorRegistry(); + /** @var StarTypeInterface $surfaceGenerator */ + $surfaceGenerator = $generatorRegistry->forType($this->type); + + $layerOrchestrator = new LayerOrchestrator(); + $layerOrchestrator->initBaseLayer($this->layerWidth, $this->layerHeight, '#000', 127); + + // Add outer glow first (larger, more transparent) + $outerGlowSize = $this->makeEven(round($this->starSize * 2.0)); + $layerOrchestrator->addLayer( + $this->generateGlow( + $surfaceGenerator->getColorPalette(), + $outerGlowSize, + 0x22 + ), + ($this->layerWidth - $outerGlowSize) / 2, + ($this->layerHeight - $outerGlowSize) / 2 + ); + + // Add middle glow (medium size, medium opacity) + $middleGlowSize = $this->makeEven(round($this->starSize * 1.6)); + $layerOrchestrator->addLayer( + $this->generateGlow( + $surfaceGenerator->getColorPalette(), + $middleGlowSize, + 0x44 + ), + ($this->layerWidth - $middleGlowSize) / 2, + ($this->layerHeight - $middleGlowSize) / 2 + ); + + // Add inner glow (smaller, more intense) + $innerGlowSize = $this->makeEven(round($this->starSize * 1.3)); + $layerOrchestrator->addLayer( + $this->generateGlow( + $surfaceGenerator->getColorPalette(), + $innerGlowSize, + 0x88 + ), + ($this->layerWidth - $innerGlowSize) / 2, + ($this->layerHeight - $innerGlowSize) / 2 + ); + + $starLayers = new LayerOrchestrator(); + $starLayers->initBaseLayer($this->layerWidth, $this->layerHeight, '#000', 127); + $starLayer = $starLayers->render(); + + // Generate surface + $surface = $surfaceGenerator->generate($this->starSize); + $x = ($this->layerWidth / 2) - ($this->starSize / 2); + $y = ($this->layerHeight / 2) - ($this->starSize / 2); + $starLayers->addLayer($surface, $x, $y); + + $layerOrchestrator->addLayer($starLayer); + + $image = $layerOrchestrator->render(); + + // Add lens flare effect for more visual appeal + $this->addLensFlareEffect($image, $surfaceGenerator->getColorPalette()); + + // Add the star name with a more subtle appearance + $this->addStarName($image, $surfaceGenerator->getName()); + + $this->image = $image; + + return $image; + } + + private function generateGlow(array $palette, int $size, int $intensity) + { + $layerOrchestrator = new LayerOrchestrator(); + $layerOrchestrator->initBaseLayer($size, $size, '#000', 127); + $layer = $layerOrchestrator->render(); + + // Use the corona color for the glow if available, otherwise use core + $glowColor = $palette['corona'] ?? $palette['core']; + + $glow = new GradientAlpha($size, $size, 'ellipse', $glowColor, 0x00, $intensity, 0); + $layerOrchestrator->addLayer($glow->image, 0, 0); + + return $layer; + } + + private function addLensFlareEffect($image, array $palette) + { + // Extract color components from the core color + $coreColor = $palette['core']; + list($r, $g, $b) = sscanf($coreColor, "#%02x%02x%02x"); + + // Create small light streaks in 4-8 directions + $numStreaks = rand(4, 8); + $centerX = $this->layerWidth / 2; + $centerY = $this->layerHeight / 2; + $maxLength = $this->starSize * 0.7; + + for ($i = 0; $i < $numStreaks; $i++) { + $angle = (2 * M_PI / $numStreaks) * $i; + $length = $maxLength * (0.7 + (rand(0, 30) / 100)); + + $endX = $centerX + cos($angle) * $length; + $endY = $centerY + sin($angle) * $length; + + // Draw a line with decreasing opacity + $steps = 20; + for ($j = 0; $j < $steps; $j++) { + $t = $j / $steps; + $x = $centerX + ($endX - $centerX) * $t; + $y = $centerY + ($endY - $centerY) * $t; + + // Decrease opacity as we move away from center + $alpha = 100 * (1 - $t); + $color = imagecolorallocatealpha($image, $r, $g, $b, (int)(127 - ($alpha * 127 / 100))); + + // Draw a small point + $pointSize = 2 * (1 - $t); + imagefilledellipse($image, (int)$x, (int)$y, (int)$pointSize, (int)$pointSize, $color); + } + } + + // Add a few small random glints + for ($i = 0; $i < 5; $i++) { + $distance = rand($this->starSize / 2, $this->starSize); + $angle = rand(0, 360) * M_PI / 180; + + $x = $centerX + cos($angle) * $distance; + $y = $centerY + sin($angle) * $distance; + + $glintSize = rand(2, 5); + $glintColor = imagecolorallocatealpha($image, $r, $g, $b, rand(70, 100)); + + imagefilledellipse($image, (int)$x, (int)$y, $glintSize, $glintSize, $glintColor); + } + } + + private function addStarName($image, string $name) + { + // Add the name with a subtle glow effect + $textX = $this->layerWidth / 2 - (strlen($name) * 3); + $textY = ($this->layerHeight / 2 - $this->starSize / 2) - 15; + + // Add a subtle text shadow/glow + $shadowColor = imagecolorallocatealpha($image, 0, 0, 0, 80); + Text::write($image, $name, $textX + 1, $textY + 1, $shadowColor); + + // Write the actual text + Text::write($image, $name, $textX, $textY); + } + + // TODO: move to dedicated Math class + private function makeEven($number): int + { + $number = intval($number); + + if ($number % 2 === 0) { + return $number; + } + + return $number + 1; + } +} diff --git a/src/Odin/Astronomical/Star/Surface/AbstractStarType.php b/src/Odin/Astronomical/Star/Surface/AbstractStarType.php new file mode 100644 index 0000000..5bdd47c --- /dev/null +++ b/src/Odin/Astronomical/Star/Surface/AbstractStarType.php @@ -0,0 +1,63 @@ +initBaseLayer($size, $size, '#000', LayerOrchestrator::TRANSPARENT); + $surface = $layerOrchestrator->render(); + + $allocatedColors = $this->allocatePaletteColors($surface); + + // Draw the star as a filled circle with the core color + imagefilledellipse( + $surface, + $size / 2, + $size / 2, + $size, + $size, + $allocatedColors['core'] + ); + + // Apply surface texture and effects specific to each star type + $this->applySurfaceEffects($surface, $size, $allocatedColors); + + return $surface; + } + + /** + * Apply surface texture and effects specific to each star type. + */ + abstract protected function applySurfaceEffects($surface, int $size, array $colors): void; + + /** + * For the given $layer, it will return an array of allocated colors based + * on the color palette of this star type. + */ + private function allocatePaletteColors($layer): array + { + $allocatedColors = []; + $palette = $this->getColorPalette(); + + foreach ($palette as $colorName => $hexColor) { + list($r, $g, $b) = ColorHelper::hexToRgb($hexColor); + $allocatedColors[$colorName] = imagecolorallocate($layer, $r, $g, $b); + } + + return $allocatedColors; + } +} diff --git a/src/Odin/Astronomical/Star/Surface/RedGiantStarType.php b/src/Odin/Astronomical/Star/Surface/RedGiantStarType.php new file mode 100644 index 0000000..246f107 --- /dev/null +++ b/src/Odin/Astronomical/Star/Surface/RedGiantStarType.php @@ -0,0 +1,434 @@ + '#ff2200', + 'surface' => '#ff5500', + 'corona' => '#ff8844', + ]; + } + + /** + * Apply surface effects to the star + */ + protected function applySurfaceEffects($surface, int $size, array $colors): void + { + // Create a base gradient for the red giant + $this->createBaseGradient($surface, $size); + + // Add horizontal bands similar to gas planets + $this->addHorizontalBands($surface, $size); + + // Add large convection cells + $this->addConvectionCells($surface, $size); + + // Add sunspots + $this->addSunspots($surface, $size); + + // Add prominences (solar flares) + $this->addProminences($surface, $size); + } + + /** + * Create a base gradient for the red giant + */ + private function createBaseGradient($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + for ($x = 0; $x < $size; $x++) { + for ($y = 0; $y < $size; $y++) { + $distanceFromCenter = sqrt(pow($x - $centerX, 2) + pow($y - $centerY, 2)); + + if ($distanceFromCenter <= $radius) { + // Calculate gradient based on distance from center + $gradientFactor = $distanceFromCenter / $radius; + + // Red giant has orange-red gradient - less intense than before + $r = 220 - (int)(70 * $gradientFactor); // Reduced from 255 + $g = 140 - (int)(120 * $gradientFactor); // Reduced from 150 + $b = 20; // Constant low blue + + // Apply some noise for texture + $noise = rand(-20, 20); + $r = max(150, min(220, $r + $noise)); + $g = max(20, min(140, $g + $noise)); + + // More transparent at edges and overall + $alpha = (int)(40 * $gradientFactor); // Increased alpha for less brightness + + $color = imagecolorallocatealpha($surface, $r, $g, $b, $alpha); + imagesetpixel($surface, $x, $y, $color); + } + } + } + } + + /** + * Add horizontal bands similar to gas planets + */ + private function addHorizontalBands($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create 16-24 horizontal bands (doubled from 8-12) + $bandCount = rand(16, 24); + $bandHeight = (int)($size / $bandCount); + + for ($i = 0; $i < $bandCount; $i++) { + // Band position - ensure it's an integer + $bandY = (int)(($i * $bandHeight) + rand(-2, 2)); // Cast to int + + // Band color - alternating darker and lighter with subtle differences + $isDark = ($i % 2 == 0); + + if ($isDark) { + $bandColor = imagecolorallocatealpha( + $surface, + rand(170, 200), + rand(60, 90), + rand(0, 20), + rand(50, 70) // More transparent + ); + } else { + $bandColor = imagecolorallocatealpha( + $surface, + rand(200, 230), + rand(90, 130), + rand(10, 30), + rand(40, 60) // More transparent + ); + } + + // Draw the band with wavy edges + $this->drawWavyBand($surface, $size, $bandY, $bandHeight, $bandColor); + + // Add some texture to the band - but only to some bands to avoid overcrowding + if (rand(0, 3) > 0) { // 75% chance to add texture + $this->addBandTexture($surface, $size, $bandY, $bandHeight, $bandColor, $isDark); + } + } + } + + /** + * Draw a wavy horizontal band + */ + private function drawWavyBand($surface, int $size, int $bandY, int $bandHeight, $bandColor): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create wavy top and bottom edges - smaller amplitude + $waveAmplitude = rand(2, 5); // Reduced from 3-8 + $waveFrequency = rand(3, 5); // Increased from 2-4 + + // Draw the band + for ($x = 0; $x < $size; $x++) { + // Calculate distance from center horizontally + $distX = $x - $centerX; + $distFromCenter = abs($distX); + + if ($distFromCenter < $radius) { + // Calculate vertical position adjustment based on wave + $waveOffset = $waveAmplitude * sin($waveFrequency * $distX * M_PI / $radius); + + // Calculate band top and bottom with wave - ensure they're integers + $bandTop = (int)($bandY + $waveOffset); + $bandBottom = (int)($bandY + $bandHeight + $waveOffset); + + // Draw vertical line for this x position + for ($y = max(0, $bandTop); $y < min($size, $bandBottom); $y++) { + // Check if point is within the circle + $distY = $y - $centerY; + $distTotal = sqrt($distX * $distX + $distY * $distY); + + if ($distTotal <= $radius) { + // Apply some transparency variation for texture + $alpha = imagecolorsforindex($surface, imagecolorat($surface, (int)$x, (int)$y))['alpha']; + $newColor = imagecolorallocatealpha( + $surface, + imagecolorsforindex($surface, $bandColor)['red'], + imagecolorsforindex($surface, $bandColor)['green'], + imagecolorsforindex($surface, $bandColor)['blue'], + max($alpha - 5, 0) // Less opacity change (was -10) + ); + imagesetpixel($surface, (int)$x, (int)$y, $newColor); + } + } + } + } + } + + /** + * Add texture to a horizontal band + */ + private function addBandTexture($surface, int $size, int $bandY, int $bandHeight, $bandColor, bool $isDark): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Add more swirls and spots to the band + $featureCount = rand(10, 20); // Increased from 5-15 + for ($i = 0; $i < $featureCount; $i++) { + // Position within the band + $x = rand(0, $size); + $y = rand($bandY, $bandY + $bandHeight); + + // Check if within the circle + $distX = $x - $centerX; + $distY = $y - $centerY; + $distTotal = sqrt($distX * $distX + $distY * $distY); + + if ($distTotal <= $radius) { + // Feature size - smaller + $featureSize = rand(3, 8); // Reduced from 5-15 + + // Feature color - contrast with band but more subtle + if ($isDark) { + $featureColor = imagecolorallocatealpha( + $surface, + rand(190, 220), + rand(80, 120), + rand(10, 30), + rand(40, 60) + ); + } else { + $featureColor = imagecolorallocatealpha( + $surface, + rand(160, 190), + rand(50, 90), + rand(0, 20), + rand(50, 70) + ); + } + + // Draw the feature - oval shape stretched horizontally + imagefilledellipse( + $surface, + (int)$x, + (int)$y, + (int)($featureSize * 2), // Wider horizontally + (int)$featureSize, + $featureColor + ); + } + } + + // Add some small streaks along the band + $streakCount = rand(5, 10); + for ($i = 0; $i < $streakCount; $i++) { + // Position within the band + $startX = rand(0, $size); + $startY = rand($bandY + 1, min($size - 1, $bandY + $bandHeight - 1)); // Ensure within bounds + + // Check if within the circle + $distX = $startX - $centerX; + $distY = $startY - $centerY; + $distTotal = sqrt($distX * $distX + $distY * $distY); + + if ($distTotal <= $radius * 0.9) { + // Streak length + $length = rand(5, 15); + $endX = min($size - 1, $startX + $length); // Ensure within bounds + + // Get base colors from band color + $baseColors = imagecolorsforindex($surface, $bandColor); + + // Apply bounded adjustments to ensure values stay within 0-255 + $r = max(0, min(255, $baseColors['red'] + rand(-20, 20))); + $g = max(0, min(255, $baseColors['green'] + rand(-20, 20))); + $b = max(0, min(255, $baseColors['blue'] + rand(-10, 10))); + $a = rand(30, 50); + + // Streak color with bounded values + $streakColor = imagecolorallocatealpha( + $surface, + $r, + $g, + $b, + $a + ); + + // Draw streak + imageline($surface, (int)$startX, (int)$startY, (int)$endX, (int)$startY, $streakColor); + } + } + } + + /** + * Add large convection cells + */ + private function addConvectionCells($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create fewer large convection cells + $cellCount = rand(15, 25); // Reduced from 30-50 + for ($i = 0; $i < $cellCount; $i++) { + // Random position within the star + $angle = rand(0, 360) * M_PI / 180; + $distance = rand(0, (int)($radius * 0.9)); + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + // Cell size varies - larger than before + $cellSize = rand(10, 25); // Increased from 8-20 + + // Cell color - more subdued + $cellColor = imagecolorallocatealpha( + $surface, + rand(180, 220), // Reduced from 200-255 + rand(60, 120), // Reduced from 80-150 + rand(0, 20), + rand(30, 60) // More transparent + ); + + // Draw cell with irregular shape + imagefilledellipse($surface, (int)$x, (int)$y, $cellSize, (int)($cellSize * rand(80, 120) / 100), $cellColor); + } + } + + /** + * Add sunspots + */ + private function addSunspots($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create fewer sunspots + $spotCount = rand(5, 10); // Reduced from 10-20 + for ($i = 0; $i < $spotCount; $i++) { + // Random position within the star + $angle = rand(0, 360) * M_PI / 180; + $distance = rand(0, (int)($radius * 0.8)); + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + // Spot size varies + $spotSize = rand(5, 15); // Slightly larger + + // Dark spot color + $spotColor = imagecolorallocatealpha($surface, rand(80, 120), rand(20, 60), 0, rand(0, 20)); + + // Draw the spot + imagefilledellipse($surface, (int)$x, (int)$y, $spotSize, $spotSize, $spotColor); + + // Add darker center to some spots + if (rand(0, 2) == 0) { + $centerColor = imagecolorallocatealpha($surface, rand(60, 100), rand(0, 20), 0, rand(20, 40)); + imagefilledellipse($surface, (int)$x, (int)$y, (int)($spotSize / 2), (int)($spotSize / 2), $centerColor); + } + } + } + + /** + * Add prominences (solar flares) + */ + private function addProminences($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create fewer prominences + $flareCount = rand(3, 6); // Reduced from 5-10 + for ($i = 0; $i < $flareCount; $i++) { + // Position near the edge + $angle = rand(0, 360) * M_PI / 180; + $distance = rand((int)($radius * 0.8), (int)($radius * 0.95)); + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + // Flare size - larger for more visual impact + $flareSize = rand(10, 20); // Increased from 5-15 + + // Bright orange-red color + $flareColor = imagecolorallocatealpha( + $surface, + rand(220, 255), + rand(100, 180), + rand(0, 50), + rand(0, 20) + ); + + // Draw the prominence + imagefilledellipse($surface, (int)$x, (int)$y, $flareSize, $flareSize, $flareColor); + + // Add glow around the prominence + for ($j = 1; $j <= 3; $j++) { // Reduced from 4 layers + $glowSize = $flareSize + $j * 3; + $glowColor = imagecolorallocatealpha($surface, 255, rand(100, 180), rand(30, 80), 30 + $j * 15); // More transparent + imagefilledellipse($surface, (int)$x, (int)$y, $glowSize, $glowSize, $glowColor); + } + + // Sometimes add a prominence extension (like a solar flare) + if (rand(0, 2) == 0) { + $extAngle = $angle + (rand(-30, 30) * M_PI / 180); + $extLength = rand($flareSize, $flareSize * 2); + + $extEndX = $x + cos($extAngle) * $extLength; + $extEndY = $y + sin($extAngle) * $extLength; + + // Draw a thick line for the extension + $this->drawThickLine($surface, (int)$x, (int)$y, (int)$extEndX, (int)$extEndY, $flareColor, rand(3, 6)); + } + } + } + + /** + * Draw a thick line + */ + private function drawThickLine($image, $x1, $y1, $x2, $y2, $color, $thickness): void + { + $t = $thickness / 2 - 0.5; + + if ($x1 == $x2 || $y1 == $y2) { + imagesetthickness($image, $thickness); + imageline($image, (int)$x1, (int)$y1, (int)$x2, (int)$y2, $color); + imagesetthickness($image, 1); + return; + } + + $k = ($y2 - $y1) / ($x2 - $x1); + $a = $t / sqrt(1 + pow($k, 2)); + $points = [ + (int)round($x1 - (1+$k)*$a), (int)round($y1 + (1-$k)*$a), + (int)round($x1 - (1-$k)*$a), (int)round($y1 - (1+$k)*$a), + (int)round($x2 + (1+$k)*$a), (int)round($y2 - (1-$k)*$a), + (int)round($x2 + (1-$k)*$a), (int)round($y2 + (1+$k)*$a), + ]; + + imagefilledpolygon($image, $points, 4, $color); + } +} diff --git a/src/Odin/Astronomical/Star/Surface/RegularStarType.php b/src/Odin/Astronomical/Star/Surface/RegularStarType.php new file mode 100644 index 0000000..cf23b92 --- /dev/null +++ b/src/Odin/Astronomical/Star/Surface/RegularStarType.php @@ -0,0 +1,206 @@ + '#ffff66', + 'surface' => '#ffffcc', + 'corona' => '#ffffdd', + ]; + } + + /** + * {@inheritdoc} + */ + protected function applySurfaceEffects($surface, int $size, array $colors): void + { + // Create a base gradient for the star from center to edge + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + for ($x = 0; $x < $size; $x++) { + for ($y = 0; $y < $size; $y++) { + $distanceFromCenter = sqrt(pow($x - $centerX, 2) + pow($y - $centerY, 2)); + + if ($distanceFromCenter <= $radius) { + // Calculate gradient based on distance from center + $gradientFactor = $distanceFromCenter / $radius; + + // Brighter in center, dimmer at edges - with more color variation + $r = 255 - (int)(50 * $gradientFactor); // Reduce brightness at edges + $g = 240 - (int)(70 * $gradientFactor); + $b = (int)(102 + (153 * (1 - $gradientFactor))); // 102 to 255 + + // Add some noise for texture + $noise = rand(-15, 15); + $r = max(0, min(255, $r + $noise)); + $g = max(0, min(255, $g + $noise)); + $b = max(0, min(255, $b + $noise)); + + // More transparent at edges + $alpha = (int)(40 * $gradientFactor); // Increased alpha for less brightness + + $color = imagecolorallocatealpha($surface, $r, $g, $b, $alpha); + imagesetpixel($surface, $x, $y, $color); + } + } + } + + // Add solar granulation effect (small cells) - with more contrast + $this->addGranulationEffect($surface, $size, $colors); + + // Add some brighter spots (solar flares) - fewer but more distinct + $this->addSolarFlares($surface, $size, $colors); + + // Add some decorative bands for visual interest + $this->addDecorativeBands($surface, $size, $colors); + } + + /** + * Add solar granulation effect (the cell-like pattern on a star's surface) + */ + private function addGranulationEffect($surface, int $size, array $colors): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create several small granulation cells + $cellCount = 150; // Reduced from 200 for less busy appearance + for ($i = 0; $i < $cellCount; $i++) { + // Random position within the star + $angle = rand(0, 360) * M_PI / 180; + $distance = rand(0, (int)($radius * 0.9)); + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + // Cell size varies + $cellSize = rand(3, 8); // Slightly larger cells + + // Cell color - more contrast with surroundings + $cellColor = imagecolorallocatealpha( + $surface, + rand(220, 255), + rand(180, 220), + rand(100, 150), + rand(30, 60) + ); + + // Draw cell with irregular shape + imagefilledellipse($surface, (int)$x, (int)$y, $cellSize, (int)($cellSize * rand(80, 120) / 100), $cellColor); + } + } + + /** + * Add solar flares and bright spots + */ + private function addSolarFlares($surface, int $size, array $colors): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Add 3-6 solar flares (fewer but more distinct) + $flareCount = rand(3, 6); + for ($i = 0; $i < $flareCount; $i++) { + // Position near the edge + $angle = rand(0, 360) * M_PI / 180; + $distance = rand((int)($radius * 0.7), (int)($radius * 0.95)); + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + // Flare size - larger for more visual impact + $flareSize = rand(10, 20); + + // Bright yellow-orange color + $flareColor = imagecolorallocatealpha($surface, 255, rand(200, 255), rand(100, 150), 0); + + // Draw the flare + imagefilledellipse($surface, (int)$x, (int)$y, $flareSize, $flareSize, $flareColor); + + // Add glow around the flare + for ($j = 1; $j <= 3; $j++) { + $glowSize = $flareSize + $j * 4; + $glowColor = imagecolorallocatealpha($surface, 255, rand(180, 220), rand(80, 120), 20 + $j * 20); + imagefilledellipse($surface, (int)$x, (int)$y, $glowSize, $glowSize, $glowColor); + } + } + } + + /** + * Add decorative bands for visual interest + */ + private function addDecorativeBands($surface, int $size, array $colors): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Add 2-3 subtle bands + $bandCount = rand(2, 3); + for ($i = 0; $i < $bandCount; $i++) { + // Band position - evenly distributed + $bandRadius = $radius * (0.3 + ($i * 0.25)); + + // Band color - slightly different from base + $bandColor = imagecolorallocatealpha( + $surface, + rand(220, 255), + rand(200, 240), + rand(120, 180), + rand(50, 70) + ); + + // Draw a circle for the band + imageellipse($surface, $centerX, $centerY, (int)($bandRadius * 2), (int)($bandRadius * 2), $bandColor); + + // Add some texture to the band + $this->addBandTexture($surface, $size, $bandRadius, $bandColor); + } + } + + /** + * Add texture to decorative bands + */ + private function addBandTexture($surface, int $size, float $bandRadius, $bandColor): void + { + $centerX = $size / 2; + $centerY = $size / 2; + + // Add texture points along the band + $pointCount = rand(20, 30); + for ($i = 0; $i < $pointCount; $i++) { + $angle = ($i * 360 / $pointCount) * M_PI / 180; + + // Add some variation to the radius + $adjustedRadius = $bandRadius * (0.95 + (rand(0, 10) / 100)); + + $x = $centerX + $adjustedRadius * cos($angle); + $y = $centerY + $adjustedRadius * sin($angle); + + // Draw a small point + $pointSize = rand(2, 4); + imagefilledellipse($surface, (int)$x, (int)$y, $pointSize, $pointSize, $bandColor); + } + } +} diff --git a/src/Odin/Astronomical/Star/Surface/StarSurfaceGeneratorRegistry.php b/src/Odin/Astronomical/Star/Surface/StarSurfaceGeneratorRegistry.php new file mode 100644 index 0000000..d5328f7 --- /dev/null +++ b/src/Odin/Astronomical/Star/Surface/StarSurfaceGeneratorRegistry.php @@ -0,0 +1,29 @@ +generatorClasses as $generatorClass) { + $generator = new $generatorClass; + + if ($typeName === $generator->getName()) { + return $generator; + } + } + + throw new \InvalidArgumentException( + sprintf('No generator found for star type "%s"', $typeName) + ); + } +} diff --git a/src/Odin/Astronomical/Star/Surface/StarTypeInterface.php b/src/Odin/Astronomical/Star/Surface/StarTypeInterface.php new file mode 100644 index 0000000..b2d9ee5 --- /dev/null +++ b/src/Odin/Astronomical/Star/Surface/StarTypeInterface.php @@ -0,0 +1,28 @@ + '#ffffff', + 'surface' => '#ccffff', + 'corona' => '#eeffff', + ]; + } + + /** + * {@inheritdoc} + */ + protected function applySurfaceEffects($surface, int $size, array $colors): void + { + // Create a base gradient for the white dwarf + $this->createBaseGradient($surface, $size); + + // Add an intense but not overwhelming bright center + $this->addIntenseBrightCenter($surface, $size); + + // Add blue-tinted regions + $this->addBlueTintedRegions($surface, $size); + + // Add crystalline-like patterns + $this->addCrystallinePatterns($surface, $size); + + // Add decorative rings for visual appeal + $this->addDecorativeRings($surface, $size); + } + + /** + * Create a base gradient for the white dwarf + */ + private function createBaseGradient($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + for ($x = 0; $x < $size; $x++) { + for ($y = 0; $y < $size; $y++) { + $distanceFromCenter = sqrt(pow($x - $centerX, 2) + pow($y - $centerY, 2)); + + if ($distanceFromCenter <= $radius) { + // Calculate gradient based on distance from center - less steep gradient + $gradientFactor = pow($distanceFromCenter / $radius, 0.8); // Power < 1 makes gradient more uniform + + // White-blue gradient - more blue tint, less variation from center to edge + $r = 200 - (int)(40 * $gradientFactor); // Reduced difference (was 60) + $g = 210 - (int)(30 * $gradientFactor); // Reduced difference (was 50) + $b = 255 - (int)(10 * $gradientFactor); // Keep blue high + + // Add some noise for texture + $noise = rand(-15, 15); + $r = max(160, min(220, $r + $noise)); + $g = max(180, min(230, $g + $noise)); + $b = max(220, min(255, $b + $noise)); + + // More transparent at edges and overall + $alpha = (int)(30 * $gradientFactor); // Reduced from 40 for less contrast + + $color = imagecolorallocatealpha($surface, $r, $g, $b, $alpha); + imagesetpixel($surface, $x, $y, $color); + } + } + } + } + + /** + * Add an intense bright center characteristic of white dwarfs + */ + private function addIntenseBrightCenter($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + + // Create multiple layers of decreasing size and opacity for a glowing effect + $maxLayers = 3; // Reduced from 4 + for ($i = 0; $i < $maxLayers; $i++) { + $layerSize = (int)(($size / 3) * (($maxLayers - $i) / $maxLayers)); + $opacity = 40 + ($i * 10); // More transparent (was 30 + i*15) + + // More blue tint in the center, but less bright + $centerColor = imagecolorallocatealpha($surface, 210, 220, 250, $opacity); + imagefilledellipse($surface, (int)$centerX, (int)$centerY, $layerSize, $layerSize, $centerColor); + } + + // Add a small, less bright core + $coreSize = (int)($size / 12); // Even smaller core (was size/10) + $coreColor = imagecolorallocatealpha($surface, 220, 230, 255, 30); // More transparency (was 20) + imagefilledellipse($surface, (int)$centerX, (int)$centerY, $coreSize, $coreSize, $coreColor); + } + + /** + * Add subtle blue-tinted regions + */ + private function addBlueTintedRegions($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Add 25-35 subtle blue regions + $regionCount = rand(25, 35); + for ($i = 0; $i < $regionCount; $i++) { + // Position throughout the star + $angle = rand(0, 360) * M_PI / 180; + $distance = rand((int)($radius * 0.2), (int)($radius * 0.95)); // Allow closer to center + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + $regionSize = rand(3, 8); + + // Blue-white color with varying intensity - more blue + $blueIntensity = rand(200, 255); + $regionColor = imagecolorallocatealpha( + $surface, + rand(160, 200), // Reduced from 180-220 + rand(180, 220), // Reduced from 180-220 + $blueIntensity, + rand(30, 60) + ); + + // Draw the region + imagefilledellipse($surface, (int)$x, (int)$y, $regionSize, $regionSize, $regionColor); + } + } + + /** + * Add crystalline-like patterns to simulate the highly compressed carbon surface + */ + private function addCrystallinePatterns($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + $radius = $size / 2; + + // Create 4-8 crystalline pattern regions (reduced from 5-10) + $patternCount = rand(4, 8); + for ($i = 0; $i < $patternCount; $i++) { + // Position these patterns toward the outer regions + $angle = rand(0, 360) * M_PI / 180; + $distance = rand((int)($radius * 0.5), (int)($radius * 0.9)); + + $centerPatternX = $centerX + $distance * cos($angle); + $centerPatternY = $centerY + $distance * sin($angle); + + // Create 3-6 radiating lines from this center (reduced from 5-8) + $lineCount = rand(3, 6); + for ($j = 0; $j < $lineCount; $j++) { + $lineAngle = rand(0, 360) * M_PI / 180; + $lineLength = rand(5, 15); // Shorter lines + + $endX = $centerPatternX + $lineLength * cos($lineAngle); + $endY = $centerPatternY + $lineLength * sin($lineAngle); + + // Line color - more subdued + $lineColor = imagecolorallocatealpha( + $surface, + rand(200, 240), // Reduced from 220-255 + rand(200, 240), // Reduced from 220-255 + rand(220, 255), + rand(30, 60) // More transparent + ); + + // Draw the line + imageline( + $surface, + (int)$centerPatternX, + (int)$centerPatternY, + (int)$endX, + (int)$endY, + $lineColor + ); + + // Sometimes add a small dot at the end of the line + if (rand(0, 2) == 0) { + $dotColor = imagecolorallocatealpha( + $surface, + rand(200, 240), // Reduced brightness + rand(200, 240), // Reduced brightness + rand(220, 255), + rand(20, 40) + ); + imagefilledellipse($surface, (int)$endX, (int)$endY, 2, 2, $dotColor); + } + } + } + + // Add a few bright spots to simulate heat pockets - fewer than before + for ($i = 0; $i < 10; $i++) { // Reduced from 15 + $angle = rand(0, 360) * M_PI / 180; + $distance = rand(0, (int)($radius * 0.8)); + + $x = $centerX + $distance * cos($angle); + $y = $centerY + $distance * sin($angle); + + $spotSize = rand(1, 3); + $spotColor = imagecolorallocatealpha($surface, 240, 240, 255, 20); // Added transparency + + imagefilledellipse($surface, (int)$x, (int)$y, $spotSize, $spotSize, $spotColor); + } + } + + /** + * Add decorative rings for visual appeal + */ + private function addDecorativeRings($surface, int $size): void + { + $centerX = $size / 2; + $centerY = $size / 2; + + // Add 2-3 subtle rings + $ringCount = rand(2, 3); + for ($i = 0; $i < $ringCount; $i++) { + // Ring radius - evenly distributed + $ringRadius = ($size / 2) * (0.4 + ($i * 0.2)); + + // Ring color - blue-white with transparency + $ringColor = imagecolorallocatealpha( + $surface, + rand(200, 230), + rand(200, 230), + rand(230, 255), + rand(50, 70) + ); + + // Draw the ring + imageellipse($surface, $centerX, $centerY, (int)($ringRadius * 2), (int)($ringRadius * 2), $ringColor); + + // Add some texture to the ring + $this->addRingTexture($surface, $size, $ringRadius, $ringColor); + } + } + + /** + * Add texture to decorative rings + */ + private function addRingTexture($surface, int $size, float $ringRadius, $ringColor): void + { + $centerX = $size / 2; + $centerY = $size / 2; + + // Add texture points along the ring + $pointCount = rand(15, 25); + for ($i = 0; $i < $pointCount; $i++) { + $angle = ($i * 360 / $pointCount) * M_PI / 180; + + // Add some variation to the radius + $adjustedRadius = $ringRadius * (0.95 + (rand(0, 10) / 100)); + + $x = $centerX + $adjustedRadius * cos($angle); + $y = $centerY + $adjustedRadius * sin($angle); + + // Draw a small point + $pointSize = rand(1, 3); + imagefilledellipse($surface, (int)$x, (int)$y, $pointSize, $pointSize, $ringColor); + } + } +} diff --git a/src/Odin/Star.php b/src/Odin/Star.php new file mode 100644 index 0000000..91ad574 --- /dev/null +++ b/src/Odin/Star.php @@ -0,0 +1,96 @@ +layerOrchestrator = new LayerOrchestrator(); + $this->configuration = $configuration ?? new Configuration(); + } + + public function diameter(int $diameterInPixels): self + { + $this->diameter = $diameterInPixels; + + return $this; + } + + public function regular(): self + { + $this->type = 'Regular'; + + return $this; + } + + public function redGiant(): self + { + $this->type = 'RedGiant'; + + return $this; + } + + public function whiteDwarf(): self + { + $this->type = 'WhiteDwarf'; + + return $this; + } + + public function render(): \SplFileObject + { + mt_srand($this->configuration->seed()); + + if (null === $this->diameter) { + throw new \LogicException('The star cannot be rendered without a diameter.'); + } + + if (null === $this->type) { + throw new \LogicException('The star cannot be rendered without a type.'); + } + + $star = new InternalStar($this->type, $this->diameter); + $this->layerOrchestrator->initTransparentBaseLayer($this->diameter, $this->diameter); + $this->layerOrchestrator->addLayer($star->render(), -$this->diameter / 2, -$this->diameter / 2); + + $image = $this->layerOrchestrator->render(); + $imagePath = $this->generateImagePath($this->configuration); + + imagepng($image, $imagePath); + imagedestroy($image); + + return new \SplFileObject($imagePath); + } + + private function generateImagePath(?Configuration $configuration): string + { + $name = uniqid('odin-star-') . '.png'; + $directory = sys_get_temp_dir(); + if (null !== $configuration) { + $directory = $configuration->directory(); + } + + return $directory . DIRECTORY_SEPARATOR . $name; + } +}