ヤミRoot VoidGate
User / IP
:
216.73.216.143
Host / Server
:
146.88.233.70 / dev.loger.cm
System
:
Linux hybrid1120.fr.ns.planethoster.net 3.10.0-957.21.2.el7.x86_64 #1 SMP Wed Jun 5 14:26:44 UTC 2019 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
logercm
/
dev.loger.cm
/
fixtures
/
assert
/
Viewing: config.tar
bootstrap.php 0000644 00000002107 15117152224 0007272 0 ustar 00 <?php use Symfony\Component\Dotenv\Dotenv; require dirname(__DIR__).'/vendor/autoload.php'; if (!class_exists(Dotenv::class)) { throw new LogicException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); } // Load cached env vars if the .env.local.php file exists // Run "composer dump-env prod" to create it (requires symfony/flex >=1.2) if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) { (new Dotenv(false))->populate($env); } else { // load all the .env files (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env'); } $_SERVER += $_ENV; $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; packages/api_platform.yaml 0000644 00000003744 15117152224 0011673 0 ustar 00 api_platform: collection: order_parameter_name: '_order' # the URL query parameter to use is now "_order" enable_fos_user: true enable_nelmio_api_doc: true graphql: graphiql: enabled: true mapping: paths: ['%kernel.project_dir%/src/Entity'] swagger: api_keys: apiKey: name: Authorization type: header versions: [3] # The title of the API. title: 'Loger REST API' # The description of the API. description: 'Endpoint Swagger' # The version of the API. version: '1.0.0' oauth: # To enable or disable oauth. enabled: false # The oauth client id. clientId: '' # The oauth client secret. clientSecret: '' # The oauth type. type: 'oauth2' # The oauth flow grant type. flow: 'password' # The oauth token url. tokenUrl: '/oauth/v2/token' # The oauth authentication url. authorizationUrl: '/oauth/v2/auth' # The oauth scopes. scopes: ['read','write'] # The list of enabled formats. The first one will be the default. formats: json: ['application/json'] jsonld: ['application/ld+json'] jsonhal: ['application/hal+json'] jsonapi: ['application/vnd.api+json'] xml: ['application/xml', 'text/xml'] yaml: ['application/x-yaml'] csv: ['text/csv'] html: ['text/html'] graphql: ['application/graphql'] patch_formats: json: ['application/merge-patch+json'] jsonapi: ['application/vnd.api+json'] # The list of enabled error formats. The first one will be the default. error_formats: jsonproblem: ['application/problem+json'] jsonld: ['application/ld+json'] # Hydra error formats jsonapi: ['application/vnd.api+json'] packages/cache.yaml 0000644 00000001257 15117152224 0010256 0 ustar 00 framework: cache: # Unique name of your app: used to compute stable namespaces for cache keys. #prefix_seed: your_vendor_name/app_name # The "app" cache stores to the filesystem by default. # The data in this cache should persist between deploys. # Other options include: # Redis #app: cache.adapter.redis #default_redis_provider: redis://localhost # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) #app: cache.adapter.apcu # Namespaced pools use the above "app" backend by default #pools: #my.dedicated.cache: null packages/dev/jms_serializer.yaml 0000644 00000000312 15117152224 0013002 0 ustar 00 jms_serializer: visitors: json_serialization: options: - JSON_PRETTY_PRINT - JSON_UNESCAPED_SLASHES - JSON_PRESERVE_ZERO_FRACTION packages/doctrine.yaml 0000644 00000001515 15117152224 0011017 0 ustar 00 doctrine: dbal: url: '%env(resolve:DATABASE_URL)%' # IMPORTANT: You MUST configure your server version, # either here or in the DATABASE_URL env var (see .env file) #server_version: '5.7' driver: 'pdo_mysql' server_version: '5.7' charset: utf8mb4 unix_socket: /Applications/MAMP/tmp/mysql/mysql.sock default_table_options: charset: utf8mb4 collate: utf8mb4_unicode_ci orm: auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true mappings: App: is_bundle: false type: annotation dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App packages/doctrine_migrations.yaml 0000644 00000000361 15117152224 0013251 0 ustar 00 doctrine_migrations: migrations_paths: # namespace is arbitrary but should be different from App\Migrations # as migrations classes should NOT be autoloaded 'DoctrineMigrations': '%kernel.project_dir%/migrations' packages/fos_rest.yaml 0000644 00000001456 15117152224 0011040 0 ustar 00 # Read the documentation: https://symfony.com/doc/master/bundles/FOSRestBundle/index.html fos_rest: body_converter: enabled: true validate: true validation_errors_argument: violations view: formats: { json: true, xml: false, rss: false } view_response_listener: true serializer: serialize_null: true body_listener: array_normalizer: fos_rest.normalizer.camel_keys param_fetcher_listener: true allowed_methods_listener: true routing_loader: false format_listener: rules: - { path: '^/api/v1', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } - { path: '^/', priorities: [ 'text/html', '*/*'], fallback_format: html, prefer_extension: true } packages/gesdinet_jwt_refresh_token.yaml 0000644 00000000303 15117152224 0014606 0 ustar 00 gesdinet_jwt_refresh_token: refresh_token_class: App\Entity\RefreshToken # This is the class name of the refresh token, you will need to adjust this to match the class your application will use packages/hautelook_alice.yaml 0000644 00000000125 15117152224 0012334 0 ustar 00 when@dev: &dev hautelook_alice: fixtures_path: fixtures when@test: *dev packages/jms_serializer.yaml 0000644 00000000705 15117152224 0012232 0 ustar 00 jms_serializer: visitors: xml_serialization: format_output: '%kernel.debug%' # metadata: # auto_detection: false # directories: # any-name: # namespace_prefix: "My\\FooBundle" # path: "@MyFooBundle/Resources/config/serializer" # another-name: # namespace_prefix: "My\\BarBundle" # path: "@MyBarBundle/Resources/config/serializer" packages/lexik_jwt_authentication.yaml 0000644 00000000267 15117152224 0014312 0 ustar 00 lexik_jwt_authentication: secret_key: '%env(resolve:JWT_SECRET_KEY)%' public_key: '%env(resolve:JWT_PUBLIC_KEY)%' pass_phrase: '%env(JWT_PASSPHRASE)%' token_ttl: 7200 packages/mailer.yaml 0000644 00000000070 15117152224 0010454 0 ustar 00 framework: mailer: dsn: '%env(MAILER_URL)%' packages/nelmio_alice.yaml 0000644 00000000347 15117152224 0011632 0 ustar 00 when@dev: &dev nelmio_alice: functions_blacklist: - 'current' - 'shuffle' - 'date' - 'time' - 'file' - 'md5' - 'sha1' when@test: *dev packages/nelmio_cors.yaml 0000644 00000002035 15117152224 0011517 0 ustar 00 nelmio_cors: defaults: allow_credentials: false allow_origin: [] allow_headers: [] allow_methods: [] expose_headers: [] max_age: 0 hosts: [] origin_regex: false forced_allow_origin_value: ~ paths: '^/api/v1': origin_regex: true allow_origin: ['^http://localhost:[0-9]+','https://testshare.epassevents.cm','https://biocarrylife.com'] # You probably want to change this regex to match your real domain allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] allow_headers: ['Content-Type', 'Authorization'] expose_headers: ['Link'] max_age: 3600 '^/': origin_regex: true allow_origin: ['^http://localhost:[0-9]+','https://testshare.epassevents.cm','https://biocarrylife.com'] allow_headers: ['Content-Type', 'Authorization'] allow_methods: ['POST', 'PUT', 'GET', 'DELETE'] max_age: 3600 hosts: ['^api\v1\.'] packages/notifier.yaml 0000644 00000000621 15117152224 0011024 0 ustar 00 framework: notifier: chatter_transports: firebase: '%env(FIREBASE_DSN)%' texter_transports: channel_policy: # use chat/slack, chat/telegram, sms/twilio or sms/nexmo urgent: ['email'] high: ['email'] medium: ['email'] low: ['email'] admin_recipients: - { email: admin@example.com } packages/prod/doctrine.yaml 0000644 00000001041 15117152224 0011755 0 ustar 00 doctrine: orm: auto_generate_proxy_classes: false metadata_cache_driver: type: pool pool: doctrine.system_cache_pool query_cache_driver: type: pool pool: doctrine.system_cache_pool result_cache_driver: type: pool pool: doctrine.result_cache_pool framework: cache: pools: doctrine.result_cache_pool: adapter: cache.app doctrine.system_cache_pool: adapter: cache.system packages/prod/jms_serializer.yaml 0000644 00000000246 15117152224 0013176 0 ustar 00 jms_serializer: visitors: json_serialization: options: - JSON_UNESCAPED_SLASHES - JSON_PRESERVE_ZERO_FRACTION packages/prod/routing.yaml 0000644 00000000071 15117152224 0011637 0 ustar 00 framework: router: strict_requirements: null packages/routing.yaml 0000644 00000000052 15117152224 0010672 0 ustar 00 framework: router: utf8: true packages/sensio_framework_extra.yaml 0000644 00000000077 15117152224 0013772 0 ustar 00 sensio_framework_extra: router: annotations: false packages/stof_doctrine_extensions.yaml 0000644 00000000546 15117152224 0014334 0 ustar 00 # Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html # See the official DoctrineExtensions documentation for more details: https://github.com/doctrine-extensions/DoctrineExtensions/tree/main/doc stof_doctrine_extensions: default_locale: fr_FR orm: default: timestampable: true packages/test/framework.yaml 0000644 00000000125 15117152224 0012160 0 ustar 00 framework: test: true session: storage_id: session.storage.mock_file packages/test/twig.yaml 0000644 00000000041 15117152224 0011132 0 ustar 00 twig: strict_variables: true packages/test/validator.yaml 0000644 00000000103 15117152224 0012144 0 ustar 00 framework: validation: not_compromised_password: false packages/translation.yaml 0000644 00000000246 15117152224 0011546 0 ustar 00 framework: default_locale: '%locale%' translator: paths: - '%kernel.project_dir%/translations' fallbacks: - '%locale%' packages/twig.yaml 0000644 00000000414 15117152224 0010157 0 ustar 00 twig: default_path: '%kernel.project_dir%/templates' debug: '%kernel.debug%' strict_variables: '%kernel.debug%' # On déclare ci-dessous le nom de la variable globale globals: locales: '%app_locales%' localeLangs: '%app.locales%' packages/validator.yaml 0000644 00000000407 15117152224 0011174 0 ustar 00 framework: validation: email_validation_mode: html5 # Enables validator auto-mapping support. # For instance, basic validation constraints will be inferred from Doctrine's metadata. #auto_mapping: # App\Entity\: [] packages/vich_uploader.yaml 0000644 00000000606 15117152224 0012034 0 ustar 00 vich_uploader: db_driver: orm mappings: media_object: uri_prefix: '%app.path.media_object%' upload_destination: '%kernel.project_dir%/public/media' # Will rename uploaded files using a uniqueid as a prefix. namer: Vich\UploaderBundle\Naming\OrignameNamer delete_on_remove: true delete_on_update: true packages/web_profiler.yaml 0000644 00000000437 15117152224 0011671 0 ustar 00 when@dev: web_profiler: toolbar: true intercept_redirects: false framework: profiler: { only_exceptions: false } when@test: web_profiler: toolbar: false intercept_redirects: false framework: profiler: { collect: false } packages/webpack_encore.yaml 0000644 00000003224 15117152224 0012156 0 ustar 00 webpack_encore: # The path where Encore is building the assets - i.e. Encore.setOutputPath() output_path: '%kernel.project_dir%/public/build' # If multiple builds are defined (as shown below), you can disable the default build: # output_path: false # Set attributes that will be rendered on all script and link tags script_attributes: defer: true # Uncomment (also under link_attributes) if using Turbo Drive # https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change # 'data-turbo-track': reload # link_attributes: # Uncomment if using Turbo Drive # 'data-turbo-track': reload # If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials') # crossorigin: 'anonymous' # Preload all rendered script and link tags automatically via the HTTP/2 Link header # preload: true # Throw an exception if the entrypoints.json file is missing or an entry is missing from the data # strict_mode: false # If you have multiple builds: # builds: # frontend: '%kernel.project_dir%/public/frontend/build' # pass the build name as the 3rd argument to the Twig functions # {{ encore_entry_script_tags('entry1', null, 'frontend') }} framework: assets: json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' #when@prod: # webpack_encore: # # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) # # Available in version 1.2 # cache: true #when@test: # webpack_encore: # strict_mode: false packages/framework.yaml 0000644 00000001566 15117152224 0011213 0 ustar 00 # see https://symfony.com/doc/current/reference/configuration/framework.html framework: form: { enabled: true } serializer: { enable_annotations: true } fragments: hinclude_default_template: hinclude.html.twig secret: '%env(APP_SECRET)%' #csrf_protection: true http_method_override: false # Enables session support. Note that the session will ONLY be started if you read or write from it. # Remove or comment this section to explicitly disable session support. session: handler_id: null cookie_secure: auto cookie_samesite: lax storage_factory_id: session.storage.factory.native gc_probability: null #esi: true #fragments: true php_errors: log: true when@test: framework: test: true session: storage_factory_id: session.storage.factory.mock_file packages/security.yaml 0000644 00000005046 15117152224 0011062 0 ustar 00 security: role_hierarchy: ROLE_ADMIN: [ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH,ROLE_USER] enable_authenticator_manager: true password_hashers: Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface: 'auto' App\Entity\User: algorithm: auto # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers providers: # used to reload user from session & other features (e.g. switch_user) app_user_provider: entity: class: App\Entity\User property: email firewalls: dev: pattern: ^/_(profiler|wdt) security: false api: pattern: ^/api/v1/ stateless: false entry_point: jwt json_login: check_path: /api/v1/authentication_token provider: app_user_provider username_path: email password_path: password success_handler: lexik_jwt_authentication.handler.authentication_success failure_handler: lexik_jwt_authentication.handler.authentication_failure jwt: ~ main: stateless: false lazy: false switch_user: true provider: app_user_provider entry_point: form_login custom_authenticators: - App\Security\BackendAuthenticator form_login: check_path: app_login login_path: app_login default_target_path: /admin/ use_referer: true logout: path: app_logout # where to redirect after logout target: app_login remember_me: secret: '%kernel.secret%' lifetime: 684888 path: / access_control: - { path: ^/api/v1/docs, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI - { path: ^/api/v1/authentication_token, roles: PUBLIC_ACCESS } - { path: ^/api/v1/authentication_token/refresh, roles: PUBLIC_ACCESS } - { path: ^/register, roles: PUBLIC_ACCESS } - { path: ^/email/verify, roles: PUBLIC_ACCESS } - { path: ^/login, roles: PUBLIC_ACCESS } - { path: ^/client_location, roles: [ PUBLIC_ACCESS ] } - { path: ^/admin, roles: PUBLIC_ACCESS } - { path: ^/resetting, roles: IS_AUTHENTICATED_FULLY } - { path: ^/external-service/sms, roles: IS_AUTHENTICATED_FULLY } packages/reset_password.yaml 0000644 00000000154 15117152224 0012252 0 ustar 00 symfonycasts_reset_password: request_password_repository: App\Repository\ResetPasswordRequestRepository routes/annotations.yaml 0000644 00000000373 15117152224 0011311 0 ustar 00 controllers: resource: ../../src/Controller/ type: annotation prefix: /{_locale} requirements: _locale: '%app_locales%' defaults: _locale: '%locale%' kernel: resource: ../../src/Kernel.php type: annotation routes/api_platform.yaml 0000644 00000000111 15117152224 0011417 0 ustar 00 api_platform: resource: . type: api_platform prefix: /api/v1 routes/dev/framework.yaml 0000644 00000000142 15117152224 0011521 0 ustar 00 _errors: resource: '@FrameworkBundle/Resources/config/routing/errors.xml' prefix: /_error routes/framework.yaml 0000644 00000000170 15117152224 0010744 0 ustar 00 when@dev: _errors: resource: '@FrameworkBundle/Resources/config/routing/errors.xml' prefix: /_error routes/web_profiler.yaml 0000644 00000000402 15117152224 0011424 0 ustar 00 when@dev: web_profiler_wdt: resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' prefix: /_wdt web_profiler_profiler: resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' prefix: /_profiler jwt/private.pem 0000644 00000006376 15117152224 0007541 0 ustar 00 -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-256-CBC,5008F8722207E7ACCE55A25C54FCD61B OGIZgEfWIoWF//Fd2im6nQMF39POyhKtRfJAexceS4Xo0V59fv5h9d121uvf0dHM skVpiMy48oZupjUxBkXNKwRUKVpkwVIM80Kptde4zdFzMAFs/GKb686K1b4yAutq l4vlgCTreRvLSm+VpgWIvJZL9hy0uskWGYOF8nhj9uOSGgvG8ea9tn5lZjFZXJrt C8dS1LGgH42XARiepp/P77wV+++l2UL77FxAIOIfhe2kVmS8P69f2KuhAhEvKMxG li+S0KFRrHoJC4b2J8j0DcZYbMkvqhDQUcv24TeNKA1qDvuhS8se1df9eyTUZJVQ 68WiwlJgqb4u3Cib9Z9vdzgyQrHyor7lctl3LTKi2e9uIi/E15TU17/A0I1ERIQa JPmyYyn7aSlaadHMj+dBcI7paSpzSGHPYRffoK5lF0THWIkIzu1Ck2g3hmPnSAL6 DH3DAGlOuJQvJV+6n0kteB9kBCR5knAHIAp6gDsGz/cQgcWQkzo4v0H+YpNeqfvK pQ3+RLxNm8doAeM5Kw/inu57ZIewiTkxv6kGLa35XbXZR3vFtCZO8PFDOovBZgT0 qmeq5H1XCU6Zu15YGqOjiNmAA9g9rq2LxAp4COlsVO84hpLByq+57z7q0vq5D1lP 9bNFdbYfUEdEFq8R2XL6lQX3IZHsxt87Ljvn7DZNKCWI5jU0z8NY0RVW4uBEUdVF nK79fo2DsjGl4GyoXvcxAnzVgc7JY5nddlarOAyThvmYubth5FNyLPFNq6F3Zy/Y xPgQ00r601t47RZeitGIKBpRVzO6EoMTbZWTYcCPsOqshUl1AfSdSfGn82du8hRy hPBP8TTSbP4MVcCr3/AccuDzzlMmtZ+PWiXhNFOZ/oKEsHHYPqLWQlEqXW960UKW HfaHaNalTcbpBq9/S3jGaW6hzMHaA5MhCyGBMRH0c1ZsPxOrXECXz0ZmXsxTIzIk DZv/QOudA6cNnfSdeGw5CugwikoUu3aL0lAlnIwvDfJg+fBZE4Tx0m6rjVmSCYGt mk5juCJYC6SlwO/mWhfZST4Z2+n6tY+Eykn9UeWZ6i08fpka+s79fp/F9c/G5SrV y3k02fN3U20V3HykzHhC8nAoQ2VfIUrnZvylC7rbvznBIJBTMr9HE3ap+LNTnmLB wSb1cuZb7K941UExMr4Eh/MC5COgDPgq47QhL50GUS40840pW0wWJH2Lbet9scnF zzy1OzfBJ18D1smaQWIbbwXwQQdzwFLIve9F8rTiFMirb+fq8NGz7dCodphY14SN Zw7bks9fTqVk3NiVUm4fRxEW7Als1vVYWiHo+l0swjOJOQLo3KfSYiU2VgBC9pQp n5ohbXGpDUTPjPAPRnoo/d7xqXjwpKkjhny9hWfLBm/LUfOJK8KzvRGQU8dLNGL3 KRjyZOhVu98F7jSTiyAH2avodsLwkGugKzKCt6zKeYgH+WDDjFMzydnGalv0m29k ujEvekVPG8oB2D94yJu/ueOhD9/16Zd58qjNtxaZfkve/x2jnAyeqZTu2YIHBmn6 c2MrTYc0pEA+P205s8k3PEUD8RYJUbEmVOD3V+1zL26Nc1LdrAHcbSAFrkOuF423 FrnSLgyk81Q75Ha+thiFVqjyrxqQyZaB3651gYmL7y3O+kj8kHU3ijaYJJWidEgk /kNIZ0CyISrughzXvkzrjTh/esereXwhILV3/779HTHUyk8cgsRer4z2AvYtHJMt cn1fTnfEe4LbxtbOCNHh0k/KlTQF0aUqLv93h0amv24sjxdxLnsKWFFXbkg+iPSb +68dUincWh88n61+RJ2W9WNNa3mEnQSA89PhKciIKcFrvSPE85wzzLbx0Rqa04G0 CMChmUmb3vpNS9liXjBqyX3PZWv5mfobD0Pd5g9GdSpu9CqYW7vnU7IXk0+A9u5C SpDEFbOo38PASw4VCwC4Rfi12Z7W0JtA39H8ttRaLaTlAoC4ksg6Cscwox9F8vfz zNxUpxepbx63i3f/6zWLRdHdzWfHZJyI/DQ2D+/kT2G04LZIoRJiPn/egCuTIJjV QpY6hapiq+kNo7jATtkSDjyZR/6N1S+cq4mVNpcEOR8tzJNdsCmicDEgCwB2CDiV L/fu3BksluMoeWfPqmLyKrCOXFv3I3AlYaftWM5LIFyt6QMICOMAW+7xUiQrtb+h 5r0l/4oF+6cu0qW27xNJS5O5i037cJ0S3igY4ngVUkBJZVQX5XMHdFR3arUF+eHS poYoesLZSusbAxMKnGBL0kIpXMVjowA4oy6nvmn5II/rj1oNlcrpCJhONxaJT+hE OZeT48EE2I91VHnEfUOpVhf0YtQ8B+32WWv63+1zWnJI9NEgo6H17RXyXJAPjH1z ck2BmD9eRySnRbMhg3506HGt1+TynkQ+s2ZuvsytAuxTn00uX2nJ8gCGU8vlfJFE UzSm4KAwSu275HIyy/RLebJQY9M3KDxEvy/C3nf/Yzkjo9xx9adW0FtJpVl/6dQs lbwMr7N8UQIonPiptZNchebDlZ7lJLAcHazdrKqFoTxHojiuqE4DNNs8/K1gQFLz 24hqG50rKchWDe72hgiQmSj9BtVk6Hwfqh+m+tZYD4FVsGCy+F/2FvJDAhf+ZWIO nUTbeeN6HbRRJQ29+U8WXM52nkoNvIL43mnLt7Qm3wE4eN7DLQUV6hAIy3fAIwDE 71Ij3ugandUtqIlMBhXzi9xoUqLypPUaCZrTlXzzGlGwsVKR1vmiuNVqUxZJU28R jxiLQqdJCIVvCi2WnRXe9f9H4dGQeRhpDw2zkU7PppAxOHnQoMfLzo5gwducil3k YMs7H3joVwS/gwO1uRGRohr0v+H5HzkBygdcbuJJgiVFjK/kNtE8VtRy5Y60F9tu gzI1P9ijKqG5NaoveD3/ckSrxn3IFXv9w2p81r5nr7YwFgN5ASGZynoYeBg4HTDW YqCp1TwiWpvD2yKFN88iM2yRQBSVSeQ+iRw9zcxLJBgRH4x4MHd88ncLySANSzVd NveVAjx6fXe++YzC4ZJZEQ0GlMk9bhpSXyyxwtKJqrTpwmXllHsTxxVULm5tMaFK X8w9ExGnAdGoHdbsGM9aLtNOatZVIuS8c0UEp79PVcQd2FkN8Jv+CP8X85SizYTX -----END RSA PRIVATE KEY----- jwt/public.pem 0000644 00000001440 15117152224 0007330 0 ustar 00 -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwRXR4uhl1c1TrfgDskMi XJuXg9HPsCEpJhJgd7a41wBnT4lhkNG9YSfvng91CG7evF0M0/dTXOz2/hv1vyiG elcWnDwO/Phca9OEcIwwgacH5KGFUIzA0eNm3x8p5t9dZ3ruW8ANibHY/YMfBjbX wcCURIA7AigDH6H53LjyeeZ75frpRC6Aw3s3WEZwXi70RqhmDwKeQ6IQPGyWlFam 8NyFGTe9sUIC8HHq87MoxwBQ/o0irqQ15d8c3FIdbi5Rw4PYV1GWlqH7+FmtOQUA 7RYFIVFmjTydaEAnwLf2vJyl7/Ac46Cywc0jeVifvS2w1Pdlqm/qVwbzXahY6Lan QoIB7aKxowzzXUdwNk+EnWSG9o/paUkeH+zHjra7A39MPzVl1DCgwBjdgMP9wjO8 hr23hiP0Gd7uBpoVKStlXUbB/AaVjIVNNQItdWDt5fA1HPlgNRREwmDxC3PLpRRn 9mC+QNtVQ3jxd0NpHYVgLhVjMSrMEf+43oSDehgko9IGMbceGP+x5FrvwPekDudi VEchmrFdl/jaomVAFP3pDotGfBQhXWcGIFDLdwRlZBk0Vg7BNkU6sJv//+Z9LILA aw6hFhkot3mjLiZEy53+NtMcjx0/BNkwfVQgbM0XZJpJ0tb4dJX5xvVZHdp0XmRV ytAuJaQiOHMl1bgy9p6KX3UCAwEAAQ== -----END PUBLIC KEY----- bundles.php 0000644 00000004047 15117152224 0006716 0 ustar 00 <?php return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true], FOS\RestBundle\FOSRestBundle::class => ['all' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'test' => true], Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true], Vich\UploaderBundle\VichUploaderBundle::class => ['all' => true], Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle::class => ['all' => true], Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], Gesdinet\JWTRefreshTokenBundle\GesdinetJWTRefreshTokenBundle::class => ['all' => true], Symfony\UX\Chartjs\ChartjsBundle::class => ['all' => true], Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], SymfonyCasts\Bundle\ResetPassword\SymfonyCastsResetPasswordBundle::class => ['all' => true], ]; routes.yaml 0000644 00000006542 15117152224 0006760 0 ustar 00 homepage: path: /api/v1/docs app_home: path: / controller: App\Controller\HomeController::index admin_dashboard: path: /admin controller: App\Controller\Admin\DashboardController::index client_location: path: /client_location methods: ['POST'] controller: App\Controller\HomeController::locatedClientAction find_buildings: path: /api/v1/find_buildings methods: ['POST'] controller: App\Controller\HomeController::findBuildAction app_payment: path: /payment/{token} controller: App\Controller\PaymentController::index payment_option_update: path: /api/v1/payment_option_update methods: [ 'POST' ] controller: App\Controller\PaymentController::updatePaymentOption app_payment_notify: path: /payment_notify controller: App\Controller\PaymentController::notifyPayment app_admin: path: /admin_check controller: App\Controller\AdminController::index app_make_offer: path: announce_offer/generate methods: ['POST'] controller: App\Controller\AnnounceDetailController::makeOffre app_get_announce_message: path: announce_chat/retrieved methods: ['POST'] controller: App\Controller\AnnounceDetailController::getMessages app_post_announce_message: path: announce_chat/post methods: ['POST'] controller: App\Controller\AnnounceDetailController::postMessage getStat: path: /api/admin/start methods: ['POST'] controller: App\Controller\Admin\DashboardController::getStat connected_user: path: /admin/user-connected/{user_id} methods: ['GET'] controller: App\Controller\Admin\DashboardController::getConnectedUser display_booking_room: path: /admin/display_booking/{booking_id} methods: ['GET'] controller: App\Controller\Admin\DashboardController::displayBooking app_login: path: /login controller: App\Controller\Admin\SecurityController::login app_file_manager: path: /file/manager controller: App\Controller\FileManagerController::index app_resend_email: path: /resend/email/{user} controller: App\Controller\RegistrationController::resendVerifyUserEmail app_logout: path: /logout controller: App\Controller\Admin\SecurityController::logout register: path: /api/register methods: ['POST'] controller: App\Controller\UserController::postUserAction app_register: path: /register controller: App\Controller\RegistrationController::register app_verify_email: path: /email/verify controller: App\Controller\RegistrationController::verifyUserEmail resetting: path: /resetting methods: ['PUT'] controller: App\Controller\UserController::resetPasswordAction authentication_token: path: /api/v1/authentication_token methods: ['POST'] gesdinet_jwt_refresh_token: path: /api/v1/authentication_token/refresh controller: gesdinet.jwtrefreshtoken::refresh app_user_log: path: /user/log controller: App\Controller\UserLogController::index external_service_sms: path: /external-service/sms methods: ['POST'] controller: App\Controller\ExternalServiceControler::postSMSAction graphiql: path: /docs/graphiql controller: api_platform.graphql.action.graphiql sendmail: path: /external-service/sendmail methods: ['POST'] controller: App\Controller\ExternalServiceControler::sendEmailAction app_building_detail: path: /building/detail controller: App\Controller\BuildingDetailController::index services.yaml 0000644 00000010427 15117152224 0007257 0 ustar 00 # This file is the entry point to configure your own services. # Files in the packages/ subdirectory configure your dependencies. # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: app.path.media_object: "media/" locale: 'fr' app_locales: fr|es|en|it|zh_CN| app.locales: [fr,es,en,it,zh_CN] publicDir: "%kernel.project_dir%/public" admin_email: 'info@loger.cm' admin_password: 'Loger237&@2023' app_client: 'loger-cmr' appUser: 'loger-cmr' createdDate: "2023" dateRange: "10" localRepository: 'loger-api' apiLink: 'public/api/v1/' serverKeyFcm: 'AAAAOSFzLyc:APA91bEBmPSN8xq2h4vYDs4wW5UfhoMTdy-Y3VclpUqIVkoe4svv5tsBLFxczyqdePX4YoIZ4gKuZxKBwM5MrrjdDG98oi5YwM00roWfuOTio_sc83TC1l5D8zyjfBgaschvw22o8EiK' app_domain: 'dev.loger.cm' services: # default configuration for services in *this* file _defaults: autowire: true # Automatically injects dependencies in your services. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. bind: $locales: '%app_locales%' $defaultLocale: '%locale%' $publicDir: "%kernel.project_dir%/public" $media_object: "%app.path.media_object" # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/*' exclude: - '../src/DependencyInjection/' - '../src/Entity/' - '../src/Kernel.php' App\Event\ExceptionListener: tags: - { name: kernel.event_listener, event: kernel.exception} # controllers are imported separately to make sure services can be injected # as action arguments even if you don't extend any base controller class App\Controller\: resource: '../src/Controller' tags: ['controller.service_arguments'] # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones # explicitly configure the service App\Event\DatabaseActivitySubscriber: tags: - name: 'doctrine.event_subscriber' # subscribers can define their priority in case multiple subscribers or listeners are associated # to the same event (default priority = 0; higher numbers = listener is run earlier) priority: 500 # you can also restrict listeners to a specific Doctrine connection connection: 'default' App\Service\SiteUpdateManager: arguments: $adminEmail: 'ngouaharonaldnmr@gmail.com' App\Event\LoginListener: tags: - { name: kernel.event_listener, event: security.interactive_login } App\Event\EasyAdminSubscriber: tags: - { name: kernel.event_subscriber, event: event.subscriber } App\Swagger\SwaggerDecorator: decorates: 'api_platform.swagger.normalizer.documentation' arguments: ['@App\Swagger\SwaggerDecorator.inner'] autoconfigure: true App\Swagger\SwaggerDecoratorAuth: decorates: 'api_platform.swagger.normalizer.documentation' arguments: ['@App\Swagger\SwaggerDecoratorAuth.inner'] autoconfigure: true App\Swagger\SwaggerDecoratorToken: decorates: 'api_platform.swagger.normalizer.documentation' arguments: ['@App\Swagger\SwaggerDecoratorToken.inner'] autoconfigure: true App\Swagger\SwaggerDecoratorUser: decorates: 'api_platform.swagger.normalizer.documentation' arguments: ['@App\Swagger\SwaggerDecoratorUser.inner'] autoconfigure: true App\Swagger\SwaggerDecoratorExternalService: decorates: 'api_platform.swagger.normalizer.documentation' arguments: ['@App\Swagger\SwaggerDecoratorExternalService.inner'] autoconfigure: true sensio_framework_extra.view.listener: alias: Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones home/logercm/dev.loger.cm/.git/config 0000644 00000000444 15117765670 0013461 0 ustar 00 [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = https://ngouaharonaldnmr@bitbucket.org/I_TRAVEL_GROUP/loger-api.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master Builder/ClassBuilder.php 0000644 00000011136 15120141155 0011214 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Builder; /** * Build PHP classes to generate config. * * @internal * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ class ClassBuilder { /** @var string */ private $namespace; /** @var string */ private $name; /** @var Property[] */ private $properties = []; /** @var Method[] */ private $methods = []; private $require = []; private $use = []; private $implements = []; private $allowExtraKeys = false; public function __construct(string $namespace, string $name) { $this->namespace = $namespace; $this->name = ucfirst($this->camelCase($name)).'Config'; } public function getDirectory(): string { return str_replace('\\', \DIRECTORY_SEPARATOR, $this->namespace); } public function getFilename(): string { return $this->name.'.php'; } public function build(): string { $rootPath = explode(\DIRECTORY_SEPARATOR, $this->getDirectory()); $require = ''; foreach ($this->require as $class) { // figure out relative path. $path = explode(\DIRECTORY_SEPARATOR, $class->getDirectory()); $path[] = $class->getFilename(); foreach ($rootPath as $key => $value) { if ($path[$key] !== $value) { break; } unset($path[$key]); } $require .= sprintf('require_once __DIR__.\DIRECTORY_SEPARATOR.\'%s\';', implode('\'.\DIRECTORY_SEPARATOR.\'', $path))."\n"; } $use = $require ? "\n" : ''; foreach (array_keys($this->use) as $statement) { $use .= sprintf('use %s;', $statement)."\n"; } $implements = [] === $this->implements ? '' : 'implements '.implode(', ', $this->implements); $body = ''; foreach ($this->properties as $property) { $body .= ' '.$property->getContent()."\n"; } foreach ($this->methods as $method) { $lines = explode("\n", $method->getContent()); foreach ($lines as $line) { $body .= ($line ? ' '.$line : '')."\n"; } } $content = strtr('<?php namespace NAMESPACE; REQUIREUSE /** * This class is automatically generated to help in creating a config. */ class CLASS IMPLEMENTS { BODY } ', ['NAMESPACE' => $this->namespace, 'REQUIRE' => $require, 'USE' => $use, 'CLASS' => $this->getName(), 'IMPLEMENTS' => $implements, 'BODY' => $body]); return $content; } public function addRequire(self $class): void { $this->require[] = $class; } public function addUse(string $class): void { $this->use[$class] = true; } public function addImplements(string $interface): void { $this->implements[] = '\\'.ltrim($interface, '\\'); } public function addMethod(string $name, string $body, array $params = []): void { $this->methods[] = new Method(strtr($body, ['NAME' => $this->camelCase($name)] + $params)); } public function addProperty(string $name, string $classType = null, string $defaultValue = null): Property { $property = new Property($name, '_' !== $name[0] ? $this->camelCase($name) : $name); if (null !== $classType) { $property->setType($classType); } $this->properties[] = $property; $defaultValue = null !== $defaultValue ? sprintf(' = %s', $defaultValue) : ''; $property->setContent(sprintf('private $%s%s;', $property->getName(), $defaultValue)); return $property; } public function getProperties(): array { return $this->properties; } private function camelCase(string $input): string { $output = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $input)))); return preg_replace('#\W#', '', $output); } public function getName(): string { return $this->name; } public function getNamespace(): string { return $this->namespace; } public function getFqcn(): string { return '\\'.$this->namespace.'\\'.$this->name; } public function setAllowExtraKeys(bool $allowExtraKeys): void { $this->allowExtraKeys = $allowExtraKeys; } public function shouldAllowExtraKeys(): bool { return $this->allowExtraKeys; } } Builder/ConfigBuilderGenerator.php 0000644 00000044340 15120141155 0013226 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Builder; use Symfony\Component\Config\Definition\ArrayNode; use Symfony\Component\Config\Definition\BooleanNode; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\EnumNode; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\FloatNode; use Symfony\Component\Config\Definition\IntegerNode; use Symfony\Component\Config\Definition\NodeInterface; use Symfony\Component\Config\Definition\PrototypedArrayNode; use Symfony\Component\Config\Definition\ScalarNode; use Symfony\Component\Config\Definition\VariableNode; use Symfony\Component\Config\Loader\ParamConfigurator; /** * Generate ConfigBuilders to help create valid config. * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ class ConfigBuilderGenerator implements ConfigBuilderGeneratorInterface { /** * @var ClassBuilder[] */ private $classes; private $outputDir; public function __construct(string $outputDir) { $this->outputDir = $outputDir; } /** * @return \Closure that will return the root config class */ public function build(ConfigurationInterface $configuration): \Closure { $this->classes = []; $rootNode = $configuration->getConfigTreeBuilder()->buildTree(); $rootClass = new ClassBuilder('Symfony\\Config', $rootNode->getName()); $path = $this->getFullPath($rootClass); if (!is_file($path)) { // Generate the class if the file not exists $this->classes[] = $rootClass; $this->buildNode($rootNode, $rootClass, $this->getSubNamespace($rootClass)); $rootClass->addImplements(ConfigBuilderInterface::class); $rootClass->addMethod('getExtensionAlias', ' public function NAME(): string { return \'ALIAS\'; }', ['ALIAS' => $rootNode->getPath()]); $this->writeClasses(); } $loader = \Closure::fromCallable(function () use ($path, $rootClass) { require_once $path; $className = $rootClass->getFqcn(); return new $className(); }); return $loader; } private function getFullPath(ClassBuilder $class): string { $directory = $this->outputDir.\DIRECTORY_SEPARATOR.$class->getDirectory(); if (!is_dir($directory)) { @mkdir($directory, 0777, true); } return $directory.\DIRECTORY_SEPARATOR.$class->getFilename(); } private function writeClasses(): void { foreach ($this->classes as $class) { $this->buildConstructor($class); $this->buildToArray($class); if ($class->getProperties()) { $class->addProperty('_usedProperties', null, '[]'); } $this->buildSetExtraKey($class); file_put_contents($this->getFullPath($class), $class->build()); } $this->classes = []; } private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace): void { if (!$node instanceof ArrayNode) { throw new \LogicException('The node was expected to be an ArrayNode. This Configuration includes an edge case not supported yet.'); } foreach ($node->getChildren() as $child) { switch (true) { case $child instanceof ScalarNode: $this->handleScalarNode($child, $class); break; case $child instanceof PrototypedArrayNode: $this->handlePrototypedArrayNode($child, $class, $namespace); break; case $child instanceof VariableNode: $this->handleVariableNode($child, $class); break; case $child instanceof ArrayNode: $this->handleArrayNode($child, $class, $namespace); break; default: throw new \RuntimeException(sprintf('Unknown node "%s".', \get_class($child))); } } } private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace): void { $childClass = new ClassBuilder($namespace, $node->getName()); $childClass->setAllowExtraKeys($node->shouldIgnoreExtraKeys()); $class->addRequire($childClass); $this->classes[] = $childClass; $hasNormalizationClosures = $this->hasNormalizationClosures($node); $property = $class->addProperty( $node->getName(), $this->getType($childClass->getFqcn(), $hasNormalizationClosures) ); $body = $hasNormalizationClosures ? ' /** * @return CLASS|$this */ public function NAME($value = []) { if (!\is_array($value)) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = $value; return $this; } if (!$this->PROPERTY instanceof CLASS) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = new CLASS($value); } elseif (0 < \func_num_args()) { throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); } return $this->PROPERTY; }' : ' public function NAME(array $value = []): CLASS { if (null === $this->PROPERTY) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = new CLASS($value); } elseif (0 < \func_num_args()) { throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); } return $this->PROPERTY; }'; $class->addUse(InvalidConfigurationException::class); $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); } private function handleVariableNode(VariableNode $node, ClassBuilder $class): void { $comment = $this->getComment($node); $property = $class->addProperty($node->getName()); $class->addUse(ParamConfigurator::class); $body = ' /** COMMENT * @return $this */ public function NAME($valueDEFAULT): self { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = $value; return $this; }'; $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment, 'DEFAULT' => $node->hasDefaultValue() ? ' = '.var_export($node->getDefaultValue(), true) : '']); } private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace): void { $name = $this->getSingularName($node); $prototype = $node->getPrototype(); $methodName = $name; $parameterType = $this->getParameterType($prototype); if (null !== $parameterType || $prototype instanceof ScalarNode) { $class->addUse(ParamConfigurator::class); $property = $class->addProperty($node->getName()); if (null === $key = $node->getKeyAttribute()) { // This is an array of values; don't use singular name $body = ' /** * @param ParamConfigurator|list<TYPE|ParamConfigurator> $value * @return $this */ public function NAME($value): self { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = $value; return $this; }'; $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : $parameterType]); } else { $body = ' /** * @param ParamConfigurator|TYPE $value * @return $this */ public function NAME(string $VAR, $VALUE): self { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY[$VAR] = $VALUE; return $this; }'; $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : $parameterType, 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']); } return; } $childClass = new ClassBuilder($namespace, $name); if ($prototype instanceof ArrayNode) { $childClass->setAllowExtraKeys($prototype->shouldIgnoreExtraKeys()); } $class->addRequire($childClass); $this->classes[] = $childClass; $hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype); $property = $class->addProperty( $node->getName(), $this->getType($childClass->getFqcn().'[]', $hasNormalizationClosures) ); if (null === $key = $node->getKeyAttribute()) { $body = $hasNormalizationClosures ? ' /** * @return CLASS|$this */ public function NAME($value = []) { $this->_usedProperties[\'PROPERTY\'] = true; if (!\is_array($value)) { $this->PROPERTY[] = $value; return $this; } return $this->PROPERTY[] = new CLASS($value); }' : ' public function NAME(array $value = []): CLASS { $this->_usedProperties[\'PROPERTY\'] = true; return $this->PROPERTY[] = new CLASS($value); }'; $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); } else { $body = $hasNormalizationClosures ? ' /** * @return CLASS|$this */ public function NAME(string $VAR, $VALUE = []) { if (!\is_array($VALUE)) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY[$VAR] = $VALUE; return $this; } if (!isset($this->PROPERTY[$VAR]) || !$this->PROPERTY[$VAR] instanceof CLASS) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY[$VAR] = new CLASS($VALUE); } elseif (1 < \func_num_args()) { throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); } return $this->PROPERTY[$VAR]; }' : ' public function NAME(string $VAR, array $VALUE = []): CLASS { if (!isset($this->PROPERTY[$VAR])) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY[$VAR] = new CLASS($VALUE); } elseif (1 < \func_num_args()) { throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); } return $this->PROPERTY[$VAR]; }'; $class->addUse(InvalidConfigurationException::class); $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']); } $this->buildNode($prototype, $childClass, $namespace.'\\'.$childClass->getName()); } private function handleScalarNode(ScalarNode $node, ClassBuilder $class): void { $comment = $this->getComment($node); $property = $class->addProperty($node->getName()); $class->addUse(ParamConfigurator::class); $body = ' /** COMMENT * @return $this */ public function NAME($value): self { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = $value; return $this; }'; $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]); } private function getParameterType(NodeInterface $node): ?string { if ($node instanceof BooleanNode) { return 'bool'; } if ($node instanceof IntegerNode) { return 'int'; } if ($node instanceof FloatNode) { return 'float'; } if ($node instanceof EnumNode) { return ''; } if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) { // This is just an array of variables return 'array'; } if ($node instanceof VariableNode) { // mixed return ''; } return null; } private function getComment(VariableNode $node): string { $comment = ''; if ('' !== $info = (string) $node->getInfo()) { $comment .= ' * '.$info."\n"; } foreach ((array) ($node->getExample() ?? []) as $example) { $comment .= ' * @example '.$example."\n"; } if ('' !== $default = $node->getDefaultValue()) { $comment .= ' * @default '.(null === $default ? 'null' : var_export($default, true))."\n"; } if ($node instanceof EnumNode) { $comment .= sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_map(function ($a) { return var_export($a, true); }, $node->getValues())))."\n"; } else { $parameterType = $this->getParameterType($node); if (null === $parameterType || '' === $parameterType) { $parameterType = 'mixed'; } $comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n"; } if ($node->isDeprecated()) { $comment .= ' * @deprecated '.$node->getDeprecation($node->getName(), $node->getParent()->getName())['message']."\n"; } return $comment; } /** * Pick a good singular name. */ private function getSingularName(PrototypedArrayNode $node): string { $name = $node->getName(); if ('s' !== substr($name, -1)) { return $name; } $parent = $node->getParent(); $mappings = $parent instanceof ArrayNode ? $parent->getXmlRemappings() : []; foreach ($mappings as $map) { if ($map[1] === $name) { $name = $map[0]; break; } } return $name; } private function buildToArray(ClassBuilder $class): void { $body = '$output = [];'; foreach ($class->getProperties() as $p) { $code = '$this->PROPERTY'; if (null !== $p->getType()) { if ($p->isArray()) { $code = $p->areScalarsAllowed() ? 'array_map(function ($v) { return $v instanceof CLASS ? $v->toArray() : $v; }, $this->PROPERTY)' : 'array_map(function ($v) { return $v->toArray(); }, $this->PROPERTY)' ; } else { $code = $p->areScalarsAllowed() ? '$this->PROPERTY instanceof CLASS ? $this->PROPERTY->toArray() : $this->PROPERTY' : '$this->PROPERTY->toArray()' ; } } $body .= strtr(' if (isset($this->_usedProperties[\'PROPERTY\'])) { $output[\'ORG_NAME\'] = '.$code.'; }', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName(), 'CLASS' => $p->getType()]); } $extraKeys = $class->shouldAllowExtraKeys() ? ' + $this->_extraKeys' : ''; $class->addMethod('toArray', ' public function NAME(): array { '.$body.' return $output'.$extraKeys.'; }'); } private function buildConstructor(ClassBuilder $class): void { $body = ''; foreach ($class->getProperties() as $p) { $code = '$value[\'ORG_NAME\']'; if (null !== $p->getType()) { if ($p->isArray()) { $code = $p->areScalarsAllowed() ? 'array_map(function ($v) { return \is_array($v) ? new '.$p->getType().'($v) : $v; }, $value[\'ORG_NAME\'])' : 'array_map(function ($v) { return new '.$p->getType().'($v); }, $value[\'ORG_NAME\'])' ; } else { $code = $p->areScalarsAllowed() ? '\is_array($value[\'ORG_NAME\']) ? new '.$p->getType().'($value[\'ORG_NAME\']) : $value[\'ORG_NAME\']' : 'new '.$p->getType().'($value[\'ORG_NAME\'])' ; } } $body .= strtr(' if (array_key_exists(\'ORG_NAME\', $value)) { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = '.$code.'; unset($value[\'ORG_NAME\']); } ', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]); } if ($class->shouldAllowExtraKeys()) { $body .= ' $this->_extraKeys = $value; '; } else { $body .= ' if ([] !== $value) { throw new InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__).implode(\', \', array_keys($value))); }'; $class->addUse(InvalidConfigurationException::class); } $class->addMethod('__construct', ' public function __construct(array $value = []) {'.$body.' }'); } private function buildSetExtraKey(ClassBuilder $class): void { if (!$class->shouldAllowExtraKeys()) { return; } $class->addUse(ParamConfigurator::class); $class->addProperty('_extraKeys'); $class->addMethod('set', ' /** * @param ParamConfigurator|mixed $value * @return $this */ public function NAME(string $key, $value): self { $this->_extraKeys[$key] = $value; return $this; }'); } private function getSubNamespace(ClassBuilder $rootClass): string { return sprintf('%s\\%s', $rootClass->getNamespace(), substr($rootClass->getName(), 0, -6)); } private function hasNormalizationClosures(NodeInterface $node): bool { try { $r = new \ReflectionProperty($node, 'normalizationClosures'); } catch (\ReflectionException $e) { return false; } $r->setAccessible(true); return [] !== $r->getValue($node); } private function getType(string $classType, bool $hasNormalizationClosures): string { return $classType.($hasNormalizationClosures ? '|scalar' : ''); } } Builder/ConfigBuilderGeneratorInterface.php 0000644 00000001234 15120141155 0015042 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Builder; use Symfony\Component\Config\Definition\ConfigurationInterface; /** * Generates ConfigBuilders to help create valid config. * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ interface ConfigBuilderGeneratorInterface { /** * @return \Closure that will return the root config class */ public function build(ConfigurationInterface $configuration): \Closure; } Builder/ConfigBuilderInterface.php 0000644 00000001276 15120141155 0013201 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Builder; /** * A ConfigBuilder provides helper methods to build a large complex array. * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ interface ConfigBuilderInterface { /** * Gets all configuration represented as an array. */ public function toArray(): array; /** * Gets the alias for the extension which config we are building. */ public function getExtensionAlias(): string; } Builder/Method.php 0000644 00000001164 15120141155 0010060 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Builder; /** * Represents a method when building classes. * * @internal * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ class Method { private $content; public function __construct(string $content) { $this->content = $content; } public function getContent(): string { return $this->content; } } Builder/Property.php 0000644 00000003351 15120141155 0010464 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Builder; /** * Represents a property when building classes. * * @internal * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ class Property { private $name; private $originalName; private $array = false; private $scalarsAllowed = false; private $type = null; private $content; public function __construct(string $originalName, string $name) { $this->name = $name; $this->originalName = $originalName; } public function getName(): string { return $this->name; } public function getOriginalName(): string { return $this->originalName; } public function setType(string $type): void { $this->array = false; $this->type = $type; if ('|scalar' === substr($type, -7)) { $this->scalarsAllowed = true; $this->type = $type = substr($type, 0, -7); } if ('[]' === substr($type, -2)) { $this->array = true; $this->type = substr($type, 0, -2); } } public function getType(): ?string { return $this->type; } public function getContent(): ?string { return $this->content; } public function setContent(string $content): void { $this->content = $content; } public function isArray(): bool { return $this->array; } public function areScalarsAllowed(): bool { return $this->scalarsAllowed; } } Definition/Builder/ArrayNodeDefinition.php 0000644 00000037503 15120141155 0014633 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\ArrayNode; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; use Symfony\Component\Config\Definition\PrototypedArrayNode; /** * This class provides a fluent interface for defining an array node. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface { protected $performDeepMerging = true; protected $ignoreExtraKeys = false; protected $removeExtraKeys = true; protected $children = []; protected $prototype; protected $atLeastOne = false; protected $allowNewKeys = true; protected $key; protected $removeKeyItem; protected $addDefaults = false; protected $addDefaultChildren = false; protected $nodeBuilder; protected $normalizeKeys = true; /** * {@inheritdoc} */ public function __construct(?string $name, NodeParentInterface $parent = null) { parent::__construct($name, $parent); $this->nullEquivalent = []; $this->trueEquivalent = []; } /** * {@inheritdoc} */ public function setBuilder(NodeBuilder $builder) { $this->nodeBuilder = $builder; } /** * {@inheritdoc} */ public function children() { return $this->getNodeBuilder(); } /** * Sets a prototype for child nodes. * * @return NodeDefinition */ public function prototype(string $type) { return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); } /** * @return VariableNodeDefinition */ public function variablePrototype() { return $this->prototype('variable'); } /** * @return ScalarNodeDefinition */ public function scalarPrototype() { return $this->prototype('scalar'); } /** * @return BooleanNodeDefinition */ public function booleanPrototype() { return $this->prototype('boolean'); } /** * @return IntegerNodeDefinition */ public function integerPrototype() { return $this->prototype('integer'); } /** * @return FloatNodeDefinition */ public function floatPrototype() { return $this->prototype('float'); } /** * @return ArrayNodeDefinition */ public function arrayPrototype() { return $this->prototype('array'); } /** * @return EnumNodeDefinition */ public function enumPrototype() { return $this->prototype('enum'); } /** * Adds the default value if the node is not set in the configuration. * * This method is applicable to concrete nodes only (not to prototype nodes). * If this function has been called and the node is not set during the finalization * phase, it's default value will be derived from its children default values. * * @return $this */ public function addDefaultsIfNotSet() { $this->addDefaults = true; return $this; } /** * Adds children with a default value when none are defined. * * This method is applicable to prototype nodes only. * * @param int|string|array|null $children The number of children|The child name|The children names to be added * * @return $this */ public function addDefaultChildrenIfNoneSet($children = null) { $this->addDefaultChildren = $children; return $this; } /** * Requires the node to have at least one element. * * This method is applicable to prototype nodes only. * * @return $this */ public function requiresAtLeastOneElement() { $this->atLeastOne = true; return $this; } /** * Disallows adding news keys in a subsequent configuration. * * If used all keys have to be defined in the same configuration file. * * @return $this */ public function disallowNewKeysInSubsequentConfigs() { $this->allowNewKeys = false; return $this; } /** * Sets a normalization rule for XML configurations. * * @param string $singular The key to remap * @param string|null $plural The plural of the key for irregular plurals * * @return $this */ public function fixXmlConfig(string $singular, string $plural = null) { $this->normalization()->remap($singular, $plural); return $this; } /** * Sets the attribute which value is to be used as key. * * This is useful when you have an indexed array that should be an * associative array. You can select an item from within the array * to be the key of the particular item. For example, if "id" is the * "key", then: * * [ * ['id' => 'my_name', 'foo' => 'bar'], * ]; * * becomes * * [ * 'my_name' => ['foo' => 'bar'], * ]; * * If you'd like "'id' => 'my_name'" to still be present in the resulting * array, then you can set the second argument of this method to false. * * This method is applicable to prototype nodes only. * * @param string $name The name of the key * @param bool $removeKeyItem Whether or not the key item should be removed * * @return $this */ public function useAttributeAsKey(string $name, bool $removeKeyItem = true) { $this->key = $name; $this->removeKeyItem = $removeKeyItem; return $this; } /** * Sets whether the node can be unset. * * @return $this */ public function canBeUnset(bool $allow = true) { $this->merge()->allowUnset($allow); return $this; } /** * Adds an "enabled" boolean to enable the current section. * * By default, the section is disabled. If any configuration is specified then * the node will be automatically enabled: * * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden * enableableArrayNode: ~ # The config is enabled & use the default values * enableableArrayNode: true # The config is enabled & use the default values * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden * enableableArrayNode: {enabled: false, ...} # The config is disabled * enableableArrayNode: false # The config is disabled * * @return $this */ public function canBeEnabled() { $this ->addDefaultsIfNotSet() ->treatFalseLike(['enabled' => false]) ->treatTrueLike(['enabled' => true]) ->treatNullLike(['enabled' => true]) ->beforeNormalization() ->ifArray() ->then(function (array $v) { $v['enabled'] = $v['enabled'] ?? true; return $v; }) ->end() ->children() ->booleanNode('enabled') ->defaultFalse() ; return $this; } /** * Adds an "enabled" boolean to enable the current section. * * By default, the section is enabled. * * @return $this */ public function canBeDisabled() { $this ->addDefaultsIfNotSet() ->treatFalseLike(['enabled' => false]) ->treatTrueLike(['enabled' => true]) ->treatNullLike(['enabled' => true]) ->children() ->booleanNode('enabled') ->defaultTrue() ; return $this; } /** * Disables the deep merging of the node. * * @return $this */ public function performNoDeepMerging() { $this->performDeepMerging = false; return $this; } /** * Allows extra config keys to be specified under an array without * throwing an exception. * * Those config values are ignored and removed from the resulting * array. This should be used only in special cases where you want * to send an entire configuration array through a special tree that * processes only part of the array. * * @param bool $remove Whether to remove the extra keys * * @return $this */ public function ignoreExtraKeys(bool $remove = true) { $this->ignoreExtraKeys = true; $this->removeExtraKeys = $remove; return $this; } /** * Sets whether to enable key normalization. * * @return $this */ public function normalizeKeys(bool $bool) { $this->normalizeKeys = $bool; return $this; } /** * {@inheritdoc} */ public function append(NodeDefinition $node) { $this->children[$node->name] = $node->setParent($this); return $this; } /** * Returns a node builder to be used to add children and prototype. * * @return NodeBuilder */ protected function getNodeBuilder() { if (null === $this->nodeBuilder) { $this->nodeBuilder = new NodeBuilder(); } return $this->nodeBuilder->setParent($this); } /** * {@inheritdoc} */ protected function createNode() { if (null === $this->prototype) { $node = new ArrayNode($this->name, $this->parent, $this->pathSeparator); $this->validateConcreteNode($node); $node->setAddIfNotSet($this->addDefaults); foreach ($this->children as $child) { $child->parent = $node; $node->addChild($child->getNode()); } } else { $node = new PrototypedArrayNode($this->name, $this->parent, $this->pathSeparator); $this->validatePrototypeNode($node); if (null !== $this->key) { $node->setKeyAttribute($this->key, $this->removeKeyItem); } if (true === $this->atLeastOne || false === $this->allowEmptyValue) { $node->setMinNumberOfElements(1); } if ($this->default) { if (!\is_array($this->defaultValue)) { throw new \InvalidArgumentException(sprintf('%s: the default value of an array node has to be an array.', $node->getPath())); } $node->setDefaultValue($this->defaultValue); } if (false !== $this->addDefaultChildren) { $node->setAddChildrenIfNoneSet($this->addDefaultChildren); if ($this->prototype instanceof static && null === $this->prototype->prototype) { $this->prototype->addDefaultsIfNotSet(); } } $this->prototype->parent = $node; $node->setPrototype($this->prototype->getNode()); } $node->setAllowNewKeys($this->allowNewKeys); $node->addEquivalentValue(null, $this->nullEquivalent); $node->addEquivalentValue(true, $this->trueEquivalent); $node->addEquivalentValue(false, $this->falseEquivalent); $node->setPerformDeepMerging($this->performDeepMerging); $node->setRequired($this->required); $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); $node->setNormalizeKeys($this->normalizeKeys); if ($this->deprecation) { $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); } if (null !== $this->normalization) { $node->setNormalizationClosures($this->normalization->before); $node->setXmlRemappings($this->normalization->remappings); } if (null !== $this->merge) { $node->setAllowOverwrite($this->merge->allowOverwrite); $node->setAllowFalse($this->merge->allowFalse); } if (null !== $this->validation) { $node->setFinalValidationClosures($this->validation->rules); } return $node; } /** * Validate the configuration of a concrete node. * * @throws InvalidDefinitionException */ protected function validateConcreteNode(ArrayNode $node) { $path = $node->getPath(); if (null !== $this->key) { throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s".', $path)); } if (false === $this->allowEmptyValue) { throw new InvalidDefinitionException(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s".', $path)); } if (true === $this->atLeastOne) { throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s".', $path)); } if ($this->default) { throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s".', $path)); } if (false !== $this->addDefaultChildren) { throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s".', $path)); } } /** * Validate the configuration of a prototype node. * * @throws InvalidDefinitionException */ protected function validatePrototypeNode(PrototypedArrayNode $node) { $path = $node->getPath(); if ($this->addDefaults) { throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s".', $path)); } if (false !== $this->addDefaultChildren) { if ($this->default) { throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s".', $path)); } if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s".', $path)); } if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s".', $path)); } } } /** * @return NodeDefinition[] */ public function getChildNodeDefinitions() { return $this->children; } /** * Finds a node defined by the given $nodePath. * * @param string $nodePath The path of the node to find. e.g "doctrine.orm.mappings" */ public function find(string $nodePath): NodeDefinition { $firstPathSegment = (false === $pathSeparatorPos = strpos($nodePath, $this->pathSeparator)) ? $nodePath : substr($nodePath, 0, $pathSeparatorPos); if (null === $node = ($this->children[$firstPathSegment] ?? null)) { throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the current node "%s".', $firstPathSegment, $this->name)); } if (false === $pathSeparatorPos) { return $node; } return $node->find(substr($nodePath, $pathSeparatorPos + \strlen($this->pathSeparator))); } } Definition/Builder/BooleanNodeDefinition.php 0000644 00000002412 15120141155 0015123 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\BooleanNode; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; /** * This class provides a fluent interface for defining a node. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class BooleanNodeDefinition extends ScalarNodeDefinition { /** * {@inheritdoc} */ public function __construct(?string $name, NodeParentInterface $parent = null) { parent::__construct($name, $parent); $this->nullEquivalent = true; } /** * Instantiate a Node. * * @return BooleanNode */ protected function instantiateNode() { return new BooleanNode($this->name, $this->parent, $this->pathSeparator); } /** * {@inheritdoc} * * @throws InvalidDefinitionException */ public function cannotBeEmpty() { throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); } } Definition/Builder/BuilderAwareInterface.php 0000644 00000001103 15120141155 0015110 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * An interface that can be implemented by nodes which build other nodes. * * @author Roland Franssen <franssen.roland@gmail.com> */ interface BuilderAwareInterface { /** * Sets a custom children builder. */ public function setBuilder(NodeBuilder $builder); } Definition/Builder/EnumNodeDefinition.php 0000644 00000002333 15120141155 0014452 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\EnumNode; /** * Enum Node Definition. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class EnumNodeDefinition extends ScalarNodeDefinition { private $values; /** * @return $this */ public function values(array $values) { $values = array_unique($values); if (empty($values)) { throw new \InvalidArgumentException('->values() must be called with at least one value.'); } $this->values = $values; return $this; } /** * Instantiate a Node. * * @return EnumNode * * @throws \RuntimeException */ protected function instantiateNode() { if (null === $this->values) { throw new \RuntimeException('You must call ->values() on enum nodes.'); } return new EnumNode($this->name, $this->parent, $this->values, $this->pathSeparator); } } Definition/Builder/ExprBuilder.php 0000644 00000012343 15120141155 0013156 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\Exception\UnsetKeyException; /** * This class builds an if expression. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Christophe Coevoet <stof@notk.org> */ class ExprBuilder { protected $node; public $ifPart; public $thenPart; public function __construct(NodeDefinition $node) { $this->node = $node; } /** * Marks the expression as being always used. * * @return $this */ public function always(\Closure $then = null) { $this->ifPart = function () { return true; }; if (null !== $then) { $this->thenPart = $then; } return $this; } /** * Sets a closure to use as tests. * * The default one tests if the value is true. * * @return $this */ public function ifTrue(\Closure $closure = null) { if (null === $closure) { $closure = function ($v) { return true === $v; }; } $this->ifPart = $closure; return $this; } /** * Tests if the value is a string. * * @return $this */ public function ifString() { $this->ifPart = function ($v) { return \is_string($v); }; return $this; } /** * Tests if the value is null. * * @return $this */ public function ifNull() { $this->ifPart = function ($v) { return null === $v; }; return $this; } /** * Tests if the value is empty. * * @return $this */ public function ifEmpty() { $this->ifPart = function ($v) { return empty($v); }; return $this; } /** * Tests if the value is an array. * * @return $this */ public function ifArray() { $this->ifPart = function ($v) { return \is_array($v); }; return $this; } /** * Tests if the value is in an array. * * @return $this */ public function ifInArray(array $array) { $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); }; return $this; } /** * Tests if the value is not in an array. * * @return $this */ public function ifNotInArray(array $array) { $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); }; return $this; } /** * Transforms variables of any type into an array. * * @return $this */ public function castToArray() { $this->ifPart = function ($v) { return !\is_array($v); }; $this->thenPart = function ($v) { return [$v]; }; return $this; } /** * Sets the closure to run if the test pass. * * @return $this */ public function then(\Closure $closure) { $this->thenPart = $closure; return $this; } /** * Sets a closure returning an empty array. * * @return $this */ public function thenEmptyArray() { $this->thenPart = function () { return []; }; return $this; } /** * Sets a closure marking the value as invalid at processing time. * * if you want to add the value of the node in your message just use a %s placeholder. * * @return $this * * @throws \InvalidArgumentException */ public function thenInvalid(string $message) { $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; return $this; } /** * Sets a closure unsetting this key of the array at processing time. * * @return $this * * @throws UnsetKeyException */ public function thenUnset() { $this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); }; return $this; } /** * Returns the related node. * * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition * * @throws \RuntimeException */ public function end() { if (null === $this->ifPart) { throw new \RuntimeException('You must specify an if part.'); } if (null === $this->thenPart) { throw new \RuntimeException('You must specify a then part.'); } return $this->node; } /** * Builds the expressions. * * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build * * @return array */ public static function buildExpressions(array $expressions) { foreach ($expressions as $k => $expr) { if ($expr instanceof self) { $if = $expr->ifPart; $then = $expr->thenPart; $expressions[$k] = function ($v) use ($if, $then) { return $if($v) ? $then($v) : $v; }; } } return $expressions; } } Definition/Builder/FloatNodeDefinition.php 0000644 00000001406 15120141155 0014613 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\FloatNode; /** * This class provides a fluent interface for defining a float node. * * @author Jeanmonod David <david.jeanmonod@gmail.com> */ class FloatNodeDefinition extends NumericNodeDefinition { /** * Instantiates a Node. * * @return FloatNode */ protected function instantiateNode() { return new FloatNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); } } Definition/Builder/IntegerNodeDefinition.php 0000644 00000001421 15120141155 0015140 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\IntegerNode; /** * This class provides a fluent interface for defining an integer node. * * @author Jeanmonod David <david.jeanmonod@gmail.com> */ class IntegerNodeDefinition extends NumericNodeDefinition { /** * Instantiates a Node. * * @return IntegerNode */ protected function instantiateNode() { return new IntegerNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); } } Definition/Builder/MergeBuilder.php 0000644 00000002321 15120141155 0013272 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * This class builds merge conditions. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class MergeBuilder { protected $node; public $allowFalse = false; public $allowOverwrite = true; public function __construct(NodeDefinition $node) { $this->node = $node; } /** * Sets whether the node can be unset. * * @return $this */ public function allowUnset(bool $allow = true) { $this->allowFalse = $allow; return $this; } /** * Sets whether the node can be overwritten. * * @return $this */ public function denyOverwrite(bool $deny = true) { $this->allowOverwrite = !$deny; return $this; } /** * Returns the related node. * * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition */ public function end() { return $this->node; } } Definition/Builder/NodeBuilder.php 0000644 00000012007 15120141155 0013122 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * This class provides a fluent interface for building a node. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class NodeBuilder implements NodeParentInterface { protected $parent; protected $nodeMapping; public function __construct() { $this->nodeMapping = [ 'variable' => VariableNodeDefinition::class, 'scalar' => ScalarNodeDefinition::class, 'boolean' => BooleanNodeDefinition::class, 'integer' => IntegerNodeDefinition::class, 'float' => FloatNodeDefinition::class, 'array' => ArrayNodeDefinition::class, 'enum' => EnumNodeDefinition::class, ]; } /** * Set the parent node. * * @return $this */ public function setParent(ParentNodeDefinitionInterface $parent = null) { $this->parent = $parent; return $this; } /** * Creates a child array node. * * @return ArrayNodeDefinition */ public function arrayNode(string $name) { return $this->node($name, 'array'); } /** * Creates a child scalar node. * * @return ScalarNodeDefinition */ public function scalarNode(string $name) { return $this->node($name, 'scalar'); } /** * Creates a child Boolean node. * * @return BooleanNodeDefinition */ public function booleanNode(string $name) { return $this->node($name, 'boolean'); } /** * Creates a child integer node. * * @return IntegerNodeDefinition */ public function integerNode(string $name) { return $this->node($name, 'integer'); } /** * Creates a child float node. * * @return FloatNodeDefinition */ public function floatNode(string $name) { return $this->node($name, 'float'); } /** * Creates a child EnumNode. * * @return EnumNodeDefinition */ public function enumNode(string $name) { return $this->node($name, 'enum'); } /** * Creates a child variable node. * * @return VariableNodeDefinition */ public function variableNode(string $name) { return $this->node($name, 'variable'); } /** * Returns the parent node. * * @return NodeDefinition&ParentNodeDefinitionInterface */ public function end() { return $this->parent; } /** * Creates a child node. * * @return NodeDefinition * * @throws \RuntimeException When the node type is not registered * @throws \RuntimeException When the node class is not found */ public function node(?string $name, string $type) { $class = $this->getNodeClass($type); $node = new $class($name); $this->append($node); return $node; } /** * Appends a node definition. * * Usage: * * $node = new ArrayNodeDefinition('name') * ->children() * ->scalarNode('foo')->end() * ->scalarNode('baz')->end() * ->append($this->getBarNodeDefinition()) * ->end() * ; * * @return $this */ public function append(NodeDefinition $node) { if ($node instanceof BuilderAwareInterface) { $builder = clone $this; $builder->setParent(null); $node->setBuilder($builder); } if (null !== $this->parent) { $this->parent->append($node); // Make this builder the node parent to allow for a fluid interface $node->setParent($this); } return $this; } /** * Adds or overrides a node Type. * * @param string $type The name of the type * @param string $class The fully qualified name the node definition class * * @return $this */ public function setNodeClass(string $type, string $class) { $this->nodeMapping[strtolower($type)] = $class; return $this; } /** * Returns the class name of the node definition. * * @return string * * @throws \RuntimeException When the node type is not registered * @throws \RuntimeException When the node class is not found */ protected function getNodeClass(string $type) { $type = strtolower($type); if (!isset($this->nodeMapping[$type])) { throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); } $class = $this->nodeMapping[$type]; if (!class_exists($class)) { throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); } return $class; } } Definition/Builder/NodeDefinition.php 0000644 00000021244 15120141155 0013627 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; use Symfony\Component\Config\Definition\NodeInterface; /** * This class provides a fluent interface for defining a node. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ abstract class NodeDefinition implements NodeParentInterface { protected $name; protected $normalization; protected $validation; protected $defaultValue; protected $default = false; protected $required = false; protected $deprecation = []; protected $merge; protected $allowEmptyValue = true; protected $nullEquivalent; protected $trueEquivalent = true; protected $falseEquivalent = false; protected $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR; protected $parent; protected $attributes = []; public function __construct(?string $name, NodeParentInterface $parent = null) { $this->parent = $parent; $this->name = $name; } /** * Sets the parent node. * * @return $this */ public function setParent(NodeParentInterface $parent) { $this->parent = $parent; return $this; } /** * Sets info message. * * @return $this */ public function info(string $info) { return $this->attribute('info', $info); } /** * Sets example configuration. * * @param string|array $example * * @return $this */ public function example($example) { return $this->attribute('example', $example); } /** * Sets an attribute on the node. * * @param mixed $value * * @return $this */ public function attribute(string $key, $value) { $this->attributes[$key] = $value; return $this; } /** * Returns the parent node. * * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null */ public function end() { return $this->parent; } /** * Creates the node. * * @return NodeInterface */ public function getNode(bool $forceRootNode = false) { if ($forceRootNode) { $this->parent = null; } if (null !== $this->normalization) { $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); } if (null !== $this->validation) { $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); } $node = $this->createNode(); if ($node instanceof BaseNode) { $node->setAttributes($this->attributes); } return $node; } /** * Sets the default value. * * @param mixed $value The default value * * @return $this */ public function defaultValue($value) { $this->default = true; $this->defaultValue = $value; return $this; } /** * Sets the node as required. * * @return $this */ public function isRequired() { $this->required = true; return $this; } /** * Sets the node as deprecated. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message the deprecation message to use * * You can use %node% and %path% placeholders in your message to display, * respectively, the node name and its complete path * * @return $this */ public function setDeprecated(/* string $package, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.' */) { $args = \func_get_args(); if (\func_num_args() < 2) { trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); $message = $args[0] ?? 'The child node "%node%" at path "%path%" is deprecated.'; $package = $version = ''; } else { $package = (string) $args[0]; $version = (string) $args[1]; $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); } $this->deprecation = [ 'package' => $package, 'version' => $version, 'message' => $message, ]; return $this; } /** * Sets the equivalent value used when the node contains null. * * @param mixed $value * * @return $this */ public function treatNullLike($value) { $this->nullEquivalent = $value; return $this; } /** * Sets the equivalent value used when the node contains true. * * @param mixed $value * * @return $this */ public function treatTrueLike($value) { $this->trueEquivalent = $value; return $this; } /** * Sets the equivalent value used when the node contains false. * * @param mixed $value * * @return $this */ public function treatFalseLike($value) { $this->falseEquivalent = $value; return $this; } /** * Sets null as the default value. * * @return $this */ public function defaultNull() { return $this->defaultValue(null); } /** * Sets true as the default value. * * @return $this */ public function defaultTrue() { return $this->defaultValue(true); } /** * Sets false as the default value. * * @return $this */ public function defaultFalse() { return $this->defaultValue(false); } /** * Sets an expression to run before the normalization. * * @return ExprBuilder */ public function beforeNormalization() { return $this->normalization()->before(); } /** * Denies the node value being empty. * * @return $this */ public function cannotBeEmpty() { $this->allowEmptyValue = false; return $this; } /** * Sets an expression to run for the validation. * * The expression receives the value of the node and must return it. It can * modify it. * An exception should be thrown when the node is not valid. * * @return ExprBuilder */ public function validate() { return $this->validation()->rule(); } /** * Sets whether the node can be overwritten. * * @return $this */ public function cannotBeOverwritten(bool $deny = true) { $this->merge()->denyOverwrite($deny); return $this; } /** * Gets the builder for validation rules. * * @return ValidationBuilder */ protected function validation() { if (null === $this->validation) { $this->validation = new ValidationBuilder($this); } return $this->validation; } /** * Gets the builder for merging rules. * * @return MergeBuilder */ protected function merge() { if (null === $this->merge) { $this->merge = new MergeBuilder($this); } return $this->merge; } /** * Gets the builder for normalization rules. * * @return NormalizationBuilder */ protected function normalization() { if (null === $this->normalization) { $this->normalization = new NormalizationBuilder($this); } return $this->normalization; } /** * Instantiate and configure the node according to this definition. * * @return NodeInterface * * @throws InvalidDefinitionException When the definition is invalid */ abstract protected function createNode(); /** * Set PathSeparator to use. * * @return $this */ public function setPathSeparator(string $separator) { if ($this instanceof ParentNodeDefinitionInterface) { foreach ($this->getChildNodeDefinitions() as $child) { $child->setPathSeparator($separator); } } $this->pathSeparator = $separator; return $this; } } Definition/Builder/NodeParentInterface.php 0000644 00000000677 15120141155 0014620 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * An interface that must be implemented by all node parents. * * @author Victor Berchet <victor@suumit.com> */ interface NodeParentInterface { } Definition/Builder/NormalizationBuilder.php 0000644 00000002632 15120141155 0015066 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * This class builds normalization conditions. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class NormalizationBuilder { protected $node; public $before = []; public $remappings = []; public function __construct(NodeDefinition $node) { $this->node = $node; } /** * Registers a key to remap to its plural form. * * @param string $key The key to remap * @param string|null $plural The plural of the key in case of irregular plural * * @return $this */ public function remap(string $key, string $plural = null) { $this->remappings[] = [$key, null === $plural ? $key.'s' : $plural]; return $this; } /** * Registers a closure to run before the normalization or an expression builder to build it if null is provided. * * @return ExprBuilder|$this */ public function before(\Closure $closure = null) { if (null !== $closure) { $this->before[] = $closure; return $this; } return $this->before[] = new ExprBuilder($this->node); } } Definition/Builder/NumericNodeDefinition.php 0000644 00000003611 15120141155 0015150 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; /** * Abstract class that contains common code of integer and float node definitions. * * @author David Jeanmonod <david.jeanmonod@gmail.com> */ abstract class NumericNodeDefinition extends ScalarNodeDefinition { protected $min; protected $max; /** * Ensures that the value is smaller than the given reference. * * @param int|float $max * * @return $this * * @throws \InvalidArgumentException when the constraint is inconsistent */ public function max($max) { if (isset($this->min) && $this->min > $max) { throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s).', $max, $this->min)); } $this->max = $max; return $this; } /** * Ensures that the value is bigger than the given reference. * * @param int|float $min * * @return $this * * @throws \InvalidArgumentException when the constraint is inconsistent */ public function min($min) { if (isset($this->max) && $this->max < $min) { throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s).', $min, $this->max)); } $this->min = $min; return $this; } /** * {@inheritdoc} * * @throws InvalidDefinitionException */ public function cannotBeEmpty() { throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); } } Definition/Builder/ParentNodeDefinitionInterface.php 0000644 00000002230 15120141155 0016614 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * An interface that must be implemented by nodes which can have children. * * @author Victor Berchet <victor@suumit.com> */ interface ParentNodeDefinitionInterface extends BuilderAwareInterface { /** * Returns a builder to add children nodes. * * @return NodeBuilder */ public function children(); /** * Appends a node definition. * * Usage: * * $node = $parentNode * ->children() * ->scalarNode('foo')->end() * ->scalarNode('baz')->end() * ->append($this->getBarNodeDefinition()) * ->end() * ; * * @return $this */ public function append(NodeDefinition $node); /** * Gets the child node definitions. * * @return NodeDefinition[] */ public function getChildNodeDefinitions(); } Definition/Builder/ScalarNodeDefinition.php 0000644 00000001353 15120141155 0014754 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\ScalarNode; /** * This class provides a fluent interface for defining a node. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ScalarNodeDefinition extends VariableNodeDefinition { /** * Instantiate a Node. * * @return ScalarNode */ protected function instantiateNode() { return new ScalarNode($this->name, $this->parent, $this->pathSeparator); } } Definition/Builder/TreeBuilder.php 0000644 00000003004 15120141155 0013131 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\NodeInterface; /** * This is the entry class for building a config tree. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class TreeBuilder implements NodeParentInterface { protected $tree; protected $root; public function __construct(string $name, string $type = 'array', NodeBuilder $builder = null) { $builder = $builder ?? new NodeBuilder(); $this->root = $builder->node($name, $type)->setParent($this); } /** * @return NodeDefinition|ArrayNodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') */ public function getRootNode(): NodeDefinition { return $this->root; } /** * Builds the tree. * * @return NodeInterface * * @throws \RuntimeException */ public function buildTree() { if (null !== $this->tree) { return $this->tree; } return $this->tree = $this->root->getNode(true); } public function setPathSeparator(string $separator) { // unset last built as changing path separator changes all nodes $this->tree = null; $this->root->setPathSeparator($separator); } } Definition/Builder/ValidationBuilder.php 0000644 00000001705 15120141155 0014332 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; /** * This class builds validation conditions. * * @author Christophe Coevoet <stof@notk.org> */ class ValidationBuilder { protected $node; public $rules = []; public function __construct(NodeDefinition $node) { $this->node = $node; } /** * Registers a closure to run as normalization or an expression builder to build it if null is provided. * * @return ExprBuilder|$this */ public function rule(\Closure $closure = null) { if (null !== $closure) { $this->rules[] = $closure; return $this; } return $this->rules[] = new ExprBuilder($this->node); } } Definition/Builder/VariableNodeDefinition.php 0000644 00000003471 15120141155 0015277 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\VariableNode; /** * This class provides a fluent interface for defining a node. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class VariableNodeDefinition extends NodeDefinition { /** * Instantiate a Node. * * @return VariableNode */ protected function instantiateNode() { return new VariableNode($this->name, $this->parent, $this->pathSeparator); } /** * {@inheritdoc} */ protected function createNode() { $node = $this->instantiateNode(); if (null !== $this->normalization) { $node->setNormalizationClosures($this->normalization->before); } if (null !== $this->merge) { $node->setAllowOverwrite($this->merge->allowOverwrite); } if (true === $this->default) { $node->setDefaultValue($this->defaultValue); } $node->setAllowEmptyValue($this->allowEmptyValue); $node->addEquivalentValue(null, $this->nullEquivalent); $node->addEquivalentValue(true, $this->trueEquivalent); $node->addEquivalentValue(false, $this->falseEquivalent); $node->setRequired($this->required); if ($this->deprecation) { $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); } if (null !== $this->validation) { $node->setFinalValidationClosures($this->validation->rules); } return $node; } } Definition/Dumper/XmlReferenceDumper.php 0000644 00000024243 15120141155 0014335 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Dumper; use Symfony\Component\Config\Definition\ArrayNode; use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\EnumNode; use Symfony\Component\Config\Definition\NodeInterface; use Symfony\Component\Config\Definition\PrototypedArrayNode; /** * Dumps an XML reference configuration for the given configuration/node instance. * * @author Wouter J <waldio.webdesign@gmail.com> */ class XmlReferenceDumper { private $reference; public function dump(ConfigurationInterface $configuration, string $namespace = null) { return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); } public function dumpNode(NodeInterface $node, string $namespace = null) { $this->reference = ''; $this->writeNode($node, 0, true, $namespace); $ref = $this->reference; $this->reference = null; return $ref; } private function writeNode(NodeInterface $node, int $depth = 0, bool $root = false, string $namespace = null) { $rootName = ($root ? 'config' : $node->getName()); $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); // xml remapping if ($node->getParent()) { $remapping = array_filter($node->getParent()->getXmlRemappings(), function (array $mapping) use ($rootName) { return $rootName === $mapping[1]; }); if (\count($remapping)) { [$singular] = current($remapping); $rootName = $singular; } } $rootName = str_replace('_', '-', $rootName); $rootAttributes = []; $rootAttributeComments = []; $rootChildren = []; $rootComments = []; if ($node instanceof ArrayNode) { $children = $node->getChildren(); // comments about the root node if ($rootInfo = $node->getInfo()) { $rootComments[] = $rootInfo; } if ($rootNamespace) { $rootComments[] = 'Namespace: '.$rootNamespace; } // render prototyped nodes if ($node instanceof PrototypedArrayNode) { $prototype = $node->getPrototype(); $info = 'prototype'; if (null !== $prototype->getInfo()) { $info .= ': '.$prototype->getInfo(); } array_unshift($rootComments, $info); if ($key = $node->getKeyAttribute()) { $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key; } if ($prototype instanceof PrototypedArrayNode) { $prototype->setName($key ?? ''); $children = [$key => $prototype]; } elseif ($prototype instanceof ArrayNode) { $children = $prototype->getChildren(); } else { if ($prototype->hasDefaultValue()) { $prototypeValue = $prototype->getDefaultValue(); } else { switch (\get_class($prototype)) { case 'Symfony\Component\Config\Definition\ScalarNode': $prototypeValue = 'scalar value'; break; case 'Symfony\Component\Config\Definition\FloatNode': case 'Symfony\Component\Config\Definition\IntegerNode': $prototypeValue = 'numeric value'; break; case 'Symfony\Component\Config\Definition\BooleanNode': $prototypeValue = 'true|false'; break; case 'Symfony\Component\Config\Definition\EnumNode': $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); break; default: $prototypeValue = 'value'; } } } } // get attributes and elements foreach ($children as $child) { if ($child instanceof ArrayNode) { // get elements $rootChildren[] = $child; continue; } // get attributes // metadata $name = str_replace('_', '-', $child->getName()); $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world // comments $comments = []; if ($child instanceof BaseNode && $info = $child->getInfo()) { $comments[] = $info; } if ($child instanceof BaseNode && $example = $child->getExample()) { $comments[] = 'Example: '.(\is_array($example) ? implode(', ', $example) : $example); } if ($child->isRequired()) { $comments[] = 'Required'; } if ($child instanceof BaseNode && $child->isDeprecated()) { $deprecation = $child->getDeprecation($child->getName(), $node->getPath()); $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); } if ($child instanceof EnumNode) { $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); } if (\count($comments)) { $rootAttributeComments[$name] = implode(";\n", $comments); } // default values if ($child->hasDefaultValue()) { $value = $child->getDefaultValue(); } // append attribute $rootAttributes[$name] = $value; } } // render comments // root node comment if (\count($rootComments)) { foreach ($rootComments as $comment) { $this->writeLine('<!-- '.$comment.' -->', $depth); } } // attribute comments if (\count($rootAttributeComments)) { foreach ($rootAttributeComments as $attrName => $comment) { $commentDepth = $depth + 4 + \strlen($attrName) + 2; $commentLines = explode("\n", $comment); $multiline = (\count($commentLines) > 1); $comment = implode(\PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); if ($multiline) { $this->writeLine('<!--', $depth); $this->writeLine($attrName.': '.$comment, $depth + 4); $this->writeLine('-->', $depth); } else { $this->writeLine('<!-- '.$attrName.': '.$comment.' -->', $depth); } } } // render start tag + attributes $rootIsVariablePrototype = isset($prototypeValue); $rootIsEmptyTag = (0 === \count($rootChildren) && !$rootIsVariablePrototype); $rootOpenTag = '<'.$rootName; if (1 >= ($attributesCount = \count($rootAttributes))) { if (1 === $attributesCount) { $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes))); } $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; if ($rootIsVariablePrototype) { $rootOpenTag .= $prototypeValue.'</'.$rootName.'>'; } $this->writeLine($rootOpenTag, $depth); } else { $this->writeLine($rootOpenTag, $depth); $i = 1; foreach ($rootAttributes as $attrName => $attrValue) { $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); $this->writeLine($attr, $depth + 4); if ($attributesCount === $i++) { $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); if ($rootIsVariablePrototype) { $rootOpenTag .= $prototypeValue.'</'.$rootName.'>'; } } } } // render children tags foreach ($rootChildren as $child) { $this->writeLine(''); $this->writeNode($child, $depth + 4); } // render end tag if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { $this->writeLine(''); $rootEndTag = '</'.$rootName.'>'; $this->writeLine($rootEndTag, $depth); } } /** * Outputs a single config reference line. */ private function writeLine(string $text, int $indent = 0) { $indent = \strlen($text) + $indent; $format = '%'.$indent.'s'; $this->reference .= sprintf($format, $text).\PHP_EOL; } /** * Renders the string conversion of the value. * * @param mixed $value */ private function writeValue($value): string { if ('%%%%not_defined%%%%' === $value) { return ''; } if (\is_string($value) || is_numeric($value)) { return $value; } if (false === $value) { return 'false'; } if (true === $value) { return 'true'; } if (null === $value) { return 'null'; } if (empty($value)) { return ''; } if (\is_array($value)) { return implode(',', $value); } return ''; } } Definition/Dumper/YamlReferenceDumper.php 0000644 00000020230 15120141155 0014467 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Dumper; use Symfony\Component\Config\Definition\ArrayNode; use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\EnumNode; use Symfony\Component\Config\Definition\NodeInterface; use Symfony\Component\Config\Definition\PrototypedArrayNode; use Symfony\Component\Config\Definition\ScalarNode; use Symfony\Component\Config\Definition\VariableNode; use Symfony\Component\Yaml\Inline; /** * Dumps a Yaml reference configuration for the given configuration/node instance. * * @author Kevin Bond <kevinbond@gmail.com> */ class YamlReferenceDumper { private $reference; public function dump(ConfigurationInterface $configuration) { return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); } public function dumpAtPath(ConfigurationInterface $configuration, string $path) { $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree(); foreach (explode('.', $path) as $step) { if (!$node instanceof ArrayNode) { throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path)); } /** @var NodeInterface[] $children */ $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren(); foreach ($children as $child) { if ($child->getName() === $step) { $node = $child; continue 2; } } throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path)); } return $this->dumpNode($node); } public function dumpNode(NodeInterface $node) { $this->reference = ''; $this->writeNode($node); $ref = $this->reference; $this->reference = null; return $ref; } private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, int $depth = 0, bool $prototypedArray = false) { $comments = []; $default = ''; $defaultArray = null; $children = null; $example = null; if ($node instanceof BaseNode) { $example = $node->getExample(); } // defaults if ($node instanceof ArrayNode) { $children = $node->getChildren(); if ($node instanceof PrototypedArrayNode) { $children = $this->getPrototypeChildren($node); } if (!$children) { if ($node->hasDefaultValue() && \count($defaultArray = $node->getDefaultValue())) { $default = ''; } elseif (!\is_array($example)) { $default = '[]'; } } } elseif ($node instanceof EnumNode) { $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues())); $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~'; } elseif (VariableNode::class === \get_class($node) && \is_array($example)) { // If there is an array example, we are sure we dont need to print a default value $default = ''; } else { $default = '~'; if ($node->hasDefaultValue()) { $default = $node->getDefaultValue(); if (\is_array($default)) { if (\count($defaultArray = $node->getDefaultValue())) { $default = ''; } elseif (!\is_array($example)) { $default = '[]'; } } else { $default = Inline::dump($default); } } } // required? if ($node->isRequired()) { $comments[] = 'Required'; } // deprecated? if ($node instanceof BaseNode && $node->isDeprecated()) { $deprecation = $node->getDeprecation($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()); $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); } // example if ($example && !\is_array($example)) { $comments[] = 'Example: '.Inline::dump($example); } $default = '' != (string) $default ? ' '.$default : ''; $comments = \count($comments) ? '# '.implode(', ', $comments) : ''; $key = $prototypedArray ? '-' : $node->getName().':'; $text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' '); if ($node instanceof BaseNode && $info = $node->getInfo()) { $this->writeLine(''); // indenting multi-line info $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info); $this->writeLine('# '.$info, $depth * 4); } $this->writeLine($text, $depth * 4); // output defaults if ($defaultArray) { $this->writeLine(''); $message = \count($defaultArray) > 1 ? 'Defaults' : 'Default'; $this->writeLine('# '.$message.':', $depth * 4 + 4); $this->writeArray($defaultArray, $depth + 1); } if (\is_array($example)) { $this->writeLine(''); $message = \count($example) > 1 ? 'Examples' : 'Example'; $this->writeLine('# '.$message.':', $depth * 4 + 4); $this->writeArray(array_map([Inline::class, 'dump'], $example), $depth + 1); } if ($children) { foreach ($children as $childNode) { $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute()); } } } /** * Outputs a single config reference line. */ private function writeLine(string $text, int $indent = 0) { $indent = \strlen($text) + $indent; $format = '%'.$indent.'s'; $this->reference .= sprintf($format, $text)."\n"; } private function writeArray(array $array, int $depth) { $isIndexed = array_values($array) === $array; foreach ($array as $key => $value) { if (\is_array($value)) { $val = ''; } else { $val = $value; } if ($isIndexed) { $this->writeLine('- '.$val, $depth * 4); } else { $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4); } if (\is_array($value)) { $this->writeArray($value, $depth + 1); } } } private function getPrototypeChildren(PrototypedArrayNode $node): array { $prototype = $node->getPrototype(); $key = $node->getKeyAttribute(); // Do not expand prototype if it isn't an array node nor uses attribute as key if (!$key && !$prototype instanceof ArrayNode) { return $node->getChildren(); } if ($prototype instanceof ArrayNode) { $keyNode = new ArrayNode($key, $node); $children = $prototype->getChildren(); if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) { $children = $this->getPrototypeChildren($prototype); } // add children foreach ($children as $childNode) { $keyNode->addChild($childNode); } } else { $keyNode = new ScalarNode($key, $node); } $info = 'Prototype'; if (null !== $prototype->getInfo()) { $info .= ': '.$prototype->getInfo(); } $keyNode->setInfo($info); return [$key => $keyNode]; } } Definition/Exception/DuplicateKeyException.php 0000644 00000001105 15120141155 0015535 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * This exception is thrown whenever the key of an array is not unique. This can * only be the case if the configuration is coming from an XML file. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class DuplicateKeyException extends InvalidConfigurationException { } Definition/Exception/Exception.php 0000644 00000000713 15120141155 0013235 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * Base exception for all configuration exceptions. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class Exception extends \RuntimeException { } Definition/Exception/ForbiddenOverwriteException.php 0000644 00000001121 15120141155 0016753 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * This exception is thrown when a configuration path is overwritten from a * subsequent configuration file, but the entry node specifically forbids this. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ForbiddenOverwriteException extends InvalidConfigurationException { } Definition/Exception/InvalidConfigurationException.php 0000644 00000002076 15120141155 0017300 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * A very general exception which can be thrown whenever non of the more specific * exceptions is suitable. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class InvalidConfigurationException extends Exception { private $path; private $containsHints = false; public function setPath(string $path) { $this->path = $path; } public function getPath() { return $this->path; } /** * Adds extra information that is suffixed to the original exception message. */ public function addHint(string $hint) { if (!$this->containsHints) { $this->message .= "\nHint: ".$hint; $this->containsHints = true; } else { $this->message .= ', '.$hint; } } } Definition/Exception/InvalidDefinitionException.php 0000644 00000000732 15120141155 0016556 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * Thrown when an error is detected in a node Definition. * * @author Victor Berchet <victor.berchet@suumit.com> */ class InvalidDefinitionException extends Exception { } Definition/Exception/InvalidTypeException.php 0000644 00000000755 15120141155 0015414 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * This exception is thrown if an invalid type is encountered. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class InvalidTypeException extends InvalidConfigurationException { } Definition/Exception/UnsetKeyException.php 0000644 00000001034 15120141155 0014722 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition\Exception; /** * This exception is usually not encountered by the end-user, but only used * internally to signal the parent scope to unset a key. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class UnsetKeyException extends Exception { } Definition/ArrayNode.php 0000644 00000027164 15120141155 0011236 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; use Symfony\Component\Config\Definition\Exception\UnsetKeyException; /** * Represents an Array node in the config tree. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ArrayNode extends BaseNode implements PrototypeNodeInterface { protected $xmlRemappings = []; protected $children = []; protected $allowFalse = false; protected $allowNewKeys = true; protected $addIfNotSet = false; protected $performDeepMerging = true; protected $ignoreExtraKeys = false; protected $removeExtraKeys = true; protected $normalizeKeys = true; public function setNormalizeKeys(bool $normalizeKeys) { $this->normalizeKeys = $normalizeKeys; } /** * {@inheritdoc} * * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. * After running this method, all keys are normalized to foo_bar. * * If you have a mixed key like foo-bar_moo, it will not be altered. * The key will also not be altered if the target key already exists. */ protected function preNormalize($value) { if (!$this->normalizeKeys || !\is_array($value)) { return $value; } $normalized = []; foreach ($value as $k => $v) { if (str_contains($k, '-') && !str_contains($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { $normalized[$normalizedKey] = $v; } else { $normalized[$k] = $v; } } return $normalized; } /** * Retrieves the children of this node. * * @return array<string, NodeInterface> */ public function getChildren() { return $this->children; } /** * Sets the xml remappings that should be performed. * * @param array $remappings An array of the form [[string, string]] */ public function setXmlRemappings(array $remappings) { $this->xmlRemappings = $remappings; } /** * Gets the xml remappings that should be performed. * * @return array an array of the form [[string, string]] */ public function getXmlRemappings() { return $this->xmlRemappings; } /** * Sets whether to add default values for this array if it has not been * defined in any of the configuration files. */ public function setAddIfNotSet(bool $boolean) { $this->addIfNotSet = $boolean; } /** * Sets whether false is allowed as value indicating that the array should be unset. */ public function setAllowFalse(bool $allow) { $this->allowFalse = $allow; } /** * Sets whether new keys can be defined in subsequent configurations. */ public function setAllowNewKeys(bool $allow) { $this->allowNewKeys = $allow; } /** * Sets if deep merging should occur. */ public function setPerformDeepMerging(bool $boolean) { $this->performDeepMerging = $boolean; } /** * Whether extra keys should just be ignored without an exception. * * @param bool $boolean To allow extra keys * @param bool $remove To remove extra keys */ public function setIgnoreExtraKeys(bool $boolean, bool $remove = true) { $this->ignoreExtraKeys = $boolean; $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; } /** * Returns true when extra keys should be ignored without an exception. */ public function shouldIgnoreExtraKeys(): bool { return $this->ignoreExtraKeys; } /** * {@inheritdoc} */ public function setName(string $name) { $this->name = $name; } /** * {@inheritdoc} */ public function hasDefaultValue() { return $this->addIfNotSet; } /** * {@inheritdoc} */ public function getDefaultValue() { if (!$this->hasDefaultValue()) { throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); } $defaults = []; foreach ($this->children as $name => $child) { if ($child->hasDefaultValue()) { $defaults[$name] = $child->getDefaultValue(); } } return $defaults; } /** * Adds a child node. * * @throws \InvalidArgumentException when the child node has no name * @throws \InvalidArgumentException when the child node's name is not unique */ public function addChild(NodeInterface $node) { $name = $node->getName(); if ('' === $name) { throw new \InvalidArgumentException('Child nodes must be named.'); } if (isset($this->children[$name])) { throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); } $this->children[$name] = $node; } /** * {@inheritdoc} * * @throws UnsetKeyException * @throws InvalidConfigurationException if the node doesn't have enough children */ protected function finalizeValue($value) { if (false === $value) { throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value))); } foreach ($this->children as $name => $child) { if (!\array_key_exists($name, $value)) { if ($child->isRequired()) { $message = sprintf('The child config "%s" under "%s" must be configured', $name, $this->getPath()); if ($child->getInfo()) { $message .= sprintf(': %s', $child->getInfo()); } else { $message .= '.'; } $ex = new InvalidConfigurationException($message); $ex->setPath($this->getPath()); throw $ex; } if ($child->hasDefaultValue()) { $value[$name] = $child->getDefaultValue(); } continue; } if ($child->isDeprecated()) { $deprecation = $child->getDeprecation($name, $this->getPath()); trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } try { $value[$name] = $child->finalize($value[$name]); } catch (UnsetKeyException $e) { unset($value[$name]); } } return $value; } /** * {@inheritdoc} */ protected function validateType($value) { if (!\is_array($value) && (!$this->allowFalse || false !== $value)) { $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "array", but got "%s"', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } $ex->setPath($this->getPath()); throw $ex; } } /** * {@inheritdoc} * * @throws InvalidConfigurationException */ protected function normalizeValue($value) { if (false === $value) { return $value; } $value = $this->remapXml($value); $normalized = []; foreach ($value as $name => $val) { if (isset($this->children[$name])) { try { $normalized[$name] = $this->children[$name]->normalize($val); } catch (UnsetKeyException $e) { } unset($value[$name]); } elseif (!$this->removeExtraKeys) { $normalized[$name] = $val; } } // if extra fields are present, throw exception if (\count($value) && !$this->ignoreExtraKeys) { $proposals = array_keys($this->children); sort($proposals); $guesses = []; foreach (array_keys($value) as $subject) { $minScore = \INF; foreach ($proposals as $proposal) { $distance = levenshtein($subject, $proposal); if ($distance <= $minScore && $distance < 3) { $guesses[$proposal] = $distance; $minScore = $distance; } } } $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); if (\count($guesses)) { asort($guesses); $msg .= sprintf('. Did you mean "%s"?', implode('", "', array_keys($guesses))); } else { $msg .= sprintf('. Available option%s %s "%s".', 1 === \count($proposals) ? '' : 's', 1 === \count($proposals) ? 'is' : 'are', implode('", "', $proposals)); } $ex = new InvalidConfigurationException($msg); $ex->setPath($this->getPath()); throw $ex; } return $normalized; } /** * Remaps multiple singular values to a single plural value. * * @return array */ protected function remapXml(array $value) { foreach ($this->xmlRemappings as [$singular, $plural]) { if (!isset($value[$singular])) { continue; } $value[$plural] = Processor::normalizeConfig($value, $singular, $plural); unset($value[$singular]); } return $value; } /** * {@inheritdoc} * * @throws InvalidConfigurationException * @throws \RuntimeException */ protected function mergeValues($leftSide, $rightSide) { if (false === $rightSide) { // if this is still false after the last config has been merged the // finalization pass will take care of removing this key entirely return false; } if (false === $leftSide || !$this->performDeepMerging) { return $rightSide; } foreach ($rightSide as $k => $v) { // no conflict if (!\array_key_exists($k, $leftSide)) { if (!$this->allowNewKeys) { $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); $ex->setPath($this->getPath()); throw $ex; } $leftSide[$k] = $v; continue; } if (!isset($this->children[$k])) { if (!$this->ignoreExtraKeys || $this->removeExtraKeys) { throw new \RuntimeException('merge() expects a normalized config array.'); } $leftSide[$k] = $v; continue; } $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); } return $leftSide; } /** * {@inheritdoc} */ protected function allowPlaceholders(): bool { return false; } } Definition/BaseNode.php 0000644 00000040021 15120141155 0011015 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\Exception; use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; use Symfony\Component\Config\Definition\Exception\UnsetKeyException; /** * The base node class. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ abstract class BaseNode implements NodeInterface { public const DEFAULT_PATH_SEPARATOR = '.'; private static $placeholderUniquePrefixes = []; private static $placeholders = []; protected $name; protected $parent; protected $normalizationClosures = []; protected $finalValidationClosures = []; protected $allowOverwrite = true; protected $required = false; protected $deprecation = []; protected $equivalentValues = []; protected $attributes = []; protected $pathSeparator; private $handlingPlaceholder; /** * @throws \InvalidArgumentException if the name contains a period */ public function __construct(?string $name, NodeInterface $parent = null, string $pathSeparator = self::DEFAULT_PATH_SEPARATOR) { if (str_contains($name = (string) $name, $pathSeparator)) { throw new \InvalidArgumentException('The name must not contain ".'.$pathSeparator.'".'); } $this->name = $name; $this->parent = $parent; $this->pathSeparator = $pathSeparator; } /** * Register possible (dummy) values for a dynamic placeholder value. * * Matching configuration values will be processed with a provided value, one by one. After a provided value is * successfully processed the configuration value is returned as is, thus preserving the placeholder. * * @internal */ public static function setPlaceholder(string $placeholder, array $values): void { if (!$values) { throw new \InvalidArgumentException('At least one value must be provided.'); } self::$placeholders[$placeholder] = $values; } /** * Adds a common prefix for dynamic placeholder values. * * Matching configuration values will be skipped from being processed and are returned as is, thus preserving the * placeholder. An exact match provided by {@see setPlaceholder()} might take precedence. * * @internal */ public static function setPlaceholderUniquePrefix(string $prefix): void { self::$placeholderUniquePrefixes[] = $prefix; } /** * Resets all current placeholders available. * * @internal */ public static function resetPlaceholders(): void { self::$placeholderUniquePrefixes = []; self::$placeholders = []; } public function setAttribute(string $key, $value) { $this->attributes[$key] = $value; } /** * @return mixed */ public function getAttribute(string $key, $default = null) { return $this->attributes[$key] ?? $default; } /** * @return bool */ public function hasAttribute(string $key) { return isset($this->attributes[$key]); } /** * @return array */ public function getAttributes() { return $this->attributes; } public function setAttributes(array $attributes) { $this->attributes = $attributes; } public function removeAttribute(string $key) { unset($this->attributes[$key]); } /** * Sets an info message. */ public function setInfo(string $info) { $this->setAttribute('info', $info); } /** * Returns info message. * * @return string|null */ public function getInfo() { return $this->getAttribute('info'); } /** * Sets the example configuration for this node. * * @param string|array $example */ public function setExample($example) { $this->setAttribute('example', $example); } /** * Retrieves the example configuration for this node. * * @return string|array|null */ public function getExample() { return $this->getAttribute('example'); } /** * Adds an equivalent value. * * @param mixed $originalValue * @param mixed $equivalentValue */ public function addEquivalentValue($originalValue, $equivalentValue) { $this->equivalentValues[] = [$originalValue, $equivalentValue]; } /** * Set this node as required. */ public function setRequired(bool $boolean) { $this->required = $boolean; } /** * Sets this node as deprecated. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message the deprecation message to use * * You can use %node% and %path% placeholders in your message to display, * respectively, the node name and its complete path */ public function setDeprecated(?string $package/* , string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.' */) { $args = \func_get_args(); if (\func_num_args() < 2) { trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); if (!isset($args[0])) { trigger_deprecation('symfony/config', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); $this->deprecation = []; return; } $message = (string) $args[0]; $package = $version = ''; } else { $package = (string) $args[0]; $version = (string) $args[1]; $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); } $this->deprecation = [ 'package' => $package, 'version' => $version, 'message' => $message, ]; } /** * Sets if this node can be overridden. */ public function setAllowOverwrite(bool $allow) { $this->allowOverwrite = $allow; } /** * Sets the closures used for normalization. * * @param \Closure[] $closures An array of Closures used for normalization */ public function setNormalizationClosures(array $closures) { $this->normalizationClosures = $closures; } /** * Sets the closures used for final validation. * * @param \Closure[] $closures An array of Closures used for final validation */ public function setFinalValidationClosures(array $closures) { $this->finalValidationClosures = $closures; } /** * {@inheritdoc} */ public function isRequired() { return $this->required; } /** * Checks if this node is deprecated. * * @return bool */ public function isDeprecated() { return (bool) $this->deprecation; } /** * Returns the deprecated message. * * @param string $node the configuration node name * @param string $path the path of the node * * @return string * * @deprecated since Symfony 5.1, use "getDeprecation()" instead. */ public function getDeprecationMessage(string $node, string $path) { trigger_deprecation('symfony/config', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); return $this->getDeprecation($node, $path)['message']; } /** * @param string $node The configuration node name * @param string $path The path of the node */ public function getDeprecation(string $node, string $path): array { return [ 'package' => $this->deprecation['package'] ?? '', 'version' => $this->deprecation['version'] ?? '', 'message' => strtr($this->deprecation['message'] ?? '', ['%node%' => $node, '%path%' => $path]), ]; } /** * {@inheritdoc} */ public function getName() { return $this->name; } /** * {@inheritdoc} */ public function getPath() { if (null !== $this->parent) { return $this->parent->getPath().$this->pathSeparator.$this->name; } return $this->name; } /** * {@inheritdoc} */ final public function merge($leftSide, $rightSide) { if (!$this->allowOverwrite) { throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); } if ($leftSide !== $leftPlaceholders = self::resolvePlaceholderValue($leftSide)) { foreach ($leftPlaceholders as $leftPlaceholder) { $this->handlingPlaceholder = $leftSide; try { $this->merge($leftPlaceholder, $rightSide); } finally { $this->handlingPlaceholder = null; } } return $rightSide; } if ($rightSide !== $rightPlaceholders = self::resolvePlaceholderValue($rightSide)) { foreach ($rightPlaceholders as $rightPlaceholder) { $this->handlingPlaceholder = $rightSide; try { $this->merge($leftSide, $rightPlaceholder); } finally { $this->handlingPlaceholder = null; } } return $rightSide; } $this->doValidateType($leftSide); $this->doValidateType($rightSide); return $this->mergeValues($leftSide, $rightSide); } /** * {@inheritdoc} */ final public function normalize($value) { $value = $this->preNormalize($value); // run custom normalization closures foreach ($this->normalizationClosures as $closure) { $value = $closure($value); } // resolve placeholder value if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { foreach ($placeholders as $placeholder) { $this->handlingPlaceholder = $value; try { $this->normalize($placeholder); } finally { $this->handlingPlaceholder = null; } } return $value; } // replace value with their equivalent foreach ($this->equivalentValues as $data) { if ($data[0] === $value) { $value = $data[1]; } } // validate type $this->doValidateType($value); // normalize value return $this->normalizeValue($value); } /** * Normalizes the value before any other normalization is applied. * * @param mixed $value * * @return mixed */ protected function preNormalize($value) { return $value; } /** * Returns parent node for this node. * * @return NodeInterface|null */ public function getParent() { return $this->parent; } /** * {@inheritdoc} */ final public function finalize($value) { if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { foreach ($placeholders as $placeholder) { $this->handlingPlaceholder = $value; try { $this->finalize($placeholder); } finally { $this->handlingPlaceholder = null; } } return $value; } $this->doValidateType($value); $value = $this->finalizeValue($value); // Perform validation on the final value if a closure has been set. // The closure is also allowed to return another value. foreach ($this->finalValidationClosures as $closure) { try { $value = $closure($value); } catch (Exception $e) { if ($e instanceof UnsetKeyException && null !== $this->handlingPlaceholder) { continue; } throw $e; } catch (\Exception $e) { throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": ', $this->getPath()).$e->getMessage(), $e->getCode(), $e); } } return $value; } /** * Validates the type of a Node. * * @param mixed $value The value to validate * * @throws InvalidTypeException when the value is invalid */ abstract protected function validateType($value); /** * Normalizes the value. * * @param mixed $value The value to normalize * * @return mixed */ abstract protected function normalizeValue($value); /** * Merges two values together. * * @param mixed $leftSide * @param mixed $rightSide * * @return mixed */ abstract protected function mergeValues($leftSide, $rightSide); /** * Finalizes a value. * * @param mixed $value The value to finalize * * @return mixed */ abstract protected function finalizeValue($value); /** * Tests if placeholder values are allowed for this node. */ protected function allowPlaceholders(): bool { return true; } /** * Tests if a placeholder is being handled currently. */ protected function isHandlingPlaceholder(): bool { return null !== $this->handlingPlaceholder; } /** * Gets allowed dynamic types for this node. */ protected function getValidPlaceholderTypes(): array { return []; } private static function resolvePlaceholderValue($value) { if (\is_string($value)) { if (isset(self::$placeholders[$value])) { return self::$placeholders[$value]; } foreach (self::$placeholderUniquePrefixes as $placeholderUniquePrefix) { if (str_starts_with($value, $placeholderUniquePrefix)) { return []; } } } return $value; } private function doValidateType($value): void { if (null !== $this->handlingPlaceholder && !$this->allowPlaceholders()) { $e = new InvalidTypeException(sprintf('A dynamic value is not compatible with a "%s" node type at path "%s".', static::class, $this->getPath())); $e->setPath($this->getPath()); throw $e; } if (null === $this->handlingPlaceholder || null === $value) { $this->validateType($value); return; } $knownTypes = array_keys(self::$placeholders[$this->handlingPlaceholder]); $validTypes = $this->getValidPlaceholderTypes(); if ($validTypes && array_diff($knownTypes, $validTypes)) { $e = new InvalidTypeException(sprintf( 'Invalid type for path "%s". Expected %s, but got %s.', $this->getPath(), 1 === \count($validTypes) ? '"'.reset($validTypes).'"' : 'one of "'.implode('", "', $validTypes).'"', 1 === \count($knownTypes) ? '"'.reset($knownTypes).'"' : 'one of "'.implode('", "', $knownTypes).'"' )); if ($hint = $this->getInfo()) { $e->addHint($hint); } $e->setPath($this->getPath()); throw $e; } $this->validateType($value); } } Definition/BooleanNode.php 0000644 00000002370 15120141155 0011527 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; /** * This node represents a Boolean value in the config tree. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class BooleanNode extends ScalarNode { /** * {@inheritdoc} */ protected function validateType($value) { if (!\is_bool($value)) { $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "bool", but got "%s".', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } $ex->setPath($this->getPath()); throw $ex; } } /** * {@inheritdoc} */ protected function isValueEmpty($value) { // a boolean value cannot be empty return false; } /** * {@inheritdoc} */ protected function getValidPlaceholderTypes(): array { return ['bool']; } } Definition/ConfigurationInterface.php 0000644 00000001145 15120141155 0013771 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; /** * Configuration interface. * * @author Victor Berchet <victor@suumit.com> */ interface ConfigurationInterface { /** * Generates the configuration tree builder. * * @return TreeBuilder */ public function getConfigTreeBuilder(); } Definition/EnumNode.php 0000644 00000003250 15120141155 0011052 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; /** * Node which only allows a finite set of values. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class EnumNode extends ScalarNode { private $values; public function __construct(?string $name, NodeInterface $parent = null, array $values = [], string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) { $values = array_unique($values); if (empty($values)) { throw new \InvalidArgumentException('$values must contain at least one element.'); } parent::__construct($name, $parent, $pathSeparator); $this->values = $values; } public function getValues() { return $this->values; } /** * {@inheritdoc} */ protected function finalizeValue($value) { $value = parent::finalizeValue($value); if (!\in_array($value, $this->values, true)) { $ex = new InvalidConfigurationException(sprintf('The value %s is not allowed for path "%s". Permissible values: %s', json_encode($value), $this->getPath(), implode(', ', array_map('json_encode', $this->values)))); $ex->setPath($this->getPath()); throw $ex; } return $value; } /** * {@inheritdoc} */ protected function allowPlaceholders(): bool { return false; } } Definition/FloatNode.php 0000644 00000002342 15120141155 0011214 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; /** * This node represents a float value in the config tree. * * @author Jeanmonod David <david.jeanmonod@gmail.com> */ class FloatNode extends NumericNode { /** * {@inheritdoc} */ protected function validateType($value) { // Integers are also accepted, we just cast them if (\is_int($value)) { $value = (float) $value; } if (!\is_float($value)) { $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "float", but got "%s".', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } $ex->setPath($this->getPath()); throw $ex; } } /** * {@inheritdoc} */ protected function getValidPlaceholderTypes(): array { return ['float']; } } Definition/IntegerNode.php 0000644 00000002131 15120141155 0011540 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; /** * This node represents an integer value in the config tree. * * @author Jeanmonod David <david.jeanmonod@gmail.com> */ class IntegerNode extends NumericNode { /** * {@inheritdoc} */ protected function validateType($value) { if (!\is_int($value)) { $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "int", but got "%s".', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } $ex->setPath($this->getPath()); throw $ex; } } /** * {@inheritdoc} */ protected function getValidPlaceholderTypes(): array { return ['int']; } } Definition/NodeInterface.php 0000644 00000004526 15120141155 0012055 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; /** * Common Interface among all nodes. * * In most cases, it is better to inherit from BaseNode instead of implementing * this interface yourself. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface NodeInterface { /** * Returns the name of the node. * * @return string */ public function getName(); /** * Returns the path of the node. * * @return string */ public function getPath(); /** * Returns true when the node is required. * * @return bool */ public function isRequired(); /** * Returns true when the node has a default value. * * @return bool */ public function hasDefaultValue(); /** * Returns the default value of the node. * * @return mixed * * @throws \RuntimeException if the node has no default value */ public function getDefaultValue(); /** * Normalizes a value. * * @param mixed $value The value to normalize * * @return mixed * * @throws InvalidTypeException if the value type is invalid */ public function normalize($value); /** * Merges two values together. * * @param mixed $leftSide * @param mixed $rightSide * * @return mixed * * @throws ForbiddenOverwriteException if the configuration path cannot be overwritten * @throws InvalidTypeException if the value type is invalid */ public function merge($leftSide, $rightSide); /** * Finalizes a value. * * @param mixed $value The value to finalize * * @return mixed * * @throws InvalidTypeException if the value type is invalid * @throws InvalidConfigurationException if the value is invalid configuration */ public function finalize($value); } Definition/NumericNode.php 0000644 00000003563 15120141155 0011557 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; /** * This node represents a numeric value in the config tree. * * @author David Jeanmonod <david.jeanmonod@gmail.com> */ class NumericNode extends ScalarNode { protected $min; protected $max; /** * @param int|float|null $min * @param int|float|null $max */ public function __construct(?string $name, NodeInterface $parent = null, $min = null, $max = null, string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) { parent::__construct($name, $parent, $pathSeparator); $this->min = $min; $this->max = $max; } /** * {@inheritdoc} */ protected function finalizeValue($value) { $value = parent::finalizeValue($value); $errorMsg = null; if (isset($this->min) && $value < $this->min) { $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); } if (isset($this->max) && $value > $this->max) { $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); } if (isset($errorMsg)) { $ex = new InvalidConfigurationException($errorMsg); $ex->setPath($this->getPath()); throw $ex; } return $value; } /** * {@inheritdoc} */ protected function isValueEmpty($value) { // a numeric value cannot be empty return false; } } Definition/Processor.php 0000644 00000005020 15120141155 0011314 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; /** * This class is the entry point for config normalization/merging/finalization. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final */ class Processor { /** * Processes an array of configurations. * * @param array $configs An array of configuration items to process */ public function process(NodeInterface $configTree, array $configs): array { $currentConfig = []; foreach ($configs as $config) { $config = $configTree->normalize($config); $currentConfig = $configTree->merge($currentConfig, $config); } return $configTree->finalize($currentConfig); } /** * Processes an array of configurations. * * @param array $configs An array of configuration items to process */ public function processConfiguration(ConfigurationInterface $configuration, array $configs): array { return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); } /** * Normalizes a configuration entry. * * This method returns a normalize configuration array for a given key * to remove the differences due to the original format (YAML and XML mainly). * * Here is an example. * * The configuration in XML: * * <twig:extension>twig.extension.foo</twig:extension> * <twig:extension>twig.extension.bar</twig:extension> * * And the same configuration in YAML: * * extensions: ['twig.extension.foo', 'twig.extension.bar'] * * @param array $config A config array * @param string $key The key to normalize * @param string|null $plural The plural form of the key if it is irregular */ public static function normalizeConfig(array $config, string $key, string $plural = null): array { if (null === $plural) { $plural = $key.'s'; } if (isset($config[$plural])) { return $config[$plural]; } if (isset($config[$key])) { if (\is_string($config[$key]) || !\is_int(key($config[$key]))) { // only one return [$config[$key]]; } return $config[$key]; } return []; } } Definition/PrototypeNodeInterface.php 0000644 00000001107 15120141155 0013773 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; /** * This interface must be implemented by nodes which can be used as prototypes. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface PrototypeNodeInterface extends NodeInterface { /** * Sets the name of the node. */ public function setName(string $name); } Definition/PrototypedArrayNode.php 0000644 00000026102 15120141155 0013317 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\DuplicateKeyException; use Symfony\Component\Config\Definition\Exception\Exception; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Exception\UnsetKeyException; /** * Represents a prototyped Array node in the config tree. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class PrototypedArrayNode extends ArrayNode { protected $prototype; protected $keyAttribute; protected $removeKeyAttribute = false; protected $minNumberOfElements = 0; protected $defaultValue = []; protected $defaultChildren; /** * @var NodeInterface[] An array of the prototypes of the simplified value children */ private $valuePrototypes = []; /** * Sets the minimum number of elements that a prototype based node must * contain. By default this is zero, meaning no elements. */ public function setMinNumberOfElements(int $number) { $this->minNumberOfElements = $number; } /** * Sets the attribute which value is to be used as key. * * This is useful when you have an indexed array that should be an * associative array. You can select an item from within the array * to be the key of the particular item. For example, if "id" is the * "key", then: * * [ * ['id' => 'my_name', 'foo' => 'bar'], * ]; * * becomes * * [ * 'my_name' => ['foo' => 'bar'], * ]; * * If you'd like "'id' => 'my_name'" to still be present in the resulting * array, then you can set the second argument of this method to false. * * @param string $attribute The name of the attribute which value is to be used as a key * @param bool $remove Whether or not to remove the key */ public function setKeyAttribute(string $attribute, bool $remove = true) { $this->keyAttribute = $attribute; $this->removeKeyAttribute = $remove; } /** * Retrieves the name of the attribute which value should be used as key. * * @return string|null */ public function getKeyAttribute() { return $this->keyAttribute; } /** * Sets the default value of this node. */ public function setDefaultValue(array $value) { $this->defaultValue = $value; } /** * {@inheritdoc} */ public function hasDefaultValue() { return true; } /** * Adds default children when none are set. * * @param int|string|array|null $children The number of children|The child name|The children names to be added */ public function setAddChildrenIfNoneSet($children = ['defaults']) { if (null === $children) { $this->defaultChildren = ['defaults']; } else { $this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children; } } /** * {@inheritdoc} * * The default value could be either explicited or derived from the prototype * default value. */ public function getDefaultValue() { if (null !== $this->defaultChildren) { $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : []; $defaults = []; foreach (array_values($this->defaultChildren) as $i => $name) { $defaults[null === $this->keyAttribute ? $i : $name] = $default; } return $defaults; } return $this->defaultValue; } /** * Sets the node prototype. */ public function setPrototype(PrototypeNodeInterface $node) { $this->prototype = $node; } /** * Retrieves the prototype. * * @return PrototypeNodeInterface */ public function getPrototype() { return $this->prototype; } /** * Disable adding concrete children for prototyped nodes. * * @throws Exception */ public function addChild(NodeInterface $node) { throw new Exception('A prototyped array node cannot have concrete children.'); } /** * {@inheritdoc} */ protected function finalizeValue($value) { if (false === $value) { throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value))); } foreach ($value as $k => $v) { $prototype = $this->getPrototypeForChild($k); try { $value[$k] = $prototype->finalize($v); } catch (UnsetKeyException $e) { unset($value[$k]); } } if (\count($value) < $this->minNumberOfElements) { $ex = new InvalidConfigurationException(sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements)); $ex->setPath($this->getPath()); throw $ex; } return $value; } /** * {@inheritdoc} * * @throws DuplicateKeyException */ protected function normalizeValue($value) { if (false === $value) { return $value; } $value = $this->remapXml($value); $isList = array_is_list($value); $normalized = []; foreach ($value as $k => $v) { if (null !== $this->keyAttribute && \is_array($v)) { if (!isset($v[$this->keyAttribute]) && \is_int($k) && $isList) { $ex = new InvalidConfigurationException(sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath())); $ex->setPath($this->getPath()); throw $ex; } elseif (isset($v[$this->keyAttribute])) { $k = $v[$this->keyAttribute]; if (\is_float($k)) { $k = var_export($k, true); } // remove the key attribute when required if ($this->removeKeyAttribute) { unset($v[$this->keyAttribute]); } // if only "value" is left if (array_keys($v) === ['value']) { $v = $v['value']; if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) { $valuePrototype = current($this->valuePrototypes) ?: clone $children['value']; $valuePrototype->parent = $this; $originalClosures = $this->prototype->normalizationClosures; if (\is_array($originalClosures)) { $valuePrototypeClosures = $valuePrototype->normalizationClosures; $valuePrototype->normalizationClosures = \is_array($valuePrototypeClosures) ? array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures; } $this->valuePrototypes[$k] = $valuePrototype; } } } if (\array_key_exists($k, $normalized)) { $ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath())); $ex->setPath($this->getPath()); throw $ex; } } $prototype = $this->getPrototypeForChild($k); if (null !== $this->keyAttribute || !$isList) { $normalized[$k] = $prototype->normalize($v); } else { $normalized[] = $prototype->normalize($v); } } return $normalized; } /** * {@inheritdoc} */ protected function mergeValues($leftSide, $rightSide) { if (false === $rightSide) { // if this is still false after the last config has been merged the // finalization pass will take care of removing this key entirely return false; } if (false === $leftSide || !$this->performDeepMerging) { return $rightSide; } $isList = array_is_list($rightSide); foreach ($rightSide as $k => $v) { // prototype, and key is irrelevant there are no named keys, append the element if (null === $this->keyAttribute && $isList) { $leftSide[] = $v; continue; } // no conflict if (!\array_key_exists($k, $leftSide)) { if (!$this->allowNewKeys) { $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath())); $ex->setPath($this->getPath()); throw $ex; } $leftSide[$k] = $v; continue; } $prototype = $this->getPrototypeForChild($k); $leftSide[$k] = $prototype->merge($leftSide[$k], $v); } return $leftSide; } /** * Returns a prototype for the child node that is associated to $key in the value array. * For general child nodes, this will be $this->prototype. * But if $this->removeKeyAttribute is true and there are only two keys in the child node: * one is same as this->keyAttribute and the other is 'value', then the prototype will be different. * * For example, assume $this->keyAttribute is 'name' and the value array is as follows: * * [ * [ * 'name' => 'name001', * 'value' => 'value001' * ] * ] * * Now, the key is 0 and the child node is: * * [ * 'name' => 'name001', * 'value' => 'value001' * ] * * When normalizing the value array, the 'name' element will removed from the child node * and its value becomes the new key of the child node: * * [ * 'name001' => ['value' => 'value001'] * ] * * Now only 'value' element is left in the child node which can be further simplified into a string: * * ['name001' => 'value001'] * * Now, the key becomes 'name001' and the child node becomes 'value001' and * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance. * * @return mixed */ private function getPrototypeForChild(string $key) { $prototype = $this->valuePrototypes[$key] ?? $this->prototype; $prototype->setName($key); return $prototype; } } Definition/ScalarNode.php 0000644 00000003231 15120141155 0011352 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; /** * This node represents a scalar value in the config tree. * * The following values are considered scalars: * * booleans * * strings * * null * * integers * * floats * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ScalarNode extends VariableNode { /** * {@inheritdoc} */ protected function validateType($value) { if (!\is_scalar($value) && null !== $value) { $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "scalar", but got "%s".', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } $ex->setPath($this->getPath()); throw $ex; } } /** * {@inheritdoc} */ protected function isValueEmpty($value) { // assume environment variables are never empty (which in practice is likely to be true during runtime) // not doing so breaks many configs that are valid today if ($this->isHandlingPlaceholder()) { return false; } return null === $value || '' === $value; } /** * {@inheritdoc} */ protected function getValidPlaceholderTypes(): array { return ['bool', 'int', 'float', 'string']; } } Definition/VariableNode.php 0000644 00000006733 15120141155 0011704 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Definition; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; /** * This node represents a value of variable type in the config tree. * * This node is intended for values of arbitrary type. * Any PHP type is accepted as a value. * * @author Jeremy Mikola <jmikola@gmail.com> */ class VariableNode extends BaseNode implements PrototypeNodeInterface { protected $defaultValueSet = false; protected $defaultValue; protected $allowEmptyValue = true; public function setDefaultValue($value) { $this->defaultValueSet = true; $this->defaultValue = $value; } /** * {@inheritdoc} */ public function hasDefaultValue() { return $this->defaultValueSet; } /** * {@inheritdoc} */ public function getDefaultValue() { $v = $this->defaultValue; return $v instanceof \Closure ? $v() : $v; } /** * Sets if this node is allowed to have an empty value. * * @param bool $boolean True if this entity will accept empty values */ public function setAllowEmptyValue(bool $boolean) { $this->allowEmptyValue = $boolean; } /** * {@inheritdoc} */ public function setName(string $name) { $this->name = $name; } /** * {@inheritdoc} */ protected function validateType($value) { } /** * {@inheritdoc} */ protected function finalizeValue($value) { // deny environment variables only when using custom validators // this avoids ever passing an empty value to final validation closures if (!$this->allowEmptyValue && $this->isHandlingPlaceholder() && $this->finalValidationClosures) { $e = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an environment variable when empty values are not allowed by definition and are validated.', $this->getPath())); if ($hint = $this->getInfo()) { $e->addHint($hint); } $e->setPath($this->getPath()); throw $e; } if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { $ex = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an empty value, but got %s.', $this->getPath(), json_encode($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } $ex->setPath($this->getPath()); throw $ex; } return $value; } /** * {@inheritdoc} */ protected function normalizeValue($value) { return $value; } /** * {@inheritdoc} */ protected function mergeValues($leftSide, $rightSide) { return $rightSide; } /** * Evaluates if the given value is to be treated as empty. * * By default, PHP's empty() function is used to test for emptiness. This * method may be overridden by subtypes to better match their understanding * of empty data. * * @param mixed $value * * @return bool * * @see finalizeValue() */ protected function isValueEmpty($value) { return empty($value); } } Exception/FileLoaderImportCircularReferenceException.php 0000644 00000002025 15120141155 0017571 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Exception; /** * Exception class for when a circular reference is detected when importing resources. * * @author Fabien Potencier <fabien@symfony.com> */ class FileLoaderImportCircularReferenceException extends LoaderLoadException { public function __construct(array $resources, ?int $code = 0, \Throwable $previous = null) { if (null === $code) { trigger_deprecation('symfony/config', '5.3', 'Passing null as $code to "%s()" is deprecated, pass 0 instead.', __METHOD__); $code = 0; } $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); \Exception::__construct($message, $code, $previous); } } Exception/FileLocatorFileNotFoundException.php 0000644 00000001424 15120141155 0015546 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Exception; /** * File locator exception if a file does not exist. * * @author Leo Feyer <https://github.com/leofeyer> */ class FileLocatorFileNotFoundException extends \InvalidArgumentException { private $paths; public function __construct(string $message = '', int $code = 0, \Throwable $previous = null, array $paths = []) { parent::__construct($message, $code, $previous); $this->paths = $paths; } public function getPaths() { return $this->paths; } } Exception/LoaderLoadException.php 0000644 00000010114 15120141155 0013070 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Exception; /** * Exception class for when a resource cannot be loaded or imported. * * @author Ryan Weaver <ryan@thatsquality.com> */ class LoaderLoadException extends \Exception { /** * @param string $resource The resource that could not be imported * @param string|null $sourceResource The original resource importing the new resource * @param int|null $code The error code * @param \Throwable|null $previous A previous exception * @param string|null $type The type of resource */ public function __construct(string $resource, string $sourceResource = null, ?int $code = 0, \Throwable $previous = null, string $type = null) { if (null === $code) { trigger_deprecation('symfony/config', '5.3', 'Passing null as $code to "%s()" is deprecated, pass 0 instead.', __METHOD__); $code = 0; } $message = ''; if ($previous) { // Include the previous exception, to help the user see what might be the underlying cause // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... if ('.' === substr($previous->getMessage(), -1)) { $trimmedMessage = substr($previous->getMessage(), 0, -1); $message .= sprintf('%s', $trimmedMessage).' in '; } else { $message .= sprintf('%s', $previous->getMessage()).' in '; } $message .= $resource.' '; // show tweaked trace to complete the human readable sentence if (null === $sourceResource) { $message .= sprintf('(which is loaded in resource "%s")', $resource); } else { $message .= sprintf('(which is being imported from "%s")', $sourceResource); } $message .= '.'; // if there's no previous message, present it the default way } elseif (null === $sourceResource) { $message .= sprintf('Cannot load resource "%s".', $resource); } else { $message .= sprintf('Cannot import resource "%s" from "%s".', $resource, $sourceResource); } // Is the resource located inside a bundle? if ('@' === $resource[0]) { $parts = explode(\DIRECTORY_SEPARATOR, $resource); $bundle = substr($parts[0], 1); $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); } elseif (null !== $type) { // maybe there is no loader for this specific type if ('annotation' === $type) { $message .= ' Make sure to use PHP 8+ or that annotations are installed and enabled.'; } else { $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type); } } parent::__construct($message, $code, $previous); } protected function varToString($var) { if (\is_object($var)) { return sprintf('Object(%s)', \get_class($var)); } if (\is_array($var)) { $a = []; foreach ($var as $k => $v) { $a[] = sprintf('%s => %s', $k, $this->varToString($v)); } return sprintf('Array(%s)', implode(', ', $a)); } if (\is_resource($var)) { return sprintf('Resource(%s)', get_resource_type($var)); } if (null === $var) { return 'null'; } if (false === $var) { return 'false'; } if (true === $var) { return 'true'; } return (string) $var; } } Loader/DelegatingLoader.php 0000644 00000002400 15120141155 0011644 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; use Symfony\Component\Config\Exception\LoaderLoadException; /** * DelegatingLoader delegates loading to other loaders using a loader resolver. * * This loader acts as an array of LoaderInterface objects - each having * a chance to load a given resource (handled by the resolver) * * @author Fabien Potencier <fabien@symfony.com> */ class DelegatingLoader extends Loader { public function __construct(LoaderResolverInterface $resolver) { $this->resolver = $resolver; } /** * {@inheritdoc} */ public function load($resource, string $type = null) { if (false === $loader = $this->resolver->resolve($resource, $type)) { throw new LoaderLoadException($resource, null, 0, null, $type); } return $loader->load($resource, $type); } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { return false !== $this->resolver->resolve($resource, $type); } } Loader/FileLoader.php 0000644 00000014045 15120141155 0010470 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\Config\Resource\GlobResource; /** * FileLoader is the abstract class used by all built-in loaders that are file based. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class FileLoader extends Loader { protected static $loading = []; protected $locator; private $currentDir; public function __construct(FileLocatorInterface $locator, string $env = null) { $this->locator = $locator; parent::__construct($env); } /** * Sets the current directory. */ public function setCurrentDir(string $dir) { $this->currentDir = $dir; } /** * Returns the file locator used by this loader. * * @return FileLocatorInterface */ public function getLocator() { return $this->locator; } /** * Imports a resource. * * @param mixed $resource A Resource * @param string|null $type The resource type or null if unknown * @param bool $ignoreErrors Whether to ignore import errors or not * @param string|null $sourceResource The original resource importing the new resource * @param string|string[]|null $exclude Glob patterns to exclude from the import * * @return mixed * * @throws LoaderLoadException * @throws FileLoaderImportCircularReferenceException * @throws FileLocatorFileNotFoundException */ public function import($resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null, $exclude = null) { if (\is_string($resource) && \strlen($resource) !== ($i = strcspn($resource, '*?{[')) && !str_contains($resource, "\n")) { $excluded = []; foreach ((array) $exclude as $pattern) { foreach ($this->glob($pattern, true, $_, false, true) as $path => $info) { // normalize Windows slashes and remove trailing slashes $excluded[rtrim(str_replace('\\', '/', $path), '/')] = true; } } $ret = []; $isSubpath = 0 !== $i && str_contains(substr($resource, 0, $i), '/'); foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath, false, $excluded) as $path => $info) { if (null !== $res = $this->doImport($path, 'glob' === $type ? null : $type, $ignoreErrors, $sourceResource)) { $ret[] = $res; } $isSubpath = true; } if ($isSubpath) { return isset($ret[1]) ? $ret : ($ret[0] ?? null); } } return $this->doImport($resource, $type, $ignoreErrors, $sourceResource); } /** * @internal */ protected function glob(string $pattern, bool $recursive, &$resource = null, bool $ignoreErrors = false, bool $forExclusion = false, array $excluded = []) { if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) { $prefix = $pattern; $pattern = ''; } elseif (0 === $i || !str_contains(substr($pattern, 0, $i), '/')) { $prefix = '.'; $pattern = '/'.$pattern; } else { $prefix = \dirname(substr($pattern, 0, 1 + $i)); $pattern = substr($pattern, \strlen($prefix)); } try { $prefix = $this->locator->locate($prefix, $this->currentDir, true); } catch (FileLocatorFileNotFoundException $e) { if (!$ignoreErrors) { throw $e; } $resource = []; foreach ($e->getPaths() as $path) { $resource[] = new FileExistenceResource($path); } return; } $resource = new GlobResource($prefix, $pattern, $recursive, $forExclusion, $excluded); yield from $resource; } private function doImport($resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null) { try { $loader = $this->resolve($resource, $type); if ($loader instanceof self && null !== $this->currentDir) { $resource = $loader->getLocator()->locate($resource, $this->currentDir, false); } $resources = \is_array($resource) ? $resource : [$resource]; for ($i = 0; $i < $resourcesCount = \count($resources); ++$i) { if (isset(self::$loading[$resources[$i]])) { if ($i == $resourcesCount - 1) { throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading)); } } else { $resource = $resources[$i]; break; } } self::$loading[$resource] = true; try { $ret = $loader->load($resource, $type); } finally { unset(self::$loading[$resource]); } return $ret; } catch (FileLoaderImportCircularReferenceException $e) { throw $e; } catch (\Exception $e) { if (!$ignoreErrors) { // prevent embedded imports from nesting multiple exceptions if ($e instanceof LoaderLoadException) { throw $e; } throw new LoaderLoadException($resource, $sourceResource, 0, $e, $type); } } return null; } } Loader/GlobFileLoader.php 0000644 00000001330 15120141155 0011265 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; /** * GlobFileLoader loads files from a glob pattern. * * @author Fabien Potencier <fabien@symfony.com> */ class GlobFileLoader extends FileLoader { /** * {@inheritdoc} */ public function load($resource, string $type = null) { return $this->import($resource); } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { return 'glob' === $type; } } Loader/Loader.php 0000644 00000003663 15120141155 0007674 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; use Symfony\Component\Config\Exception\LoaderLoadException; /** * Loader is the abstract class used by all built-in loaders. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class Loader implements LoaderInterface { protected $resolver; protected $env; public function __construct(string $env = null) { $this->env = $env; } /** * {@inheritdoc} */ public function getResolver() { return $this->resolver; } /** * {@inheritdoc} */ public function setResolver(LoaderResolverInterface $resolver) { $this->resolver = $resolver; } /** * Imports a resource. * * @param mixed $resource A resource * @param string|null $type The resource type or null if unknown * * @return mixed */ public function import($resource, string $type = null) { return $this->resolve($resource, $type)->load($resource, $type); } /** * Finds a loader able to load an imported resource. * * @param mixed $resource A resource * @param string|null $type The resource type or null if unknown * * @return LoaderInterface * * @throws LoaderLoadException If no loader is found */ public function resolve($resource, string $type = null) { if ($this->supports($resource, $type)) { return $this; } $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type); if (false === $loader) { throw new LoaderLoadException($resource, null, 0, null, $type); } return $loader; } } Loader/LoaderInterface.php 0000644 00000002167 15120141155 0011513 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; /** * LoaderInterface is the interface implemented by all loader classes. * * @author Fabien Potencier <fabien@symfony.com> */ interface LoaderInterface { /** * Loads a resource. * * @param mixed $resource The resource * * @return mixed * * @throws \Exception If something went wrong */ public function load($resource, string $type = null); /** * Returns whether this class supports the given resource. * * @param mixed $resource A resource * * @return bool */ public function supports($resource, string $type = null); /** * Gets the loader resolver. * * @return LoaderResolverInterface */ public function getResolver(); /** * Sets the loader resolver. */ public function setResolver(LoaderResolverInterface $resolver); } Loader/LoaderResolver.php 0000644 00000003020 15120141155 0011401 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; /** * LoaderResolver selects a loader for a given resource. * * A resource can be anything (e.g. a full path to a config file or a Closure). * Each loader determines whether it can load a resource and how. * * @author Fabien Potencier <fabien@symfony.com> */ class LoaderResolver implements LoaderResolverInterface { /** * @var LoaderInterface[] An array of LoaderInterface objects */ private $loaders = []; /** * @param LoaderInterface[] $loaders An array of loaders */ public function __construct(array $loaders = []) { foreach ($loaders as $loader) { $this->addLoader($loader); } } /** * {@inheritdoc} */ public function resolve($resource, string $type = null) { foreach ($this->loaders as $loader) { if ($loader->supports($resource, $type)) { return $loader; } } return false; } public function addLoader(LoaderInterface $loader) { $this->loaders[] = $loader; $loader->setResolver($this); } /** * Returns the registered loaders. * * @return LoaderInterface[] */ public function getLoaders() { return $this->loaders; } } Loader/LoaderResolverInterface.php 0000644 00000001351 15120141155 0013227 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; /** * LoaderResolverInterface selects a loader for a given resource. * * @author Fabien Potencier <fabien@symfony.com> */ interface LoaderResolverInterface { /** * Returns a loader able to load the resource. * * @param mixed $resource A resource * @param string|null $type The resource type or null if unknown * * @return LoaderInterface|false */ public function resolve($resource, string $type = null); } Loader/ParamConfigurator.php 0000644 00000001131 15120141155 0012075 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Loader; /** * Placeholder for a parameter. * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ class ParamConfigurator { private $name; public function __construct(string $name) { $this->name = $name; } public function __toString(): string { return '%'.$this->name.'%'; } } Resource/ClassExistenceResource.php 0000644 00000015623 15120141155 0013473 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * ClassExistenceResource represents a class existence. * Freshness is only evaluated against resource existence. * * The resource must be a fully-qualified class name. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class ClassExistenceResource implements SelfCheckingResourceInterface { private $resource; private $exists; private static $autoloadLevel = 0; private static $autoloadedClass; private static $existsCache = []; /** * @param string $resource The fully-qualified class name * @param bool|null $exists Boolean when the existence check has already been done */ public function __construct(string $resource, bool $exists = null) { $this->resource = $resource; if (null !== $exists) { $this->exists = [$exists, null]; } } public function __toString(): string { return $this->resource; } public function getResource(): string { return $this->resource; } /** * {@inheritdoc} * * @throws \ReflectionException when a parent class/interface/trait is not found */ public function isFresh(int $timestamp): bool { $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false); if (null !== $exists = &self::$existsCache[$this->resource]) { if ($loaded) { $exists = [true, null]; } elseif (0 >= $timestamp && !$exists[0] && null !== $exists[1]) { throw new \ReflectionException($exists[1]); } } elseif ([false, null] === $exists = [$loaded, null]) { if (!self::$autoloadLevel++) { spl_autoload_register(__CLASS__.'::throwOnRequiredClass'); } $autoloadedClass = self::$autoloadedClass; self::$autoloadedClass = ltrim($this->resource, '\\'); try { $exists[0] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false); } catch (\Exception $e) { $exists[1] = $e->getMessage(); try { self::throwOnRequiredClass($this->resource, $e); } catch (\ReflectionException $e) { if (0 >= $timestamp) { throw $e; } } } catch (\Throwable $e) { $exists[1] = $e->getMessage(); throw $e; } finally { self::$autoloadedClass = $autoloadedClass; if (!--self::$autoloadLevel) { spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass'); } } } if (null === $this->exists) { $this->exists = $exists; } return $this->exists[0] xor !$exists[0]; } /** * @internal */ public function __sleep(): array { if (null === $this->exists) { $this->isFresh(0); } return ['resource', 'exists']; } /** * @internal */ public function __wakeup() { if (\is_bool($this->exists)) { $this->exists = [$this->exists, null]; } } /** * Throws a reflection exception when the passed class does not exist but is required. * * A class is considered "not required" when it's loaded as part of a "class_exists" or similar check. * * This function can be used as an autoload function to throw a reflection * exception if the class was not found by previous autoload functions. * * A previous exception can be passed. In this case, the class is considered as being * required totally, so if it doesn't exist, a reflection exception is always thrown. * If it exists, the previous exception is rethrown. * * @throws \ReflectionException * * @internal */ public static function throwOnRequiredClass(string $class, \Exception $previous = null) { // If the passed class is the resource being checked, we shouldn't throw. if (null === $previous && self::$autoloadedClass === $class) { return; } if (class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) { if (null !== $previous) { throw $previous; } return; } if ($previous instanceof \ReflectionException) { throw $previous; } $message = sprintf('Class "%s" not found.', $class); if (self::$autoloadedClass !== $class) { $message = substr_replace($message, sprintf(' while loading "%s"', self::$autoloadedClass), -1, 0); } if (null !== $previous) { $message = $previous->getMessage(); } $e = new \ReflectionException($message, 0, $previous); if (null !== $previous) { throw $e; } $trace = debug_backtrace(); $autoloadFrame = [ 'function' => 'spl_autoload_call', 'args' => [$class], ]; if (\PHP_VERSION_ID >= 80000 && isset($trace[1])) { $callerFrame = $trace[1]; $i = 2; } elseif (false !== $i = array_search($autoloadFrame, $trace, true)) { $callerFrame = $trace[++$i]; } else { throw $e; } if (isset($callerFrame['function']) && !isset($callerFrame['class'])) { switch ($callerFrame['function']) { case 'get_class_methods': case 'get_class_vars': case 'get_parent_class': case 'is_a': case 'is_subclass_of': case 'class_exists': case 'class_implements': case 'class_parents': case 'trait_exists': case 'defined': case 'interface_exists': case 'method_exists': case 'property_exists': case 'is_callable': return; } $props = [ 'file' => $callerFrame['file'] ?? null, 'line' => $callerFrame['line'] ?? null, 'trace' => \array_slice($trace, 1 + $i), ]; foreach ($props as $p => $v) { if (null !== $v) { $r = new \ReflectionProperty(\Exception::class, $p); $r->setAccessible(true); $r->setValue($e, $v); } } } throw $e; } } Resource/ComposerResource.php 0000644 00000003060 15120141155 0012335 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * ComposerResource tracks the PHP version and Composer dependencies. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ComposerResource implements SelfCheckingResourceInterface { private $vendors; private static $runtimeVendors; public function __construct() { self::refresh(); $this->vendors = self::$runtimeVendors; } public function getVendors(): array { return array_keys($this->vendors); } public function __toString(): string { return __CLASS__; } /** * {@inheritdoc} */ public function isFresh(int $timestamp): bool { self::refresh(); return array_values(self::$runtimeVendors) === array_values($this->vendors); } private static function refresh() { self::$runtimeVendors = []; foreach (get_declared_classes() as $class) { if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { $r = new \ReflectionClass($class); $v = \dirname($r->getFileName(), 2); if (is_file($v.'/composer/installed.json')) { self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json'); } } } } } Resource/DirectoryResource.php 0000644 00000005224 15120141155 0012516 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * DirectoryResource represents a resources stored in a subdirectory tree. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class DirectoryResource implements SelfCheckingResourceInterface { private $resource; private $pattern; /** * @param string $resource The file path to the resource * @param string|null $pattern A pattern to restrict monitored files * * @throws \InvalidArgumentException */ public function __construct(string $resource, string $pattern = null) { $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); $this->pattern = $pattern; if (false === $this->resource || !is_dir($this->resource)) { throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource)); } } public function __toString(): string { return md5(serialize([$this->resource, $this->pattern])); } public function getResource(): string { return $this->resource; } public function getPattern(): ?string { return $this->pattern; } /** * {@inheritdoc} */ public function isFresh(int $timestamp): bool { if (!is_dir($this->resource)) { return false; } if ($timestamp < filemtime($this->resource)) { return false; } foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { // if regex filtering is enabled only check matching files if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) { continue; } // always monitor directories for changes, except the .. entries // (otherwise deleted files wouldn't get detected) if ($file->isDir() && str_ends_with($file, '/..')) { continue; } // for broken links try { $fileMTime = $file->getMTime(); } catch (\RuntimeException $e) { continue; } // early return if a file's mtime exceeds the passed timestamp if ($timestamp < $fileMTime) { return false; } } return true; } } Resource/FileExistenceResource.php 0000644 00000002335 15120141155 0013301 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * FileExistenceResource represents a resource stored on the filesystem. * Freshness is only evaluated against resource creation or deletion. * * The resource can be a file or a directory. * * @author Charles-Henri Bruyand <charleshenri.bruyand@gmail.com> * * @final */ class FileExistenceResource implements SelfCheckingResourceInterface { private $resource; private $exists; /** * @param string $resource The file path to the resource */ public function __construct(string $resource) { $this->resource = $resource; $this->exists = file_exists($resource); } public function __toString(): string { return $this->resource; } public function getResource(): string { return $this->resource; } /** * {@inheritdoc} */ public function isFresh(int $timestamp): bool { return file_exists($this->resource) === $this->exists; } } Resource/FileResource.php 0000644 00000002716 15120141155 0011434 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * FileResource represents a resource stored on the filesystem. * * The resource can be a file or a directory. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class FileResource implements SelfCheckingResourceInterface { /** * @var string|false */ private $resource; /** * @param string $resource The file path to the resource * * @throws \InvalidArgumentException */ public function __construct(string $resource) { $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); if (false === $this->resource) { throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource)); } } public function __toString(): string { return $this->resource; } /** * Returns the canonicalized, absolute path to the resource. */ public function getResource(): string { return $this->resource; } /** * {@inheritdoc} */ public function isFresh(int $timestamp): bool { return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp; } } Resource/GlobResource.php 0000644 00000017414 15120141155 0011441 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Glob; /** * GlobResource represents a set of resources stored on the filesystem. * * Only existence/removal is tracked (not mtimes.) * * @author Nicolas Grekas <p@tchwork.com> * * @final * * @implements \IteratorAggregate<string, \SplFileInfo> */ class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface { private $prefix; private $pattern; private $recursive; private $hash; private $forExclusion; private $excludedPrefixes; private $globBrace; /** * @param string $prefix A directory prefix * @param string $pattern A glob pattern * @param bool $recursive Whether directories should be scanned recursively or not * * @throws \InvalidArgumentException */ public function __construct(string $prefix, string $pattern, bool $recursive, bool $forExclusion = false, array $excludedPrefixes = []) { ksort($excludedPrefixes); $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); $this->pattern = $pattern; $this->recursive = $recursive; $this->forExclusion = $forExclusion; $this->excludedPrefixes = $excludedPrefixes; $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0; if (false === $this->prefix) { throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix)); } } public function getPrefix(): string { return $this->prefix; } public function __toString(): string { return 'glob.'.$this->prefix.(int) $this->recursive.$this->pattern.(int) $this->forExclusion.implode("\0", $this->excludedPrefixes); } /** * {@inheritdoc} */ public function isFresh(int $timestamp): bool { $hash = $this->computeHash(); if (null === $this->hash) { $this->hash = $hash; } return $this->hash === $hash; } /** * @internal */ public function __sleep(): array { if (null === $this->hash) { $this->hash = $this->computeHash(); } return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes']; } /** * @internal */ public function __wakeup(): void { $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0; } public function getIterator(): \Traversable { if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { return; } $prefix = str_replace('\\', '/', $this->prefix); $paths = null; if ('' === $this->pattern && is_file($prefix)) { $paths = [$this->prefix]; } elseif (!str_starts_with($this->prefix, 'phar://') && !str_contains($this->pattern, '/**/')) { if ($this->globBrace || !str_contains($this->pattern, '{')) { $paths = glob($this->prefix.$this->pattern, \GLOB_NOSORT | $this->globBrace); } elseif (!str_contains($this->pattern, '\\') || !preg_match('/\\\\[,{}]/', $this->pattern)) { foreach ($this->expandGlob($this->pattern) as $p) { $paths[] = glob($this->prefix.$p, \GLOB_NOSORT); } $paths = array_merge(...$paths); } } if (null !== $paths) { natsort($paths); foreach ($paths as $path) { if ($this->excludedPrefixes) { $normalizedPath = str_replace('\\', '/', $path); do { if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { continue 2; } } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); } if (is_file($path)) { yield $path => new \SplFileInfo($path); } if (!is_dir($path)) { continue; } if ($this->forExclusion) { yield $path => new \SplFileInfo($path); continue; } if (!$this->recursive || isset($this->excludedPrefixes[str_replace('\\', '/', $path)])) { continue; } $files = iterator_to_array(new \RecursiveIteratorIterator( new \RecursiveCallbackFilterIterator( new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), function (\SplFileInfo $file, $path) { return !isset($this->excludedPrefixes[str_replace('\\', '/', $path)]) && '.' !== $file->getBasename()[0]; } ), \RecursiveIteratorIterator::LEAVES_ONLY )); uksort($files, 'strnatcmp'); foreach ($files as $path => $info) { if ($info->isFile()) { yield $path => $info; } } } return; } if (!class_exists(Finder::class)) { throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern)); } if (is_file($prefix = $this->prefix)) { $prefix = \dirname($prefix); $pattern = basename($prefix).$this->pattern; } else { $pattern = $this->pattern; } $finder = new Finder(); $regex = Glob::toRegex($pattern); if ($this->recursive) { $regex = substr_replace($regex, '(/|$)', -2, 1); } $prefixLen = \strlen($prefix); foreach ($finder->followLinks()->sortByName()->in($prefix) as $path => $info) { $normalizedPath = str_replace('\\', '/', $path); if (!preg_match($regex, substr($normalizedPath, $prefixLen)) || !$info->isFile()) { continue; } if ($this->excludedPrefixes) { do { if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { continue 2; } } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); } yield $path => $info; } } private function computeHash(): string { $hash = hash_init('md5'); foreach ($this->getIterator() as $path => $info) { hash_update($hash, $path."\n"); } return hash_final($hash); } private function expandGlob(string $pattern): array { $segments = preg_split('/\{([^{}]*+)\}/', $pattern, -1, \PREG_SPLIT_DELIM_CAPTURE); $paths = [$segments[0]]; $patterns = []; for ($i = 1; $i < \count($segments); $i += 2) { $patterns = []; foreach (explode(',', $segments[$i]) as $s) { foreach ($paths as $p) { $patterns[] = $p.$s.$segments[1 + $i]; } } $paths = $patterns; } $j = 0; foreach ($patterns as $i => $p) { if (str_contains($p, '{')) { $p = $this->expandGlob($p); array_splice($paths, $i + $j, 1, $p); $j += \count($p) - 1; } } return $paths; } } Resource/ReflectionClassResource.php 0000644 00000022076 15120141155 0013636 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ReflectionClassResource implements SelfCheckingResourceInterface { private $files = []; private $className; private $classReflector; private $excludedVendors = []; private $hash; public function __construct(\ReflectionClass $classReflector, array $excludedVendors = []) { $this->className = $classReflector->name; $this->classReflector = $classReflector; $this->excludedVendors = $excludedVendors; } /** * {@inheritdoc} */ public function isFresh(int $timestamp): bool { if (null === $this->hash) { $this->hash = $this->computeHash(); $this->loadFiles($this->classReflector); } foreach ($this->files as $file => $v) { if (false === $filemtime = @filemtime($file)) { return false; } if ($filemtime > $timestamp) { return $this->hash === $this->computeHash(); } } return true; } public function __toString(): string { return 'reflection.'.$this->className; } /** * @internal */ public function __sleep(): array { if (null === $this->hash) { $this->hash = $this->computeHash(); $this->loadFiles($this->classReflector); } return ['files', 'className', 'hash']; } private function loadFiles(\ReflectionClass $class) { foreach ($class->getInterfaces() as $v) { $this->loadFiles($v); } do { $file = $class->getFileName(); if (false !== $file && is_file($file)) { foreach ($this->excludedVendors as $vendor) { if (str_starts_with($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { $file = false; break; } } if ($file) { $this->files[$file] = null; } } foreach ($class->getTraits() as $v) { $this->loadFiles($v); } } while ($class = $class->getParentClass()); } private function computeHash(): string { if (null === $this->classReflector) { try { $this->classReflector = new \ReflectionClass($this->className); } catch (\ReflectionException $e) { // the class does not exist anymore return false; } } $hash = hash_init('md5'); foreach ($this->generateSignature($this->classReflector) as $info) { hash_update($hash, $info); } return hash_final($hash); } private function generateSignature(\ReflectionClass $class): iterable { if (\PHP_VERSION_ID >= 80000) { $attributes = []; foreach ($class->getAttributes() as $a) { $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; } yield print_r($attributes, true); $attributes = []; } yield $class->getDocComment(); yield (int) $class->isFinal(); yield (int) $class->isAbstract(); if ($class->isTrait()) { yield print_r(class_uses($class->name), true); } else { yield print_r(class_parents($class->name), true); yield print_r(class_implements($class->name), true); yield print_r($class->getConstants(), true); } if (!$class->isInterface()) { $defaults = $class->getDefaultProperties(); foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) { if (\PHP_VERSION_ID >= 80000) { foreach ($p->getAttributes() as $a) { $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; } yield print_r($attributes, true); $attributes = []; } yield $p->getDocComment(); yield $p->isDefault() ? '<default>' : ''; yield $p->isPublic() ? 'public' : 'protected'; yield $p->isStatic() ? 'static' : ''; yield '$'.$p->name; yield print_r(isset($defaults[$p->name]) && !\is_object($defaults[$p->name]) ? $defaults[$p->name] : null, true); } } $defined = \Closure::bind(static function ($c) { return \defined($c); }, null, $class->name); foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { if (\PHP_VERSION_ID >= 80000) { foreach ($m->getAttributes() as $a) { $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; } yield print_r($attributes, true); $attributes = []; } $defaults = []; $parametersWithUndefinedConstants = []; foreach ($m->getParameters() as $p) { if (\PHP_VERSION_ID >= 80000) { foreach ($p->getAttributes() as $a) { $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; } yield print_r($attributes, true); $attributes = []; } if (!$p->isDefaultValueAvailable()) { $defaults[$p->name] = null; continue; } if (\PHP_VERSION_ID >= 80100) { $defaults[$p->name] = (string) $p; continue; } if (!$p->isDefaultValueConstant() || $defined($p->getDefaultValueConstantName())) { $defaults[$p->name] = $p->getDefaultValue(); continue; } $defaults[$p->name] = $p->getDefaultValueConstantName(); $parametersWithUndefinedConstants[$p->name] = true; } if (!$parametersWithUndefinedConstants) { yield preg_replace('/^ @@.*/m', '', $m); } else { $t = $m->getReturnType(); $stack = [ $m->getDocComment(), $m->getName(), $m->isAbstract(), $m->isFinal(), $m->isStatic(), $m->isPublic(), $m->isPrivate(), $m->isProtected(), $m->returnsReference(), $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t, ]; foreach ($m->getParameters() as $p) { if (!isset($parametersWithUndefinedConstants[$p->name])) { $stack[] = (string) $p; } else { $t = $p->getType(); $stack[] = $p->isOptional(); $stack[] = $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t; $stack[] = $p->isPassedByReference(); $stack[] = $p->isVariadic(); $stack[] = $p->getName(); } } yield implode(',', $stack); } yield print_r($defaults, true); } if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) { return; } if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) { yield EventSubscriberInterface::class; yield print_r($class->name::getSubscribedEvents(), true); } if (interface_exists(MessageSubscriberInterface::class, false) && $class->isSubclassOf(MessageSubscriberInterface::class)) { yield MessageSubscriberInterface::class; foreach ($class->name::getHandledMessages() as $key => $value) { yield $key.print_r($value, true); } } if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { yield ServiceSubscriberInterface::class; yield print_r($class->name::getSubscribedServices(), true); } } } Resource/ResourceInterface.php 0000644 00000001674 15120141155 0012457 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * ResourceInterface is the interface that must be implemented by all Resource classes. * * @author Fabien Potencier <fabien@symfony.com> */ interface ResourceInterface { /** * Returns a string representation of the Resource. * * This method is necessary to allow for resource de-duplication, for example by means * of array_unique(). The string returned need not have a particular meaning, but has * to be identical for different ResourceInterface instances referring to the same * resource; and it should be unlikely to collide with that of other, unrelated * resource instances. */ public function __toString(); } Resource/SelfCheckingResourceChecker.php 0000644 00000002540 15120141155 0014362 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; use Symfony\Component\Config\ResourceCheckerInterface; /** * Resource checker for instances of SelfCheckingResourceInterface. * * As these resources perform the actual check themselves, we can provide * this class as a standard way of validating them. * * @author Matthias Pigulla <mp@webfactory.de> */ class SelfCheckingResourceChecker implements ResourceCheckerInterface { // Common shared cache, because this checker can be used in different // situations. For example, when using the full stack framework, the router // and the container have their own cache. But they may check the very same // resources private static $cache = []; public function supports(ResourceInterface $metadata) { return $metadata instanceof SelfCheckingResourceInterface; } /** * @param SelfCheckingResourceInterface $resource */ public function isFresh(ResourceInterface $resource, int $timestamp) { $key = "$resource:$timestamp"; return self::$cache[$key] ?? self::$cache[$key] = $resource->isFresh($timestamp); } } Resource/SelfCheckingResourceInterface.php 0000644 00000001413 15120141155 0014714 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Resource; /** * Interface for Resources that can check for freshness autonomously, * without special support from external services. * * @author Matthias Pigulla <mp@webfactory.de> */ interface SelfCheckingResourceInterface extends ResourceInterface { /** * Returns true if the resource has not been updated since the given timestamp. * * @param int $timestamp The last time the resource was loaded * * @return bool */ public function isFresh(int $timestamp); } Util/Exception/InvalidXmlException.php 0000644 00000001046 15120141155 0014052 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Util\Exception; /** * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated * to the actual XML parsing. * * @author Ole Rößner <ole@roessner.it> */ class InvalidXmlException extends XmlParsingException { } Util/Exception/XmlParsingException.php 0000644 00000000722 15120141155 0014067 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Util\Exception; /** * Exception class for when XML cannot be parsed properly. * * @author Ole Rößner <ole@roessner.it> */ class XmlParsingException extends \InvalidArgumentException { } Util/XmlUtils.php 0000644 00000023251 15120141155 0007751 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config\Util; use Symfony\Component\Config\Util\Exception\InvalidXmlException; use Symfony\Component\Config\Util\Exception\XmlParsingException; /** * XMLUtils is a bunch of utility methods to XML operations. * * This class contains static methods only and is not meant to be instantiated. * * @author Fabien Potencier <fabien@symfony.com> * @author Martin Hasoň <martin.hason@gmail.com> * @author Ole Rößner <ole@roessner.it> */ class XmlUtils { /** * This class should not be instantiated. */ private function __construct() { } /** * Parses an XML string. * * @param string $content An XML string * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation * * @return \DOMDocument * * @throws XmlParsingException When parsing of XML file returns error * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself * @throws \RuntimeException When DOM extension is missing */ public static function parse(string $content, $schemaOrCallable = null) { if (!\extension_loaded('dom')) { throw new \LogicException('Extension DOM is required.'); } $internalErrors = libxml_use_internal_errors(true); if (\LIBXML_VERSION < 20900) { $disableEntities = libxml_disable_entity_loader(true); } libxml_clear_errors(); $dom = new \DOMDocument(); $dom->validateOnParse = true; if (!$dom->loadXML($content, \LIBXML_NONET | (\defined('LIBXML_COMPACT') ? \LIBXML_COMPACT : 0))) { if (\LIBXML_VERSION < 20900) { libxml_disable_entity_loader($disableEntities); } throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors))); } $dom->normalizeDocument(); libxml_use_internal_errors($internalErrors); if (\LIBXML_VERSION < 20900) { libxml_disable_entity_loader($disableEntities); } foreach ($dom->childNodes as $child) { if (\XML_DOCUMENT_TYPE_NODE === $child->nodeType) { throw new XmlParsingException('Document types are not allowed.'); } } if (null !== $schemaOrCallable) { $internalErrors = libxml_use_internal_errors(true); libxml_clear_errors(); $e = null; if (\is_callable($schemaOrCallable)) { try { $valid = $schemaOrCallable($dom, $internalErrors); } catch (\Exception $e) { $valid = false; } } elseif (!\is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { $schemaSource = file_get_contents((string) $schemaOrCallable); $valid = @$dom->schemaValidateSource($schemaSource); } else { libxml_use_internal_errors($internalErrors); throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); } if (!$valid) { $messages = static::getXmlErrors($internalErrors); if (empty($messages)) { throw new InvalidXmlException('The XML is not valid.', 0, $e); } throw new XmlParsingException(implode("\n", $messages), 0, $e); } } libxml_clear_errors(); libxml_use_internal_errors($internalErrors); return $dom; } /** * Loads an XML file. * * @param string $file An XML file path * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation * * @return \DOMDocument * * @throws \InvalidArgumentException When loading of XML file returns error * @throws XmlParsingException When XML parsing returns any errors * @throws \RuntimeException When DOM extension is missing */ public static function loadFile(string $file, $schemaOrCallable = null) { if (!is_file($file)) { throw new \InvalidArgumentException(sprintf('Resource "%s" is not a file.', $file)); } if (!is_readable($file)) { throw new \InvalidArgumentException(sprintf('File "%s" is not readable.', $file)); } $content = @file_get_contents($file); if ('' === trim($content)) { throw new \InvalidArgumentException(sprintf('File "%s" does not contain valid XML, it is empty.', $file)); } try { return static::parse($content, $schemaOrCallable); } catch (InvalidXmlException $e) { throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); } } /** * Converts a \DOMElement object to a PHP array. * * The following rules applies during the conversion: * * * Each tag is converted to a key value or an array * if there is more than one "value" * * * The content of a tag is set under a "value" key (<foo>bar</foo>) * if the tag also has some nested tags * * * The attributes are converted to keys (<foo foo="bar"/>) * * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>) * * @param \DOMElement $element A \DOMElement instance * @param bool $checkPrefix Check prefix in an element or an attribute name * * @return mixed */ public static function convertDomElementToArray(\DOMElement $element, bool $checkPrefix = true) { $prefix = (string) $element->prefix; $empty = true; $config = []; foreach ($element->attributes as $name => $node) { if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], true)) { continue; } $config[$name] = static::phpize($node->value); $empty = false; } $nodeValue = false; foreach ($element->childNodes as $node) { if ($node instanceof \DOMText) { if ('' !== trim($node->nodeValue)) { $nodeValue = trim($node->nodeValue); $empty = false; } } elseif ($checkPrefix && $prefix != (string) $node->prefix) { continue; } elseif (!$node instanceof \DOMComment) { $value = static::convertDomElementToArray($node, $checkPrefix); $key = $node->localName; if (isset($config[$key])) { if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) { $config[$key] = [$config[$key]]; } $config[$key][] = $value; } else { $config[$key] = $value; } $empty = false; } } if (false !== $nodeValue) { $value = static::phpize($nodeValue); if (\count($config)) { $config['value'] = $value; } else { $config = $value; } } return !$empty ? $config : null; } /** * Converts an xml value to a PHP type. * * @param mixed $value * * @return mixed */ public static function phpize($value) { $value = (string) $value; $lowercaseValue = strtolower($value); switch (true) { case 'null' === $lowercaseValue: return null; case ctype_digit($value): case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): $raw = $value; $cast = (int) $value; return self::isOctal($value) ? \intval($value, 8) : (($raw === (string) $cast) ? $cast : $raw); case 'true' === $lowercaseValue: return true; case 'false' === $lowercaseValue: return false; case isset($value[1]) && '0b' == $value[0].$value[1] && preg_match('/^0b[01]*$/', $value): return bindec($value); case is_numeric($value): return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; case preg_match('/^0x[0-9a-f]++$/i', $value): return hexdec($value); case preg_match('/^[+-]?[0-9]+(\.[0-9]+)?$/', $value): return (float) $value; default: return $value; } } protected static function getXmlErrors(bool $internalErrors) { $errors = []; foreach (libxml_get_errors() as $error) { $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, trim($error->message), $error->file ?: 'n/a', $error->line, $error->column ); } libxml_clear_errors(); libxml_use_internal_errors($internalErrors); return $errors; } private static function isOctal(string $str): bool { if ('-' === $str[0]) { $str = substr($str, 1); } return $str === '0'.decoct(\intval($str, 8)); } } CHANGELOG.md 0000644 00000010213 15120141155 0006345 0 ustar 00 CHANGELOG ========= 5.3.0 ----- * Add support for generating `ConfigBuilder` for extensions 5.1.0 ----- * updated the signature of method `NodeDefinition::setDeprecated()` to `NodeDefinition::setDeprecation(string $package, string $version, string $message)` * updated the signature of method `BaseNode::setDeprecated()` to `BaseNode::setDeprecation(string $package, string $version, string $message)` * deprecated passing a null message to `BaseNode::setDeprecated()` to un-deprecate a node * deprecated `BaseNode::getDeprecationMessage()`, use `BaseNode::getDeprecation()` instead 5.0.0 ----- * Dropped support for constructing a `TreeBuilder` without passing root node information. * Removed the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead * Added method `getChildNodeDefinitions()` to ParentNodeDefinitionInterface * Removed `FileLoaderLoadException`, use `LoaderLoadException` instead 4.4.0 ----- * added a way to exclude patterns of resources from being imported by the `import()` method 4.3.0 ----- * deprecated using environment variables with `cannotBeEmpty()` if the value is validated with `validate()` * made `Resource\*` classes final and not implement `Serializable` anymore * deprecated the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead 4.2.0 ----- * deprecated constructing a `TreeBuilder` without passing root node information * renamed `FileLoaderLoadException` to `LoaderLoadException` 4.1.0 ----- * added `setPathSeparator` method to `NodeBuilder` class * added third `$pathSeparator` constructor argument to `BaseNode` * the `Processor` class has been made final 4.0.0 ----- * removed `ConfigCachePass` 3.4.0 ----- * added `setDeprecated()` method to indicate a deprecated node * added `XmlUtils::parse()` method to parse an XML string * deprecated `ConfigCachePass` 3.3.0 ----- * added `ReflectionClassResource` class * added second `$exists` constructor argument to `ClassExistenceResource` * made `ClassExistenceResource` work with interfaces and traits * added `ConfigCachePass` (originally in FrameworkBundle) * added `castToArray()` helper to turn any config value into an array 3.0.0 ----- * removed `ReferenceDumper` class * removed the `ResourceInterface::isFresh()` method * removed `BCResourceInterfaceChecker` class * removed `ResourceInterface::getResource()` method 2.8.0 ----- The edge case of defining just one value for nodes of type Enum is now allowed: ```php $rootNode ->children() ->enumNode('variable') ->values(['value']) ->end() ->end() ; ``` Before: `InvalidArgumentException` (variable must contain at least two distinct elements). After: the code will work as expected and it will restrict the values of the `variable` option to just `value`. * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they can be validated that way, make them implement the new `SelfCheckingResourceInterface`. * deprecated the getResource() method in ResourceInterface. You can still call this method on concrete classes implementing the interface, but it does not make sense at the interface level as you need to know about the particular type of resource at hand to understand the semantics of the returned value. 2.7.0 ----- * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` implementation to delegate creation of ConfigCache instances 2.2.0 ----- * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` to ease configuration when some sections are respectively disabled / enabled by default. * added a `normalizeKeys()` method for array nodes (to avoid key normalization) * added numerical type handling for config definitions * added convenience methods for optional configuration sections to `ArrayNodeDefinition` * added a utils class for XML manipulations 2.1.0 ----- * added a way to add documentation on configuration * implemented `Serializable` on resources * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type hinting ConfigCache.php 0000644 00000003004 15120141155 0007376 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; /** * ConfigCache caches arbitrary content in files on disk. * * When in debug mode, those metadata resources that implement * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will * be used to check cache freshness. * * @author Fabien Potencier <fabien@symfony.com> * @author Matthias Pigulla <mp@webfactory.de> */ class ConfigCache extends ResourceCheckerConfigCache { private $debug; /** * @param string $file The absolute cache path * @param bool $debug Whether debugging is enabled or not */ public function __construct(string $file, bool $debug) { $this->debug = $debug; $checkers = []; if (true === $this->debug) { $checkers = [new SelfCheckingResourceChecker()]; } parent::__construct($file, $checkers); } /** * Checks if the cache is still fresh. * * This implementation always returns true when debug is off and the * cache file exists. * * @return bool */ public function isFresh() { if (!$this->debug && is_file($this->getPath())) { return true; } return parent::isFresh(); } } ConfigCacheFactory.php 0000644 00000002121 15120141155 0010725 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; /** * Basic implementation of ConfigCacheFactoryInterface that * creates an instance of the default ConfigCache. * * This factory and/or cache <em>do not</em> support cache validation * by means of ResourceChecker instances (that is, service-based). * * @author Matthias Pigulla <mp@webfactory.de> */ class ConfigCacheFactory implements ConfigCacheFactoryInterface { private $debug; /** * @param bool $debug The debug flag to pass to ConfigCache */ public function __construct(bool $debug) { $this->debug = $debug; } /** * {@inheritdoc} */ public function cache(string $file, callable $callback) { $cache = new ConfigCache($file, $this->debug); if (!$cache->isFresh()) { $callback($cache); } return $cache; } } ConfigCacheFactoryInterface.php 0000644 00000001676 15120141155 0012564 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; /** * Interface for a ConfigCache factory. This factory creates * an instance of ConfigCacheInterface and initializes the * cache if necessary. * * @author Matthias Pigulla <mp@webfactory.de> */ interface ConfigCacheFactoryInterface { /** * Creates a cache instance and (re-)initializes it if necessary. * * @param string $file The absolute cache file path * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback * * @return ConfigCacheInterface */ public function cache(string $file, callable $callable); } ConfigCacheInterface.php 0000644 00000002402 15120141155 0011220 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; use Symfony\Component\Config\Resource\ResourceInterface; /** * Interface for ConfigCache. * * @author Matthias Pigulla <mp@webfactory.de> */ interface ConfigCacheInterface { /** * Gets the cache file path. * * @return string */ public function getPath(); /** * Checks if the cache is still fresh. * * This check should take the metadata passed to the write() method into consideration. * * @return bool */ public function isFresh(); /** * Writes the given content into the cache file. Metadata will be stored * independently and can be used to check cache freshness at a later time. * * @param string $content The content to write into the cache * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances * * @throws \RuntimeException When the cache file cannot be written */ public function write(string $content, array $metadata = null); } FileLocator.php 0000644 00000004753 15120141155 0007464 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; /** * FileLocator uses an array of pre-defined paths to find files. * * @author Fabien Potencier <fabien@symfony.com> */ class FileLocator implements FileLocatorInterface { protected $paths; /** * @param string|string[] $paths A path or an array of paths where to look for resources */ public function __construct($paths = []) { $this->paths = (array) $paths; } /** * {@inheritdoc} */ public function locate(string $name, string $currentPath = null, bool $first = true) { if ('' === $name) { throw new \InvalidArgumentException('An empty file name is not valid to be located.'); } if ($this->isAbsolutePath($name)) { if (!file_exists($name)) { throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, [$name]); } return $name; } $paths = $this->paths; if (null !== $currentPath) { array_unshift($paths, $currentPath); } $paths = array_unique($paths); $filepaths = $notfound = []; foreach ($paths as $path) { if (@file_exists($file = $path.\DIRECTORY_SEPARATOR.$name)) { if (true === $first) { return $file; } $filepaths[] = $file; } else { $notfound[] = $file; } } if (!$filepaths) { throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: "%s").', $name, implode('", "', $paths)), 0, null, $notfound); } return $filepaths; } /** * Returns whether the file path is an absolute path. */ private function isAbsolutePath(string $file): bool { if ('/' === $file[0] || '\\' === $file[0] || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && ('\\' === $file[2] || '/' === $file[2]) ) || null !== parse_url($file, \PHP_URL_SCHEME) ) { return true; } return false; } } FileLocatorInterface.php 0000644 00000002041 15120141155 0011271 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; /** * @author Fabien Potencier <fabien@symfony.com> */ interface FileLocatorInterface { /** * Returns a full path for a given file name. * * @param string $name The file name to locate * @param string|null $currentPath The current path * @param bool $first Whether to return the first occurrence or an array of filenames * * @return string|array The full path to the file or an array of file paths * * @throws \InvalidArgumentException If $name is empty * @throws FileLocatorFileNotFoundException If a file is not found */ public function locate(string $name, string $currentPath = null, bool $first = true); } LICENSE 0000644 00000002054 15120141155 0005545 0 ustar 00 Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. README.md 0000644 00000001124 15120141155 0006014 0 ustar 00 Config Component ================ The Config component helps find, load, combine, autofill and validate configuration values of any kind, whatever their source may be (YAML, XML, INI files, or for instance a database). Resources --------- * [Documentation](https://symfony.com/doc/current/components/config.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) ResourceCheckerConfigCache.php 0000644 00000012730 15120141155 0012401 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; /** * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface * to check whether cached data is still fresh. * * @author Matthias Pigulla <mp@webfactory.de> */ class ResourceCheckerConfigCache implements ConfigCacheInterface { /** * @var string */ private $file; /** * @var iterable<mixed, ResourceCheckerInterface> */ private $resourceCheckers; /** * @param string $file The absolute cache path * @param iterable<mixed, ResourceCheckerInterface> $resourceCheckers The ResourceCheckers to use for the freshness check */ public function __construct(string $file, iterable $resourceCheckers = []) { $this->file = $file; $this->resourceCheckers = $resourceCheckers; } /** * {@inheritdoc} */ public function getPath() { return $this->file; } /** * Checks if the cache is still fresh. * * This implementation will make a decision solely based on the ResourceCheckers * passed in the constructor. * * The first ResourceChecker that supports a given resource is considered authoritative. * Resources with no matching ResourceChecker will silently be ignored and considered fresh. * * @return bool */ public function isFresh() { if (!is_file($this->file)) { return false; } if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) { $this->resourceCheckers = iterator_to_array($this->resourceCheckers); } if (!\count($this->resourceCheckers)) { return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all } $metadata = $this->getMetaFile(); if (!is_file($metadata)) { return false; } $meta = $this->safelyUnserialize($metadata); if (false === $meta) { return false; } $time = filemtime($this->file); foreach ($meta as $resource) { foreach ($this->resourceCheckers as $checker) { if (!$checker->supports($resource)) { continue; // next checker } if ($checker->isFresh($resource, $time)) { break; // no need to further check this resource } return false; // cache is stale } // no suitable checker found, ignore this resource } return true; } /** * Writes cache. * * @param string $content The content to write in the cache * @param ResourceInterface[] $metadata An array of metadata * * @throws \RuntimeException When cache file can't be written */ public function write(string $content, array $metadata = null) { $mode = 0666; $umask = umask(); $filesystem = new Filesystem(); $filesystem->dumpFile($this->file, $content); try { $filesystem->chmod($this->file, $mode, $umask); } catch (IOException $e) { // discard chmod failure (some filesystem may not support it) } if (null !== $metadata) { $filesystem->dumpFile($this->getMetaFile(), serialize($metadata)); try { $filesystem->chmod($this->getMetaFile(), $mode, $umask); } catch (IOException $e) { // discard chmod failure (some filesystem may not support it) } } if (\function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) { @opcache_invalidate($this->file, true); } } /** * Gets the meta file path. */ private function getMetaFile(): string { return $this->file.'.meta'; } private function safelyUnserialize(string $file) { $meta = false; $content = file_get_contents($file); $signalingException = new \UnexpectedValueException(); $prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback'); $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) { if (__FILE__ === $file) { throw $signalingException; } return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; }); try { $meta = unserialize($content); } catch (\Throwable $e) { if ($e !== $signalingException) { throw $e; } } finally { restore_error_handler(); ini_set('unserialize_callback_func', $prevUnserializeHandler); } return $meta; } /** * @internal */ public static function handleUnserializeCallback(string $class) { trigger_error('Class not found: '.$class); } } ResourceCheckerConfigCacheFactory.php 0000644 00000002057 15120141155 0013732 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; /** * A ConfigCacheFactory implementation that validates the * cache with an arbitrary set of ResourceCheckers. * * @author Matthias Pigulla <mp@webfactory.de> */ class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface { private $resourceCheckers = []; /** * @param iterable<int, ResourceCheckerInterface> $resourceCheckers */ public function __construct(iterable $resourceCheckers = []) { $this->resourceCheckers = $resourceCheckers; } /** * {@inheritdoc} */ public function cache(string $file, callable $callable) { $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); if (!$cache->isFresh()) { $callable($cache); } return $cache; } } ResourceCheckerInterface.php 0000644 00000002316 15120141155 0012147 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Config; use Symfony\Component\Config\Resource\ResourceInterface; /** * Interface for ResourceCheckers. * * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated * metadata resources are passed to ResourceCheckers. The ResourceCheckers * can then inspect the resources and decide whether the cache can be considered * fresh or not. * * @author Matthias Pigulla <mp@webfactory.de> * @author Benjamin Klotz <bk@webfactory.de> */ interface ResourceCheckerInterface { /** * Queries the ResourceChecker whether it can validate a given * resource or not. * * @return bool */ public function supports(ResourceInterface $metadata); /** * Validates the resource. * * @param int $timestamp The timestamp at which the cache associated with this resource was created * * @return bool */ public function isFresh(ResourceInterface $resource, int $timestamp); } composer.json 0000644 00000002514 15120141155 0007263 0 ustar 00 { "name": "symfony/config", "type": "library", "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1|^3", "symfony/filesystem": "^4.4|^5.0|^6.0", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-php80": "^1.16", "symfony/polyfill-php81": "^1.22" }, "require-dev": { "symfony/event-dispatcher": "^4.4|^5.0|^6.0", "symfony/finder": "^4.4|^5.0|^6.0", "symfony/messenger": "^4.4|^5.0|^6.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/yaml": "^4.4|^5.0|^6.0" }, "conflict": { "symfony/finder": "<4.4" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" }, "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" }
Coded With 💗 by
0x6ick