├── .djlintrc ├── .editorconfig ├── .env ├── .github ├── dependabot.yml └── workflows │ ├── php.yml │ └── split.yml ├── .gitignore ├── .phive └── phars.xml ├── .php-cs-fixer.dist.php ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── monorepo-builder.php ├── packages ├── file-association-contracts │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ └── FileAssociationInterface.php ├── file-association-entity │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ ├── config │ │ └── doctrine │ │ │ ├── AbstractFile.orm.xml │ │ │ └── EmbeddedMetadata.orm.xml │ └── src │ │ ├── AbstractFile.php │ │ ├── EmbeddedMetadata.php │ │ ├── FileCollection.php │ │ ├── FileDecorator.php │ │ ├── FileMetadataDecorator.php │ │ ├── FileTrait.php │ │ ├── ReadableFileCollection.php │ │ └── UnsetFile.php ├── file-association │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── Attribute │ │ ├── AsFileAssociation.php │ │ └── WithFileAssociation.php │ │ ├── ClassBasedFileLocationResolver │ │ ├── ChainedClassBasedFileLocationResolver.php │ │ └── DefaultClassBasedFileLocationResolver.php │ │ ├── ClassMetadataFactory │ │ ├── CachingClassMetadataFactory.php │ │ └── DefaultClassMetadataFactory.php │ │ ├── ClassSignatureResolver │ │ ├── AttributeClassSignatureResolver.php │ │ ├── ChainClassSignatureResolver.php │ │ ├── DefaultClassSignatureResolver.php │ │ └── MetadataClassSignatureResolver.php │ │ ├── Contracts │ │ ├── ClassBasedFileLocationResolverInterface.php │ │ ├── ClassMetadataFactoryInterface.php │ │ ├── ClassSignatureResolverInterface.php │ │ ├── FilePropertyManagerInterface.php │ │ ├── ObjectIdResolverInterface.php │ │ ├── ObjectManagerInterface.php │ │ ├── PropertyListerInterface.php │ │ ├── PropertyReaderInterface.php │ │ └── PropertyWriterInterface.php │ │ ├── Exception │ │ ├── DuplicatePropertyNameException.php │ │ ├── FileAssociationException.php │ │ ├── FileLocationResolver │ │ │ ├── ChainedClassNotSupportedException.php │ │ │ ├── FileLocationResolverException.php │ │ │ ├── InvalidProxyException.php │ │ │ └── ObjectNotSupportedException.php │ │ ├── InvalidClassSignatureException.php │ │ ├── LogicException.php │ │ └── ObjectIdResolver │ │ │ ├── ChainedObjectIdResolverException.php │ │ │ ├── EmptyIdException.php │ │ │ ├── IdNotSupportedException.php │ │ │ ├── MethodNotFoundException.php │ │ │ ├── ObjectIdResolverException.php │ │ │ └── ObjectNotSupportedException.php │ │ ├── FilePropertyManager │ │ ├── DefaultFilePropertyManager.php │ │ └── LoggingFilePropertyManager.php │ │ ├── Model │ │ ├── ClassMetadata.php │ │ ├── FetchMode.php │ │ ├── FilePointer.php │ │ ├── MissingFile.php │ │ ├── ObjectOperationResult.php │ │ ├── ObjectOperationType.php │ │ ├── Property.php │ │ ├── PropertyMetadata.php │ │ ├── PropertyOperationAction.php │ │ └── PropertyOperationResult.php │ │ ├── ObjectIdResolver │ │ ├── ChainedObjectIdResolver.php │ │ ├── DefaultObjectIdResolver.php │ │ └── DoctrineObjectIdResolver.php │ │ ├── ObjectManager │ │ └── DefaultObjectManager.php │ │ ├── PropertyLister │ │ ├── AttributesPropertyLister.php │ │ ├── ChainPropertyLister.php │ │ └── FileAssociationInterfacePropertyLister.php │ │ ├── PropertyReaderWriter │ │ └── DefaultPropertyReaderWriter.php │ │ ├── PropertyRecorder │ │ └── PropertyRecorder.php │ │ ├── Reconstitutor │ │ ├── AttributeReconstitutor.php │ │ └── InterfaceReconstitutor.php │ │ └── Util │ │ ├── ClassUtil.php │ │ ├── FileLocationUtil.php │ │ └── ProxyUtil.php ├── file-bundle │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ ├── config │ │ ├── file-association-debug.php │ │ ├── file-association.php │ │ ├── file-bundle.php │ │ ├── file-image.php │ │ ├── file-server.php │ │ ├── file-zip.php │ │ ├── file.php │ │ ├── rekalogika_file.yaml │ │ └── tests.php │ ├── src │ │ ├── Command │ │ │ └── FileLocationResolverCommand.php │ │ ├── Debug │ │ │ ├── FileDataCollector.php │ │ │ ├── TraceableFilePropertyManager.php │ │ │ └── TraceableObjectManager.php │ │ ├── DefaultFilesystemFactory.php │ │ ├── DependencyInjection │ │ │ ├── Configuration.php │ │ │ ├── FilterPass.php │ │ │ └── RekalogikaFileExtension.php │ │ └── RekalogikaFileBundle.php │ ├── templates │ │ ├── data_collector.html.twig │ │ └── file.svg │ └── translations │ │ ├── rekalogika_file+intl-icu.en.xlf │ │ └── rekalogika_file+intl-icu.id.xlf ├── file-contracts │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── DirectoryInterface.php │ │ ├── Exception │ │ ├── File │ │ │ ├── DerivationNotSupportedException.php │ │ │ ├── FatalErrorException.php │ │ │ ├── FileException.php │ │ │ ├── FileNotFoundException.php │ │ │ ├── LocalTemporaryFileException.php │ │ │ ├── NullFileOperationException.php │ │ │ └── TemporaryFileException.php │ │ ├── FileException.php │ │ ├── FileRepository │ │ │ ├── AdHocFilesystemException.php │ │ │ └── FileRepositoryException.php │ │ └── MetadataNotFoundException.php │ │ ├── FileInterface.php │ │ ├── FileMetadataInterface.php │ │ ├── FileNameInterface.php │ │ ├── FilePointerInterface.php │ │ ├── FileProxy.php │ │ ├── FileRepositoryInterface.php │ │ ├── FileTypeInterface.php │ │ ├── NodeInterface.php │ │ ├── NullFileInterface.php │ │ ├── NullFilePointerInterface.php │ │ ├── RawMetadataInterface.php │ │ └── Trait │ │ ├── EqualityTrait.php │ │ ├── FileDecoratorTrait.php │ │ └── MetadataTrait.php ├── file-derivation │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ └── Filter │ │ ├── AbstractFileFilter.php │ │ └── FileFilterInterface.php ├── file-filepond │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── assets │ │ ├── .gitignore │ │ ├── babel.config.js │ │ ├── dist │ │ │ └── filepond.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ │ └── filepond.js │ ├── composer.json │ ├── src │ │ ├── DependencyInjection │ │ │ └── RekalogikaFileFilePondExtension.php │ │ ├── FilePondCollectionType.php │ │ ├── FilePondFileEncodeAdapter.php │ │ ├── FilePondType.php │ │ └── RekalogikaFileFilePondBundle.php │ └── templates │ │ └── filepond_form_theme.html.twig ├── file-image │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── ImageResizer.php │ │ ├── ImageTwigExtension.php │ │ └── ImageTwigRuntime.php ├── file-metadata-contracts │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── HttpMetadataInterface.php │ │ └── ImageMetadataInterface.php ├── file-metadata │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── Constants.php │ │ ├── Metadata │ │ ├── AbstractMetadata.php │ │ ├── FileMetadata.php │ │ ├── HttpMetadata.php │ │ └── ImageMetadata.php │ │ ├── MetadataFactory.php │ │ └── Model │ │ ├── FileName.php │ │ ├── MimeMapFileTypeAdapter.php │ │ ├── TranslatableFileName.php │ │ └── TranslatableMessage.php ├── file-null │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── InvalidFile.php │ │ ├── NullFile.php │ │ ├── NullFileTrait.php │ │ ├── NullName.php │ │ ├── NullPointer.php │ │ ├── NullType.php │ │ └── TranslatableMessage.php ├── file-oneup-uploader-bridge │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ └── FromOneUpUploaderFileAdapter.php ├── file-server │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ └── FileInterfaceResourceServer.php ├── file-symfony-bridge │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── Constraints │ │ ├── File.php │ │ ├── FileValidator.php │ │ ├── Image.php │ │ └── ImageValidator.php │ │ ├── Form │ │ ├── FileTransformer.php │ │ ├── FileType.php │ │ └── FileTypeExtension.php │ │ └── HttpFoundation │ │ ├── FileResponse.php │ │ ├── FromHttpFoundationFileAdapter.php │ │ └── ToHttpFoundationFileAdapter.php ├── file-zip │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ │ ├── DirectoryResourceServer.php │ │ ├── FileZip.php │ │ ├── Model │ │ ├── Directory.php │ │ └── FilePointer.php │ │ └── ZipDirectory.php └── file │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── composer.json │ └── src │ ├── Adapter │ └── FromSplFileInfoAdapter.php │ ├── Contracts │ ├── FilesystemRepositoryInterface.php │ ├── MetadataAwareFilesystemOperator.php │ ├── MetadataAwareFilesystemReader.php │ └── MetadataAwareFilesystemWriter.php │ ├── Directory.php │ ├── Exception │ └── FilesystemRepository │ │ ├── FilesystemAlreadyExistsException.php │ │ ├── FilesystemNotFoundException.php │ │ └── FilesystemRepositoryException.php │ ├── File.php │ ├── FileAdapter.php │ ├── FileFactory.php │ ├── FilePointer.php │ ├── Filesystem │ ├── LocalFilesystemDecorator.php │ └── RemoteFilesystemDecorator.php │ ├── LocalTemporaryFile.php │ ├── MetadataGenerator │ ├── MetadataGenerator.php │ └── MetadataGeneratorInterface.php │ ├── MetadataSerializer │ ├── MetadataSerializer.php │ └── MetadataSerializerInterface.php │ ├── RawMetadata.php │ ├── Repository │ ├── FileRepository.php │ ├── FilesystemRepository.php │ └── LocalFilesystemAdapter.php │ └── TemporaryFile.php ├── phpstan.neon.dist ├── phpunit.xml.dist ├── psalm.xml ├── rector.php └── tests ├── assets ├── app.js ├── bootstrap.js ├── controllers.json ├── controllers │ ├── hello_controller.js │ └── rekalogika │ │ └── file-filepond │ │ └── filepond.js └── styles │ └── app.css ├── bin └── console ├── config ├── packages │ ├── asset_mapper.yaml │ ├── debug.yaml │ ├── doctrine.yaml │ ├── flysystem.yaml │ ├── framework.yaml │ ├── monolog.yaml │ ├── rekalogika_file.yaml │ ├── routing.yaml │ ├── translation.yaml │ ├── twig.yaml │ └── web_profiler.yaml ├── routes │ ├── test.yaml │ └── web_profiler.yaml └── services.php ├── importmap.php ├── public ├── .htaccess └── index.php ├── src ├── App │ ├── Controller │ │ └── TestController.php │ └── Entity │ │ ├── DummyEntity.php │ │ └── User.php ├── Factory.php ├── TestKernel.php └── Tests │ ├── Architecture │ └── Architecture.php │ ├── File │ ├── FileFactory.php │ ├── FileNameTest.php │ ├── FileRepositoryTest.php │ ├── FileTest.php │ ├── FileTestTrait.php │ ├── FileTypeTest.php │ ├── FilesystemRepositoryTest.php │ ├── FromSplFileInfoAdapterTest.php │ ├── MetadataSerializerTest.php │ ├── MetadataTest.php │ └── ReferenceTest.php │ ├── FileAssociation │ ├── ClassMetadataFactoryTest.php │ ├── ClassSignatureTest.php │ ├── CommandTest.php │ ├── DefaultFileLocationResolverTest.php │ ├── DefaultObjectIdResolverTest.php │ ├── DoctrineTestCase.php │ ├── EntityTest.php │ ├── ObjectManagerTest.php │ ├── PropertyListerTest.php │ ├── PropertyRecorderTest.php │ └── ProxyUtilTest.php │ ├── FileAssociationEntity │ ├── AbstractFileTest.php │ └── EmbeddedMetadataTest.php │ ├── FileContracts │ └── FileProxyTest.php │ ├── FileImage │ ├── Fixtures │ │ └── image_resize.test │ ├── ImageResizerTest.php │ ├── TwigTest.php │ └── TwigTestExtension.php │ ├── FileNull │ └── NullFileTest.php │ ├── FileServer │ └── TemporaryUrlTest.php │ ├── FileSymfonyBridge │ ├── FileAdapterTest.php │ ├── FileExtensionTest.php │ ├── FileResponseTest.php │ ├── FileTransformerTest.php │ ├── FileTypeTest.php │ ├── FileValidatorTest.php │ └── ImageValidatorTest.php │ ├── FileZip │ └── ZipTest.php │ ├── Model │ ├── Entity.php │ ├── EntityExtendingAbstractFile.php │ ├── EntityImplementingFileAssociation.php │ ├── EntityWithAnyId.php │ ├── EntityWithAttribute.php │ ├── EntityWithDifferentFileProperties.php │ ├── EntityWithEmbeddedMetadata.php │ ├── EntityWithFileProxyUtilGetterSetter.php │ ├── EntityWithLazyFile.php │ ├── EntityWithMandatoryFile.php │ ├── EntityWithOverridenSignature.php │ ├── EntityWithPlainGetterSetter.php │ ├── EntityWithoutId.php │ ├── SubclassOfEntityImplementingFileAssociation.php │ └── SubclassOfEntityWithAttribute.php │ └── Resources │ ├── corrupt.jpg │ ├── legacyImageMetadata.json │ ├── legacyMetadata.json │ ├── localFile.txt │ └── smiley.png └── templates ├── base.html.twig └── index.html.twig /.djlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "max_blank_lines": 2, 3 | "custom_blocks": "embed", 4 | "max_line_length": 80 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; top-most EditorConfig file 2 | root = true 3 | 4 | ; Unix-style newlines 5 | [*] 6 | charset = utf-8 7 | end_of_line = LF 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{php,html,twig}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.md] 16 | max_line_length = 80 17 | 18 | [COMMIT_EDITMSG] 19 | max_line_length = 0 -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | APP_ENV=test 2 | PHP=php 3 | SYMFONY=symfony 4 | COMPOSER=composer 5 | 6 | # use .env.local to override, example: 7 | 8 | # PHP with xdebug: 9 | # PHP=php -dxdebug.start_with_request=yes -dxdebug.mode=debug 10 | 11 | # PHP using docker and xdebug: 12 | #PHP=docker run -it --rm --user $$(id -u):$$(id -g) -v "$$PWD":/usr/src/myapp -w /usr/src/myapp php:8.4.0beta5-cli php -dxdebug.start_with_request=yes -dxdebug.mode=debug 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "composer" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | - package-ecosystem: "npm" 12 | directory: "/packages/file-filepond/assets" 13 | schedule: 14 | interval: "weekly" 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | .phpunit.cache 4 | tools 5 | .php-cs-fixer.cache 6 | var 7 | rector.log 8 | .env.local 9 | .vscode 10 | -------------------------------------------------------------------------------- /.phive/phars.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__ . '/packages') 5 | ->in(__DIR__ . '/tests/src') 6 | 7 | ; 8 | 9 | $config = new PhpCsFixer\Config(); 10 | return $config->setRules([ 11 | '@PER-CS2.0' => true, 12 | '@PER-CS2.0:risky' => true, 13 | 'fully_qualified_strict_types' => [ 14 | 'import_symbols' => true, 15 | ], 16 | 'global_namespace_import' => [ 17 | 'import_classes' => false, 18 | 'import_constants' => false, 19 | 'import_functions' => false, 20 | ], 21 | 'no_unneeded_import_alias' => true, 22 | 'no_unused_imports' => true, 23 | 'ordered_imports' => [ 24 | 'sort_algorithm' => 'alpha', 25 | 'imports_order' => ['class', 'function', 'const'] 26 | ], 27 | 'declare_strict_types' => true, 28 | 'native_function_invocation' => ['include' => ['@compiler_optimized']], 29 | 'header_comment' => [ 30 | 'header' => << 34 | 35 | For the full copyright and license information, please view the LICENSE file 36 | that was distributed with this source code. 37 | EOF, 38 | ] 39 | ]) 40 | ->setFinder($finder) 41 | ; 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-src 2 | 3 | Monorepo for `rekalogika/file` and related packages. 4 | 5 | ## Documentation 6 | 7 | * [rekalogika.dev/file](https://rekalogika.dev/file). 8 | * [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle). 9 | -------------------------------------------------------------------------------- /monorepo-builder.php: -------------------------------------------------------------------------------- 1 | packageDirectories([__DIR__ . '/packages']); 17 | $mbConfig->defaultBranch('main'); 18 | $mbConfig->disableDefaultWorkers(); 19 | 20 | $mbConfig->workers([ 21 | UpdateReplaceReleaseWorker::class, 22 | SetCurrentMutualDependenciesReleaseWorker::class, 23 | // AddTagToChangelogReleaseWorker::class, 24 | // TagVersionReleaseWorker::class, 25 | // PushTagReleaseWorker::class, 26 | // SetNextMutualDependenciesReleaseWorker::class, 27 | UpdateBranchAliasReleaseWorker::class, 28 | // PushNextDevReleaseWorker::class, 29 | ]); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/file-association-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-association-contracts/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-association-contracts/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-association-contracts 2 | 3 | Interface for file association. To be implemented in entities that have file 4 | association. 5 | 6 | ## Documentation 7 | 8 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 9 | 10 | ## License 11 | 12 | MIT 13 | 14 | ## Contributing 15 | 16 | The `rekalogika/file-association-contracts` repository is a read-only repo split 17 | from the main repo. Issues and pull requests should be submitted to the 18 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 19 | -------------------------------------------------------------------------------- /packages/file-association-contracts/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-association-contracts", 3 | "description": "Interface for file association. To be implemented in entities that support file association.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "type": "library", 6 | "license": "MIT", 7 | "keywords": [ 8 | "file", 9 | "association", 10 | "upload", 11 | "file upload" 12 | ], 13 | "authors": [ 14 | { 15 | "name": "Priyadi Iman Nurcahyo", 16 | "email": "priyadi@rekalogika.com" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.2" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Rekalogika\\Contracts\\File\\Association\\": "src/" 25 | } 26 | }, 27 | "extra": { 28 | "branch-alias": { 29 | "dev-main": "2.3-dev" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/file-association-contracts/src/FileAssociationInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Association; 15 | 16 | interface FileAssociationInterface 17 | { 18 | /** 19 | * Determines the properties that accepts file 20 | * 21 | * @return array 22 | */ 23 | public static function getFileAssociationPropertyList(): array; 24 | } 25 | -------------------------------------------------------------------------------- /packages/file-association-entity/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-association-entity/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-association-entity/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-association-entity 2 | 3 | Utilities for handling files inside domain entities: 4 | 5 | * `EmbeddedMetadata` for embedding metadata inside Doctrine entities 6 | * `AbstractFile` for creating one-to-many relations with files 7 | * `NullFile` for handling cases in domain entities where a file must be present 8 | but is missing in the storage backend. 9 | ## Documentation 10 | 11 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 12 | 13 | ## License 14 | 15 | MIT 16 | 17 | ## Contributing 18 | 19 | The `rekalogika/file-association-entity` repository is a read-only repo split 20 | from the main repo. Issues and pull requests should be submitted to the 21 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 22 | -------------------------------------------------------------------------------- /packages/file-association-entity/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-association-entity", 3 | "description": "Utilities for handling files inside domain entities: EmbeddedMetadata for embedding metadata inside Doctrine entities, AbstractFile for creating one-to-many relations with files, and NullFile to handle cases in domain entities where a file must be present but is missing in the storage backend.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "type": "library", 6 | "license": "MIT", 7 | "keywords": [ 8 | "file", 9 | "association", 10 | "upload", 11 | "entity", 12 | "attachment", 13 | "doctrine", 14 | "file upload", 15 | "symfony" 16 | ], 17 | "authors": [ 18 | { 19 | "name": "Priyadi Iman Nurcahyo", 20 | "email": "priyadi@rekalogika.com" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "Rekalogika\\Domain\\File\\Association\\Entity\\": "src/" 26 | } 27 | }, 28 | "require": { 29 | "php": "^8.2", 30 | "doctrine/collections": "^2.0", 31 | "psr/http-message": "^1.0 || ^2.0", 32 | "rekalogika/file-contracts": "^2.2.2", 33 | "rekalogika/file-metadata": "^2.2.2", 34 | "rekalogika/file-null": "^2.2.2", 35 | "rekalogika/doctrine-collections-decorator": "^2.0", 36 | "symfony/translation-contracts": "^3.0" 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-main": "2.3-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/file-association-entity/config/doctrine/AbstractFile.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /packages/file-association-entity/config/doctrine/EmbeddedMetadata.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 14 | 18 | 22 | 27 | 31 | 35 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /packages/file-association-entity/src/AbstractFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Association\Entity; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Association\Attribute\WithFileAssociation; 18 | 19 | /** 20 | * Easily create a Doctrine entity that implements FileInterface. 21 | */ 22 | #[WithFileAssociation] 23 | abstract class AbstractFile implements FileInterface 24 | { 25 | use FileTrait; 26 | 27 | public function __construct( 28 | FileInterface $file, 29 | ) { 30 | $this->setWrapped($file); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/file-association/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-association/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-association/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-association 2 | 3 | Handles the association between Doctrine entities and files using the 4 | rekalogika/file framework, including from file uploads. 5 | 6 | ## Features 7 | 8 | * Requires only a single property in the entity for each associated file. 9 | * File properties are file properties. It is not necessary to store any of the 10 | file's properties in the entity associated with the file. 11 | * DX improvement, less micro-management of entity-file relations. 12 | * Reads and writes directly into the file properties, even if private. You are 13 | free to have business logic in the getters and setters. 14 | * Doesn't require you to update another property of the entity (`lastUpdated`?) 15 | just to make sure the correct Doctrine events will be fired. 16 | 17 | ## Documentation 18 | 19 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 20 | 21 | ## License 22 | 23 | MIT 24 | 25 | ## Contributing 26 | 27 | The `rekalogika/file-association` repository is a read-only repo split from the 28 | main repo. Issues and pull requests should be submitted to the 29 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 30 | -------------------------------------------------------------------------------- /packages/file-association/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-association", 3 | "description": "Handles the association between Doctrine entities and files using the rekalogika/file framework, including from file uploads.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "type": "library", 6 | "license": "MIT", 7 | "keywords": [ 8 | "file", 9 | "association", 10 | "upload", 11 | "entity", 12 | "attachment", 13 | "doctrine", 14 | "file upload", 15 | "symfony" 16 | ], 17 | "authors": [ 18 | { 19 | "name": "Priyadi Iman Nurcahyo", 20 | "email": "priyadi@rekalogika.com" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "Rekalogika\\File\\Association\\": "src/" 26 | } 27 | }, 28 | "require": { 29 | "php": "^8.2", 30 | "psr/cache": "^2 || ^3", 31 | "psr/log": "^2 || ^3", 32 | "psr/http-message": "^1.0 || ^2.0", 33 | "rekalogika/file-association-contracts": "^2.2.2", 34 | "rekalogika/file-contracts": "^2.2.2", 35 | "rekalogika/file-null": "^2.2.2", 36 | "rekalogika/reconstitutor": "^2.2", 37 | "symfony/deprecation-contracts": "^3.1", 38 | "symfony/service-contracts": "^3.1" 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-main": "2.3-dev" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/file-association/src/Attribute/AsFileAssociation.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Attribute; 15 | 16 | use Rekalogika\File\Association\Model\FetchMode; 17 | 18 | #[\Attribute(\Attribute::TARGET_PROPERTY)] 19 | final readonly class AsFileAssociation 20 | { 21 | public function __construct( 22 | public FetchMode $fetch = FetchMode::Eager, 23 | ) {} 24 | } 25 | -------------------------------------------------------------------------------- /packages/file-association/src/Attribute/WithFileAssociation.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Attribute; 15 | 16 | use Rekalogika\File\Association\Exception\InvalidClassSignatureException; 17 | 18 | #[\Attribute(\Attribute::TARGET_CLASS)] 19 | final class WithFileAssociation 20 | { 21 | public function __construct( 22 | private readonly ?string $signature = null, 23 | ) { 24 | // if not null, ensure $classSignature contains only alphanumeric characters only 25 | 26 | if ( 27 | $signature !== null 28 | && !preg_match('/^[a-zA-Z0-9_]+$/', $signature) 29 | ) { 30 | throw new InvalidClassSignatureException(\sprintf( 31 | 'Class signature must contain only alphanumeric characters and underscores, "%s" given', 32 | $signature, 33 | )); 34 | } 35 | } 36 | 37 | public function getSignature(): ?string 38 | { 39 | return $this->signature; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/file-association/src/ClassMetadataFactory/CachingClassMetadataFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\ClassMetadataFactory; 15 | 16 | use Psr\Cache\CacheItemPoolInterface; 17 | use Rekalogika\File\Association\Contracts\ClassMetadataFactoryInterface; 18 | use Rekalogika\File\Association\Model\ClassMetadata; 19 | 20 | final readonly class CachingClassMetadataFactory implements ClassMetadataFactoryInterface 21 | { 22 | public function __construct( 23 | private ClassMetadataFactoryInterface $classMetadataFactory, 24 | private CacheItemPoolInterface $cache, 25 | ) {} 26 | 27 | #[\Override] 28 | public function getClassMetadata(string $class): ClassMetadata 29 | { 30 | $cacheKey = hash('xxh128', $class); 31 | $cacheItem = $this->cache->getItem($cacheKey); 32 | 33 | if ($cacheItem->isHit()) { 34 | /** @psalm-suppress MixedAssignment */ 35 | $result = $cacheItem->get(); 36 | 37 | if ($result instanceof ClassMetadata) { 38 | return $result; 39 | } 40 | 41 | $this->cache->deleteItem($cacheKey); 42 | } 43 | 44 | $result = $this->classMetadataFactory->getClassMetadata($class); 45 | 46 | $cacheItem->set($result); 47 | $this->cache->save($cacheItem); 48 | 49 | return $result; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/file-association/src/ClassSignatureResolver/AttributeClassSignatureResolver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\ClassSignatureResolver; 15 | 16 | use Rekalogika\File\Association\Attribute\WithFileAssociation; 17 | use Rekalogika\File\Association\Contracts\ClassSignatureResolverInterface; 18 | 19 | final readonly class AttributeClassSignatureResolver implements ClassSignatureResolverInterface 20 | { 21 | #[\Override] 22 | public function getClassSignature(string $class): ?string 23 | { 24 | $reflectionClass = new \ReflectionClass($class); 25 | 26 | $attribute = $reflectionClass 27 | ->getAttributes(WithFileAssociation::class)[0] ?? null; 28 | 29 | if ($attribute === null) { 30 | return null; 31 | } 32 | 33 | $attributeInstance = $attribute->newInstance(); 34 | 35 | return $attributeInstance->getSignature(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/file-association/src/ClassSignatureResolver/ChainClassSignatureResolver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\ClassSignatureResolver; 15 | 16 | use Rekalogika\File\Association\Contracts\ClassSignatureResolverInterface; 17 | 18 | final readonly class ChainClassSignatureResolver implements ClassSignatureResolverInterface 19 | { 20 | /** 21 | * @param iterable $resolvers 22 | */ 23 | public function __construct( 24 | private iterable $resolvers, 25 | ) {} 26 | 27 | #[\Override] 28 | public function getClassSignature(string $class): string 29 | { 30 | foreach ($this->resolvers as $resolver) { 31 | $signature = $resolver->getClassSignature($class); 32 | 33 | if ($signature !== null) { 34 | return $signature; 35 | } 36 | } 37 | 38 | throw new \LogicException( 39 | \sprintf('No class signature resolver found for class "%s"', $class), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/file-association/src/ClassSignatureResolver/DefaultClassSignatureResolver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\ClassSignatureResolver; 15 | 16 | use Rekalogika\File\Association\Contracts\ClassSignatureResolverInterface; 17 | 18 | final readonly class DefaultClassSignatureResolver implements ClassSignatureResolverInterface 19 | { 20 | #[\Override] 21 | public function getClassSignature(string $class): string 22 | { 23 | return sha1($class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/file-association/src/ClassSignatureResolver/MetadataClassSignatureResolver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\ClassSignatureResolver; 15 | 16 | use Rekalogika\File\Association\Contracts\ClassMetadataFactoryInterface; 17 | use Rekalogika\File\Association\Contracts\ClassSignatureResolverInterface; 18 | 19 | /** 20 | * Gets the class signature already saved in the metadata 21 | */ 22 | final readonly class MetadataClassSignatureResolver implements ClassSignatureResolverInterface 23 | { 24 | public function __construct( 25 | private ClassMetadataFactoryInterface $classMetadataFactory, 26 | ) {} 27 | 28 | #[\Override] 29 | public function getClassSignature(string $class): string 30 | { 31 | return $this->classMetadataFactory 32 | ->getClassMetadata($class) 33 | ->getSignature(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/ClassBasedFileLocationResolverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\Contracts\File\FilePointerInterface; 17 | use Rekalogika\File\Association\Exception\FileLocationResolver\FileLocationResolverException; 18 | 19 | /** 20 | * Determines where a file is stored depending on the class, identifier, and 21 | * property name. 22 | */ 23 | interface ClassBasedFileLocationResolverInterface 24 | { 25 | /** 26 | * @param class-string $class 27 | * @throws FileLocationResolverException 28 | */ 29 | public function getFileLocation( 30 | string $class, 31 | string $id, 32 | string $propertyName, 33 | ): FilePointerInterface; 34 | } 35 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/ClassMetadataFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\File\Association\Model\ClassMetadata; 17 | 18 | /** 19 | * Gets the metadata for a class. 20 | */ 21 | interface ClassMetadataFactoryInterface 22 | { 23 | /** 24 | * Gets the metadata for a class. 25 | * 26 | * @param class-string $class 27 | */ 28 | public function getClassMetadata(string $class): ClassMetadata; 29 | } 30 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/ClassSignatureResolverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | interface ClassSignatureResolverInterface 17 | { 18 | /** 19 | * Takes a class name and returns a string that uniquely identifies the 20 | * class. The string will be used as a part of the location of a file. The 21 | * result must consist of alphanumeric characters only. Because the location 22 | * of a file is potentially part of a public URL, the result should not 23 | * reveal internal information, like the class name itself. 24 | * 25 | * @param class-string $class 26 | */ 27 | public function getClassSignature(string $class): ?string; 28 | } 29 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/FilePropertyManagerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\File\Association\Model\PropertyMetadata; 17 | use Rekalogika\File\Association\Model\PropertyOperationResult; 18 | 19 | /** 20 | * @internal 21 | */ 22 | interface FilePropertyManagerInterface 23 | { 24 | /** 25 | * Process a potential incoming file upload on a property 26 | */ 27 | public function flushProperty( 28 | PropertyMetadata $propertyMetadata, 29 | object $object, 30 | string $id, 31 | ): PropertyOperationResult; 32 | 33 | /** 34 | * Process a file removal on a property 35 | */ 36 | public function removeProperty( 37 | PropertyMetadata $propertyMetadata, 38 | object $object, 39 | string $id, 40 | ): PropertyOperationResult; 41 | 42 | /** 43 | * Process a property on an object load 44 | */ 45 | public function loadProperty( 46 | PropertyMetadata $propertyMetadata, 47 | object $object, 48 | string $id, 49 | ): PropertyOperationResult; 50 | } 51 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/ObjectIdResolverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\File\Association\Exception\ObjectIdResolver\ObjectIdResolverException; 17 | 18 | /** 19 | * Determines the unique identifier of an object 20 | */ 21 | interface ObjectIdResolverInterface 22 | { 23 | /** 24 | * @throws ObjectIdResolverException 25 | */ 26 | public function getObjectId(object $object): string; 27 | } 28 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/ObjectManagerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\File\Association\Model\ObjectOperationResult; 17 | 18 | /** 19 | * @internal 20 | */ 21 | interface ObjectManagerInterface 22 | { 23 | /** 24 | * Called when the object is saved 25 | */ 26 | public function flushObject(object $object): ObjectOperationResult; 27 | 28 | /** 29 | * Called when the object is removed 30 | */ 31 | public function removeObject(object $object): ObjectOperationResult; 32 | 33 | /** 34 | * Called when the object is loaded 35 | */ 36 | public function loadObject(object $object): ObjectOperationResult; 37 | } 38 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/PropertyListerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\File\Association\Model\Property; 17 | 18 | /** 19 | * Lists all the properties of an object that are considered file association. 20 | * 21 | * @internal 22 | */ 23 | interface PropertyListerInterface 24 | { 25 | /** 26 | * Returns the property names of the object that are file association. 27 | * 28 | * @param class-string $class 29 | * @return iterable Property names of the object that are file association. 30 | * 31 | */ 32 | public function getFileProperties(string $class): iterable; 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/PropertyReaderInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Association\Model\PropertyMetadata; 18 | 19 | /** 20 | * Reads a property of an object. 21 | * 22 | * @internal 23 | */ 24 | interface PropertyReaderInterface 25 | { 26 | public function read( 27 | object $object, 28 | PropertyMetadata $propertyMetadata, 29 | ): ?FileInterface; 30 | } 31 | -------------------------------------------------------------------------------- /packages/file-association/src/Contracts/PropertyWriterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Contracts; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Association\Model\PropertyMetadata; 18 | 19 | /** 20 | * Writes a valuo to a property of an object.. 21 | * 22 | * @internal 23 | */ 24 | interface PropertyWriterInterface 25 | { 26 | public function write( 27 | object $object, 28 | PropertyMetadata $propertyMetadata, 29 | ?FileInterface $value, 30 | ): void; 31 | } 32 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/DuplicatePropertyNameException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception; 15 | 16 | final class DuplicatePropertyNameException extends LogicException 17 | { 18 | /** 19 | * @param string $propertyName 20 | * @param class-string $class1 21 | * @param class-string $class2 22 | * @param string $leafClass 23 | */ 24 | public function __construct( 25 | string $propertyName, 26 | string $class1, 27 | string $class2, 28 | string $leafClass, 29 | ) { 30 | parent::__construct( 31 | \sprintf( 32 | 'Invalid class signature for property "%s" in class "%s" and "%s". Leaf class: "%s"', 33 | $propertyName, 34 | $class1, 35 | $class2, 36 | $leafClass, 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/FileAssociationException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception; 15 | 16 | interface FileAssociationException extends \Throwable {} 17 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/FileLocationResolver/ChainedClassNotSupportedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\FileLocationResolver; 15 | 16 | final class ChainedClassNotSupportedException extends \RuntimeException implements FileLocationResolverException 17 | { 18 | /** 19 | * @param class-string $class 20 | * @param iterable $exceptions 21 | */ 22 | public function __construct( 23 | string $class, 24 | private readonly iterable $exceptions, 25 | ?\Throwable $previous = null, 26 | ) { 27 | parent::__construct( 28 | \sprintf( 29 | 'Cannot find a resolver that supports class "%s"', 30 | $class, 31 | ), 32 | 0, 33 | $previous, 34 | ); 35 | } 36 | 37 | /** 38 | * @return iterable 39 | */ 40 | public function getExceptions(): iterable 41 | { 42 | return $this->exceptions; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/FileLocationResolver/FileLocationResolverException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\FileLocationResolver; 15 | 16 | use Rekalogika\File\Association\Exception\FileAssociationException; 17 | 18 | interface FileLocationResolverException extends FileAssociationException {} 19 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/FileLocationResolver/InvalidProxyException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\FileLocationResolver; 15 | 16 | use Rekalogika\File\Association\Exception\FileAssociationException; 17 | 18 | final class InvalidProxyException extends \LogicException implements FileAssociationException {} 19 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/FileLocationResolver/ObjectNotSupportedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\FileLocationResolver; 15 | 16 | use Rekalogika\File\Association\Contracts\ClassBasedFileLocationResolverInterface; 17 | 18 | final class ObjectNotSupportedException extends \RuntimeException implements FileLocationResolverException 19 | { 20 | /** 21 | * @param class-string $class 22 | */ 23 | public function __construct( 24 | string $class, 25 | object $object, 26 | string $message, 27 | ?\Throwable $previous = null, 28 | ) { 29 | parent::__construct( 30 | \sprintf( 31 | 'File location resolver "%s" does not support object "%s": %s', 32 | $class, 33 | $object::class, 34 | $message, 35 | ), 36 | 0, 37 | $previous, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/InvalidClassSignatureException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception; 15 | 16 | final class InvalidClassSignatureException extends LogicException {} 17 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/LogicException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception; 15 | 16 | class LogicException extends \LogicException implements FileAssociationException {} 17 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/ObjectIdResolver/ChainedObjectIdResolverException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\ObjectIdResolver; 15 | 16 | final class ChainedObjectIdResolverException extends \LogicException implements ObjectIdResolverException 17 | { 18 | /** 19 | * @param iterable $exceptions 20 | */ 21 | public function __construct( 22 | object $object, 23 | private readonly iterable $exceptions, 24 | ?\Throwable $previous = null, 25 | ) { 26 | parent::__construct( 27 | \sprintf( 28 | 'None of the object ID resolvers registered in the system supports the object "%s"', 29 | $object::class, 30 | ), 31 | 0, 32 | $previous, 33 | ); 34 | } 35 | 36 | /** 37 | * @return iterable 38 | */ 39 | public function getExceptions(): iterable 40 | { 41 | return $this->exceptions; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/ObjectIdResolver/EmptyIdException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\ObjectIdResolver; 15 | 16 | final class EmptyIdException extends \RuntimeException implements ObjectIdResolverException 17 | { 18 | public function __construct( 19 | object $object, 20 | string $method, 21 | ?\Throwable $previous = null, 22 | ) { 23 | parent::__construct( 24 | \sprintf( 25 | "Method '%s' in object '%s' returned an empty id", 26 | $method, 27 | $object::class, 28 | ), 29 | 0, 30 | $previous, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/ObjectIdResolver/IdNotSupportedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\ObjectIdResolver; 15 | 16 | final class IdNotSupportedException extends \LogicException implements ObjectIdResolverException 17 | { 18 | public function __construct( 19 | object $object, 20 | string $method, 21 | mixed $id, 22 | ?\Throwable $previous = null, 23 | ) { 24 | parent::__construct( 25 | \sprintf( 26 | 'Method "%s" of object "%s" returned an unsupported identifier "%s"', 27 | $method, 28 | $object::class, 29 | get_debug_type($id), 30 | ), 31 | 0, 32 | $previous, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/ObjectIdResolver/MethodNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\ObjectIdResolver; 15 | 16 | final class MethodNotFoundException extends \RuntimeException implements ObjectIdResolverException 17 | { 18 | public function __construct( 19 | object $object, 20 | string $method, 21 | ?\Throwable $previous = null, 22 | ) { 23 | parent::__construct( 24 | \sprintf( 25 | "Method '%s' not found in object '%s'", 26 | $method, 27 | $object::class, 28 | ), 29 | 0, 30 | $previous, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/ObjectIdResolver/ObjectIdResolverException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\ObjectIdResolver; 15 | 16 | use Rekalogika\File\Association\Exception\FileAssociationException; 17 | 18 | interface ObjectIdResolverException extends FileAssociationException {} 19 | -------------------------------------------------------------------------------- /packages/file-association/src/Exception/ObjectIdResolver/ObjectNotSupportedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Exception\ObjectIdResolver; 15 | 16 | final class ObjectNotSupportedException extends \LogicException implements ObjectIdResolverException 17 | { 18 | public function __construct( 19 | object $object, 20 | ?\Throwable $previous = null, 21 | ) { 22 | parent::__construct( 23 | \sprintf( 24 | 'Object "%s" is not supported.', 25 | $object::class, 26 | ), 27 | 0, 28 | $previous, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/FetchMode.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | enum FetchMode 17 | { 18 | case Eager; 19 | case Lazy; 20 | } 21 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/FilePointer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | use Rekalogika\Contracts\File\FilePointerInterface; 17 | use Rekalogika\Contracts\File\Trait\EqualityTrait; 18 | 19 | /** 20 | * An implementation of FilePointerInterface. We don't use the one in the 21 | * rekalogika/file package so that this package does not have to depend on that. 22 | */ 23 | final class FilePointer implements FilePointerInterface 24 | { 25 | use EqualityTrait; 26 | 27 | public function __construct( 28 | private ?string $filesystemIdentifier, 29 | private string $key, 30 | ) {} 31 | 32 | #[\Override] 33 | public function getFilesystemIdentifier(): ?string 34 | { 35 | return $this->filesystemIdentifier; 36 | } 37 | 38 | #[\Override] 39 | public function getKey(): string 40 | { 41 | return $this->key; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/ObjectOperationResult.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | final readonly class ObjectOperationResult 17 | { 18 | /** 19 | * @param class-string $class 20 | * @param list $propertyResults 21 | */ 22 | public function __construct( 23 | public ObjectOperationType $type, 24 | public string $class, 25 | public string $objectId, 26 | public array $propertyResults, 27 | public ?float $duration = null, 28 | ) {} 29 | 30 | public function withDuration(float $duration): self 31 | { 32 | return new self( 33 | type: $this->type, 34 | class: $this->class, 35 | objectId: $this->objectId, 36 | propertyResults: $this->propertyResults, 37 | duration: $duration, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/ObjectOperationType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | /** 17 | * @internal 18 | */ 19 | enum ObjectOperationType 20 | { 21 | case Flush; 22 | 23 | case Load; 24 | 25 | case Remove; 26 | 27 | public function getString(): string 28 | { 29 | return match ($this) { 30 | self::Flush => 'Flush', 31 | self::Load => 'Load', 32 | self::Remove => 'Remove', 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/Property.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | final readonly class Property 17 | { 18 | private string $signature; 19 | 20 | /** 21 | * @param class-string $class 22 | */ 23 | public function __construct( 24 | private string $class, 25 | private string $name, 26 | ) { 27 | $this->signature = \sprintf('%s::%s', $class, $name); 28 | } 29 | 30 | public function getName(): string 31 | { 32 | return $this->name; 33 | } 34 | 35 | /** 36 | * @return class-string 37 | */ 38 | public function getClass(): string 39 | { 40 | return $this->class; 41 | } 42 | 43 | public function getSignature(): string 44 | { 45 | return $this->signature; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/PropertyMetadata.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | /** 17 | * The result of a property inspection. 18 | */ 19 | final readonly class PropertyMetadata 20 | { 21 | /** 22 | * @param class-string $class 23 | * @param class-string $scopeClass 24 | */ 25 | public function __construct( 26 | private string $class, 27 | private string $scopeClass, 28 | private string $name, 29 | private bool $mandatory, 30 | private FetchMode $fetch, 31 | ) {} 32 | 33 | public function getName(): string 34 | { 35 | return $this->name; 36 | } 37 | 38 | /** 39 | * @return class-string 40 | */ 41 | public function getClass(): string 42 | { 43 | return $this->class; 44 | } 45 | 46 | /** 47 | * @return class-string 48 | */ 49 | public function getScopeClass(): string 50 | { 51 | return $this->scopeClass; 52 | } 53 | 54 | public function isMandatory(): bool 55 | { 56 | return $this->mandatory; 57 | } 58 | 59 | public function getFetch(): FetchMode 60 | { 61 | return $this->fetch; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/PropertyOperationAction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | /** 17 | * @internal 18 | */ 19 | enum PropertyOperationAction 20 | { 21 | case Nothing; 22 | case Saved; 23 | case Removed; 24 | case LoadedNormal; 25 | case LoadedLazy; 26 | case LoadedMissing; 27 | case LoadedNotFound; 28 | 29 | public function getString(): string 30 | { 31 | return match ($this) { 32 | self::Nothing => 'Nothing', 33 | self::Saved => 'Saved', 34 | self::Removed => 'Removed', 35 | self::LoadedNormal => 'Loaded Normal', 36 | self::LoadedLazy => 'Loaded Lazy', 37 | self::LoadedMissing => 'Loaded Missing', 38 | self::LoadedNotFound => 'Loaded Not Found', 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/file-association/src/Model/PropertyOperationResult.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Model; 15 | 16 | use Rekalogika\Contracts\File\FilePointerInterface; 17 | 18 | final readonly class PropertyOperationResult 19 | { 20 | /** 21 | * @param class-string $class 22 | * @param class-string $scopeClass 23 | */ 24 | public function __construct( 25 | public ObjectOperationType $type, 26 | public PropertyOperationAction $action, 27 | public string $class, 28 | public string $scopeClass, 29 | public string $property, 30 | public string $objectId, 31 | public FilePointerInterface $filePointer, 32 | public ?float $duration = null, 33 | ) {} 34 | 35 | public function withDuration(float $duration): self 36 | { 37 | return new self( 38 | type: $this->type, 39 | action: $this->action, 40 | class: $this->class, 41 | scopeClass: $this->scopeClass, 42 | property: $this->property, 43 | objectId: $this->objectId, 44 | filePointer: $this->filePointer, 45 | duration: $duration, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/file-association/src/ObjectIdResolver/DefaultObjectIdResolver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\ObjectIdResolver; 15 | 16 | use Rekalogika\File\Association\Contracts\ObjectIdResolverInterface; 17 | use Rekalogika\File\Association\Exception\ObjectIdResolver\EmptyIdException; 18 | use Rekalogika\File\Association\Exception\ObjectIdResolver\IdNotSupportedException; 19 | use Rekalogika\File\Association\Exception\ObjectIdResolver\MethodNotFoundException; 20 | 21 | final readonly class DefaultObjectIdResolver implements ObjectIdResolverInterface 22 | { 23 | public function __construct( 24 | private string $method = 'getId', 25 | ) {} 26 | 27 | #[\Override] 28 | public function getObjectId(object $object): string 29 | { 30 | if (method_exists($object, $this->method)) { 31 | /** @var mixed */ 32 | $id = $object->{$this->method}(); 33 | } else { 34 | throw new MethodNotFoundException($object, $this->method); 35 | } 36 | 37 | if (!\is_string($id) && !\is_int($id) && !$id instanceof \Stringable) { 38 | throw new IdNotSupportedException($object, $this->method, $id); 39 | } 40 | 41 | $id = (string) $id; 42 | 43 | if ($id === '') { 44 | throw new EmptyIdException($object, $this->method); 45 | } 46 | 47 | return $id; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/file-association/src/PropertyLister/ChainPropertyLister.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\PropertyLister; 15 | 16 | use Rekalogika\File\Association\Contracts\PropertyListerInterface; 17 | 18 | /** 19 | * Chains multiple property listers 20 | */ 21 | final readonly class ChainPropertyLister implements PropertyListerInterface 22 | { 23 | /** 24 | * @param iterable $propertyListers 25 | */ 26 | public function __construct( 27 | private iterable $propertyListers, 28 | ) {} 29 | 30 | #[\Override] 31 | public function getFileProperties(string $class): iterable 32 | { 33 | $properties = []; 34 | 35 | foreach ($this->propertyListers as $propertyLister) { 36 | $newProperties = $propertyLister->getFileProperties($class); 37 | 38 | $newProperties = \is_array($newProperties) 39 | ? $newProperties 40 | : iterator_to_array($newProperties); 41 | 42 | foreach ($newProperties as $property) { 43 | $properties[$property->getSignature()] = $property; 44 | } 45 | } 46 | 47 | yield from $properties; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/file-association/src/Util/FileLocationUtil.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Association\Util; 15 | 16 | final readonly class FileLocationUtil 17 | { 18 | private function __construct() {} 19 | 20 | public static function createHashedDirectory( 21 | string $id, 22 | int $hashLevel, 23 | ): string { 24 | return implode( 25 | '/', 26 | \array_slice( 27 | str_split(sha1($id), 2), 28 | 0, 29 | $hashLevel, 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-bundle/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-bundle/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-bundle/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-bundle 2 | 3 | Integrates the rekalogika/file library into the Symfony framework. 4 | 5 | ## Documentation 6 | 7 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 8 | 9 | ## License 10 | 11 | MIT 12 | 13 | ## Contributing 14 | 15 | The `rekalogika/file-bundle` repository is a read-only repo split from the main 16 | repo. Issues and pull requests should be submitted to the 17 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 18 | -------------------------------------------------------------------------------- /packages/file-bundle/config/file-bundle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | use League\Flysystem\FilesystemOperator; 15 | use Rekalogika\File\Association\Contracts\ClassBasedFileLocationResolverInterface; 16 | use Rekalogika\File\Bundle\Command\FileLocationResolverCommand; 17 | use Rekalogika\File\Bundle\DefaultFilesystemFactory; 18 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 19 | 20 | use function Symfony\Component\DependencyInjection\Loader\Configurator\service; 21 | 22 | return static function (ContainerConfigurator $containerConfigurator): void { 23 | $services = $containerConfigurator->services(); 24 | 25 | // 26 | // filesystem 27 | // 28 | 29 | $services 30 | ->set(DefaultFilesystemFactory::class) 31 | ; 32 | 33 | $services 34 | ->set('rekalogika.file.default_filesystem') 35 | ->class(FilesystemOperator::class) 36 | ->factory([ 37 | service(DefaultFilesystemFactory::class), 38 | 'getDefaultFilesystem', 39 | ]) 40 | ; 41 | 42 | 43 | // 44 | // commands 45 | // 46 | 47 | $services 48 | ->set('rekalogika.file.command.file_location_resolver') 49 | ->class(FileLocationResolverCommand::class) 50 | ->args([ 51 | '$fileLocationResolver' => service(ClassBasedFileLocationResolverInterface::class), 52 | ]) 53 | ->tag('console.command') 54 | ; 55 | }; 56 | -------------------------------------------------------------------------------- /packages/file-bundle/config/file-image.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | use Rekalogika\File\Image\ImageResizer; 15 | use Rekalogika\File\Image\ImageTwigExtension; 16 | use Rekalogika\File\Image\ImageTwigRuntime; 17 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 18 | 19 | use function Symfony\Component\DependencyInjection\Loader\Configurator\service; 20 | 21 | return static function (ContainerConfigurator $containerConfigurator): void { 22 | $services = $containerConfigurator->services(); 23 | 24 | $services 25 | ->set(ImageResizer::class) 26 | ->tag('rekalogika.file.derivation.filter') 27 | ; 28 | 29 | $services 30 | ->set('rekalogika.file.image.twig_extension') 31 | ->class(ImageTwigExtension::class) 32 | ->tag('twig.extension', [ 33 | 'priority' => 1000000, 34 | ]) 35 | ; 36 | 37 | $services 38 | ->set('rekalogika.file.image.twig_runtime') 39 | ->class(ImageTwigRuntime::class) 40 | ->args([ 41 | service(ImageResizer::class), 42 | ]) 43 | ->tag('twig.runtime', [ 44 | 'priority' => 1000000, 45 | ]) 46 | ; 47 | }; 48 | -------------------------------------------------------------------------------- /packages/file-bundle/config/file-server.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | use Rekalogika\Contracts\File\FileRepositoryInterface; 15 | use Rekalogika\File\Server\FileInterfaceResourceServer; 16 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 17 | 18 | use function Symfony\Component\DependencyInjection\Loader\Configurator\service; 19 | 20 | return static function (ContainerConfigurator $containerConfigurator): void { 21 | $services = $containerConfigurator->services(); 22 | 23 | $services 24 | ->set('rekalogika.file.server.file_interface_resource_server') 25 | ->class(FileInterfaceResourceServer::class) 26 | ->tag('rekalogika.temporary_url.resource_server', [ 27 | 'method' => 'respond', 28 | ]) 29 | ->tag('rekalogika.temporary_url.resource_transformer', [ 30 | 'method' => 'transform', 31 | ]) 32 | ->args([ 33 | service(FileRepositoryInterface::class), 34 | ]); 35 | }; 36 | -------------------------------------------------------------------------------- /packages/file-bundle/config/file.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | use Rekalogika\Contracts\File\FileRepositoryInterface; 15 | use Rekalogika\File\FileFactory; 16 | use Rekalogika\File\Repository\FileRepository; 17 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 18 | 19 | use function Symfony\Component\DependencyInjection\Loader\Configurator\service; 20 | 21 | return static function (ContainerConfigurator $containerConfigurator): void { 22 | $services = $containerConfigurator->services(); 23 | 24 | $services 25 | ->set('rekalogika.file.factory') 26 | ->class(FileFactory::class) 27 | ; 28 | 29 | $services 30 | ->set(FileRepositoryInterface::class) 31 | ->class(FileRepository::class) 32 | ->factory([ 33 | service('rekalogika.file.factory'), 34 | 'getFileRepository', 35 | ]) 36 | ->tag('kernel.reset', ['method' => 'reset']) 37 | ; 38 | }; 39 | -------------------------------------------------------------------------------- /packages/file-bundle/config/rekalogika_file.yaml: -------------------------------------------------------------------------------- 1 | rekalogika_file: 2 | # all the filesystems registered in FileRepository 3 | filesystems: 4 | # our default filesystem 5 | default: rekalogika.file.default_filesystem 6 | 7 | # # the default storage if using Flysystem Bundle 8 | # default: '@default.storage' 9 | 10 | default_filesystem_directory: '%kernel.project_dir%/var/storage/default' 11 | -------------------------------------------------------------------------------- /packages/file-bundle/config/tests.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | use League\Flysystem\FilesystemOperator; 15 | use Rekalogika\File\Tests\Factory; 16 | use Rekalogika\File\Tests\TestKernel; 17 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 18 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 19 | 20 | return static function (ContainerConfigurator $containerConfigurator): void { 21 | $services = $containerConfigurator->services(); 22 | 23 | // mock urlgenerator 24 | $services->set(UrlGeneratorInterface::class) 25 | ->factory([Factory::class, 'createUrlGenerator']); 26 | 27 | // add test aliases 28 | $serviceIds = TestKernel::getServiceIds(); 29 | 30 | // foreach ($serviceIds as $serviceId) { 31 | // $services->set($serviceId)->public(); 32 | // } 33 | 34 | // filesystem for testing 35 | $services->set('test.filesystem', FilesystemOperator::class) 36 | ->factory([Factory::class, 'createTestFilesystem']) 37 | ->tag('rekalogika.file.filesystem', ['identifier' => 'default']) 38 | ->public(); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/file-bundle/src/DefaultFilesystemFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bundle; 15 | 16 | use League\Flysystem\Filesystem; 17 | use League\Flysystem\FilesystemOperator; 18 | use League\Flysystem\Local\LocalFilesystemAdapter; 19 | 20 | /** 21 | * Provides a good default, ready-to-use filesystem post-installation without 22 | * any dependency except on Flysystem. 23 | */ 24 | final class DefaultFilesystemFactory 25 | { 26 | private ?FilesystemOperator $filesystem = null; 27 | 28 | public function __construct(private readonly string $directory) {} 29 | 30 | public function getDefaultFilesystem(): FilesystemOperator 31 | { 32 | if ($this->filesystem !== null) { 33 | return $this->filesystem; 34 | } 35 | 36 | $adapter = new LocalFilesystemAdapter($this->directory); 37 | 38 | return $this->filesystem = new Filesystem($adapter); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/file-bundle/src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bundle\DependencyInjection; 15 | 16 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 17 | use Symfony\Component\Config\Definition\ConfigurationInterface; 18 | 19 | final class Configuration implements ConfigurationInterface 20 | { 21 | #[\Override] 22 | public function getConfigTreeBuilder(): TreeBuilder 23 | { 24 | $treeBuilder = new TreeBuilder('rekalogika_file'); 25 | $rootNode = $treeBuilder->getRootNode(); 26 | 27 | $rootNode 28 | ->children() 29 | ->arrayNode('filesystems') 30 | ->info('Filesystems registered in FileRepository in a key-value pairs. The key is the identifier of the filesystem, and the value is the filesystem instance.') 31 | ->requiresAtLeastOneElement() 32 | ->useAttributeAsKey('identifier') 33 | ->scalarPrototype()->end() 34 | ->end() 35 | 36 | ->scalarNode('default_filesystem_directory') 37 | ->info('The storage directory used by the default filesystem.') 38 | ->defaultValue('%kernel.project_dir%/var/storage/default') 39 | ->end() 40 | ->end(); 41 | 42 | return $treeBuilder; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/file-bundle/src/RekalogikaFileBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bundle; 15 | 16 | use Rekalogika\File\Bundle\DependencyInjection\FilterPass; 17 | use Rekalogika\File\Derivation\Filter\FileFilterInterface; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\HttpKernel\Bundle\Bundle; 20 | 21 | final class RekalogikaFileBundle extends Bundle 22 | { 23 | #[\Override] 24 | public function getPath(): string 25 | { 26 | return \dirname(__DIR__); 27 | } 28 | 29 | #[\Override] 30 | public function build(ContainerBuilder $container): void 31 | { 32 | parent::build($container); 33 | 34 | if (interface_exists(FileFilterInterface::class)) { 35 | $container->addCompilerPass(new FilterPass()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/file-bundle/templates/file.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/file-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-contracts/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-contracts/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-contracts 2 | 3 | Interfaces, traits, and nominal classes for file abstraction. 4 | 5 | ## Documentation 6 | 7 | [rekalogika.dev/file](https://rekalogika.dev/file) 8 | 9 | ## License 10 | 11 | MIT 12 | 13 | ## Contributing 14 | 15 | The `rekalogika/file-contracts` repository is a read-only repo split from the 16 | main repo. Issues and pull requests should be submitted to the 17 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 18 | -------------------------------------------------------------------------------- /packages/file-contracts/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-contracts", 3 | "description": "Interfaces, traits, and nominal classes for file abstraction.", 4 | "homepage": "https://rekalogika.dev/file", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "filesystem", 10 | "stream", 11 | "storage", 12 | "repository", 13 | "upload", 14 | "file upload", 15 | "entity", 16 | "attachment" 17 | ], 18 | "authors": [ 19 | { 20 | "name": "Priyadi Iman Nurcahyo", 21 | "email": "priyadi@rekalogika.com" 22 | } 23 | ], 24 | "autoload": { 25 | "psr-4": { 26 | "Rekalogika\\Contracts\\File\\": "src/" 27 | } 28 | }, 29 | "require": { 30 | "php": "^8.2", 31 | "psr/http-message": "^1.0 || ^2.0", 32 | "symfony/translation-contracts": "^3.0" 33 | }, 34 | "extra": { 35 | "branch-alias": { 36 | "dev-main": "2.3-dev" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/file-contracts/src/DirectoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | /** 17 | * Represents a directory, which is a collection of files, file pointers, and 18 | * other directories. 19 | * 20 | * @template TKey of array-key 21 | * @template T of NodeInterface 22 | * @extends \Traversable 23 | */ 24 | interface DirectoryInterface extends 25 | NodeInterface, 26 | \Traversable, 27 | \Countable 28 | { 29 | /** 30 | * The name for this set of files, will be used as the name of the 31 | * downloaded folder or archive file, etc. 32 | */ 33 | public function getName(): FileNameInterface; 34 | } 35 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/DerivationNotSupportedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | final class DerivationNotSupportedException extends \RuntimeException implements FileException {} 17 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/FatalErrorException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | final class FatalErrorException extends \LogicException implements FileException {} 17 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/FileException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | use Rekalogika\Contracts\File\Exception\FileException as ExceptionFileException; 17 | 18 | interface FileException extends ExceptionFileException {} 19 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | final class FileNotFoundException extends \RuntimeException implements FileException 17 | { 18 | public function __construct( 19 | string $key, 20 | ?string $filesystemId = null, 21 | ?\Throwable $previous = null, 22 | ) { 23 | if ($filesystemId !== null) { 24 | parent::__construct( 25 | \sprintf( 26 | 'File "%s" in filesystem "%s" does not exist.', 27 | $key, 28 | $filesystemId, 29 | ), 30 | 0, 31 | $previous, 32 | ); 33 | } else { 34 | parent::__construct( 35 | \sprintf( 36 | 'File "%s" does not exist.', 37 | $key, 38 | ), 39 | 0, 40 | $previous, 41 | ); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/LocalTemporaryFileException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | final class LocalTemporaryFileException extends \RuntimeException implements FileException {} 17 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/NullFileOperationException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | /** 17 | * A null file should not cause any side effect. Therefore, any operation 18 | * involving a null file that will potentially cause a side effect should throw 19 | * this exception. 20 | */ 21 | final class NullFileOperationException extends \RuntimeException implements FileException {} 22 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/File/TemporaryFileException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\File; 15 | 16 | final class TemporaryFileException extends \RuntimeException implements FileException 17 | { 18 | public function __construct( 19 | string $prefix, 20 | ?string $filesystemId = null, 21 | ?\Throwable $previous = null, 22 | ) { 23 | if ($filesystemId !== null) { 24 | parent::__construct( 25 | \sprintf( 26 | 'Cannot create a temporary file with prefix "%s" in filesystem "%s"', 27 | $prefix, 28 | $filesystemId, 29 | ), 30 | 0, 31 | $previous, 32 | ); 33 | } else { 34 | parent::__construct( 35 | \sprintf( 36 | 'Cannot create a temporary file with prefix "%s" in the local filesystem.', 37 | $prefix, 38 | ), 39 | 0, 40 | $previous, 41 | ); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/FileException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception; 15 | 16 | interface FileException extends \Throwable {} 17 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/FileRepository/AdHocFilesystemException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\FileRepository; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | 18 | final class AdHocFilesystemException extends \RuntimeException implements FileRepositoryException 19 | { 20 | public function __construct( 21 | FileInterface $file, 22 | ?\Throwable $previous = null, 23 | ) { 24 | parent::__construct(\sprintf( 25 | 'File with key "%s" has an ad-hoc filesystem "%s", but the function you are using in the file repository cannot work with such a file.', 26 | $file->getKey(), 27 | $file->getFilesystemIdentifier() ?? 'null', 28 | ), 0, $previous); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/FileRepository/FileRepositoryException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception\FileRepository; 15 | 16 | use Rekalogika\Contracts\File\Exception\FileException; 17 | 18 | interface FileRepositoryException extends FileException {} 19 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Exception/MetadataNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Exception; 15 | 16 | final class MetadataNotFoundException extends \RuntimeException implements FileException 17 | { 18 | public function __construct(string $metadataName, ?\Throwable $previous = null) 19 | { 20 | parent::__construct( 21 | \sprintf('Metadata "%s" is not found', $metadataName), 22 | 0, 23 | $previous, 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/file-contracts/src/FileMetadataInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | /** 17 | * Represent metadata that is applicable to all files. 18 | */ 19 | interface FileMetadataInterface 20 | { 21 | /** 22 | * Gets the file name. 23 | */ 24 | public function getName(): FileNameInterface; 25 | 26 | /** 27 | * Sets the file name. 28 | */ 29 | public function setName(?string $fileName): void; 30 | 31 | /** 32 | * Gets the file size. 33 | * 34 | * @return int<0,max> 35 | */ 36 | public function getSize(): int; 37 | 38 | /** 39 | * Gets the file media type (MIME type). 40 | */ 41 | public function getType(): FileTypeInterface; 42 | 43 | /** 44 | * Sets the file media type (MIME type). 45 | */ 46 | public function setType(string $type): void; 47 | 48 | /** 49 | * Gets the file modification time. 50 | */ 51 | public function getModificationTime(): \DateTimeInterface; 52 | } 53 | -------------------------------------------------------------------------------- /packages/file-contracts/src/FileNameInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | use Symfony\Contracts\Translation\TranslatableInterface; 17 | 18 | /** 19 | * Represents a file name 20 | */ 21 | interface FileNameInterface extends \Stringable, TranslatableInterface 22 | { 23 | /** 24 | * The full filename, with extension. 25 | */ 26 | public function getFull(): \Stringable&TranslatableInterface; 27 | 28 | /** 29 | * Set the full filename, with extension. 30 | */ 31 | public function setFull(string $name): void; 32 | 33 | /** 34 | * The base of the filename, without extension. 35 | */ 36 | public function getBase(): \Stringable&TranslatableInterface; 37 | 38 | /** 39 | * Set the base of the filename, without extension. 40 | */ 41 | public function setBase(string $name): void; 42 | 43 | /** 44 | * The extension of the filename, without the dot, in lower case 45 | */ 46 | public function getExtension(): ?string; 47 | 48 | /** 49 | * Set the extension of the filename, without the dot. 50 | */ 51 | public function setExtension(?string $extension): void; 52 | 53 | /** 54 | * Whether the filename has an extension. 55 | */ 56 | public function hasExtension(): bool; 57 | } 58 | -------------------------------------------------------------------------------- /packages/file-contracts/src/FilePointerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | /** 17 | * Pointer to a file 18 | */ 19 | interface FilePointerInterface extends NodeInterface 20 | { 21 | /** 22 | * Identifies the filesystem that the file is stored on. Null means that 23 | * the file is accessed directly on the local filesystem. 24 | */ 25 | public function getFilesystemIdentifier(): ?string; 26 | 27 | /** 28 | * The key that identifies the object. Usually in the form of a file path. 29 | * It is the access key to the file and unique across the same filesystem. 30 | * It does not need to have a filename. 31 | */ 32 | public function getKey(): string; 33 | 34 | /** 35 | * Determines if the pointer is equal to another pointer. 36 | */ 37 | public function isEqualTo(self|FileInterface $other): bool; 38 | 39 | /** 40 | * Determines if the pointer is on the same filesystem as the other pointer. 41 | */ 42 | public function isSameFilesystem(self|FileInterface $other): bool; 43 | } 44 | -------------------------------------------------------------------------------- /packages/file-contracts/src/FileTypeInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | use Symfony\Contracts\Translation\TranslatableInterface; 17 | 18 | /** 19 | * Represents a MIME content-type 20 | */ 21 | interface FileTypeInterface extends \Stringable 22 | { 23 | /** 24 | * The MIME type identifier for the type, like 'image/jpeg'. 25 | */ 26 | public function getName(): string; 27 | 28 | /** 29 | * The MIME media type for the type, like 'image'. 30 | */ 31 | public function getType(): string; 32 | 33 | /** 34 | * The MIME subtype for the type, like 'jpeg'. 35 | */ 36 | public function getSubType(): string; 37 | 38 | /** 39 | * Gets the list of common file extensions for the type, in lowercase, 40 | * without the leading dot. 41 | * 42 | * @return array 43 | */ 44 | public function getCommonExtensions(): array; 45 | 46 | /** 47 | * Gets the best file extension for the type, in lowercase, without the 48 | * leading dot. 49 | */ 50 | public function getExtension(): ?string; 51 | 52 | /** 53 | * Description of the type 54 | */ 55 | public function getDescription(): \Stringable&TranslatableInterface; 56 | } 57 | -------------------------------------------------------------------------------- /packages/file-contracts/src/NodeInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | /** 17 | * Represents a file, a file pointer, or directory 18 | */ 19 | interface NodeInterface {} 20 | -------------------------------------------------------------------------------- /packages/file-contracts/src/NullFileInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | /** 17 | * Marker interface for a null file object. 18 | */ 19 | interface NullFileInterface extends FileInterface {} 20 | -------------------------------------------------------------------------------- /packages/file-contracts/src/NullFilePointerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | /** 17 | * Marker interface for a null file pointer object. 18 | */ 19 | interface NullFilePointerInterface extends FilePointerInterface {} 20 | -------------------------------------------------------------------------------- /packages/file-contracts/src/RawMetadataInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File; 15 | 16 | use Rekalogika\Contracts\File\Exception\MetadataNotFoundException; 17 | 18 | /** 19 | * Represent the low-level metadata structure of a file. 20 | * 21 | * @extends \Traversable 22 | */ 23 | interface RawMetadataInterface extends \Traversable, \Countable 24 | { 25 | /** 26 | * Gets a specific metadata. 27 | * 28 | * @throws MetadataNotFoundException 29 | */ 30 | public function get(string $key): int|string|bool|null; 31 | 32 | /** 33 | * Gets a specific metadata. Returns null if the metadata is not found. 34 | */ 35 | public function tryGet(string $key): int|string|bool|null; 36 | 37 | /** 38 | * Sets the specified metdata 39 | */ 40 | public function set(string $key, int|string|bool|null $value): void; 41 | 42 | /** 43 | * Deletes the specified metadata 44 | */ 45 | public function delete(string $key): void; 46 | 47 | /** 48 | * Merges the given list of metadata into the current one. 49 | * 50 | * @param iterable $metadata 51 | */ 52 | public function merge(iterable $metadata): void; 53 | } 54 | -------------------------------------------------------------------------------- /packages/file-contracts/src/Trait/EqualityTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Trait; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\Contracts\File\FilePointerInterface; 18 | use Rekalogika\Contracts\File\NullFileInterface; 19 | use Rekalogika\Contracts\File\NullFilePointerInterface; 20 | 21 | trait EqualityTrait 22 | { 23 | abstract public function getFilesystemIdentifier(): ?string; 24 | 25 | abstract public function getKey(): string; 26 | 27 | public function isEqualTo(FilePointerInterface|FileInterface $other): bool 28 | { 29 | return !$other instanceof NullFileInterface 30 | && !$other instanceof NullFilePointerInterface 31 | && $this->isSameFilesystem($other) 32 | && $this->getKey() === $other->getKey(); 33 | } 34 | 35 | public function isSameFilesystem(FilePointerInterface|FileInterface $other): bool 36 | { 37 | return !$other instanceof NullFileInterface 38 | && !$other instanceof NullFilePointerInterface 39 | && $this->getFilesystemIdentifier() === $other->getFilesystemIdentifier(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/file-derivation/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-derivation/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-derivation/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-derivation 2 | 3 | Provides an abstract class to streamline the creation of filters using the file 4 | derivation feature of the rekalogika/file framework. 5 | 6 | ## Documentation 7 | 8 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 9 | 10 | ## License 11 | 12 | MIT 13 | 14 | ## Contributing 15 | 16 | The `rekalogika/file-derivation` repository is a read-only repo split from the 17 | main repo. Issues and pull requests should be submitted to the 18 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 19 | -------------------------------------------------------------------------------- /packages/file-derivation/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-derivation", 3 | "description": "Provides an abstract class to streamline the creation of filters using the file derivation feature of the rekalogika/file framework.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "type": "library", 6 | "license": "MIT", 7 | "keywords": [ 8 | "file", 9 | "derivation", 10 | "filter" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Priyadi Iman Nurcahyo", 15 | "email": "priyadi@rekalogika.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Rekalogika\\File\\Derivation\\": "src/" 21 | } 22 | }, 23 | "require": { 24 | "php": "^8.2", 25 | "rekalogika/file-contracts": "^2.2.2", 26 | "rekalogika/file": "^2.2.2" 27 | }, 28 | "extra": { 29 | "branch-alias": { 30 | "dev-main": "2.3-dev" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-derivation/src/Filter/FileFilterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Derivation\Filter; 15 | 16 | use Rekalogika\Contracts\File\FileRepositoryInterface; 17 | 18 | interface FileFilterInterface 19 | { 20 | public function setFileRepository( 21 | FileRepositoryInterface $fileRepository, 22 | ): void; 23 | } 24 | -------------------------------------------------------------------------------- /packages/file-filepond/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-filepond/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-filepond/assets/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /packages/file-filepond/assets/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', { 4 | "loose": true, 5 | "modules": false 6 | }] 7 | ], 8 | assumptions: { 9 | superIsCallableConstructor: false, 10 | }, 11 | }; -------------------------------------------------------------------------------- /packages/file-filepond/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-filepond", 3 | "description": "Upload files using Symfony Form, FilePond, and the rekalogika/file framework", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "license": "MIT", 6 | "type": "symfony-bundle", 7 | "authors": [ 8 | { 9 | "name": "Priyadi Iman Nurcahyo", 10 | "email": "priyadi@rekalogika.com" 11 | } 12 | ], 13 | "keywords": [ 14 | "file", 15 | "filepond", 16 | "file-upload", 17 | "form", 18 | "symfony-ux", 19 | "symfony form", 20 | "javascript" 21 | ], 22 | "autoload": { 23 | "psr-4": { 24 | "Rekalogika\\File\\Bridge\\FilePond\\": "src/" 25 | } 26 | }, 27 | "require": { 28 | "php": "^8.2", 29 | "rekalogika/file": "^2.2.2", 30 | "rekalogika/file-association-entity": "^2.2.2", 31 | "rekalogika/file-bundle": "^2.2.2", 32 | "rekalogika/file-contracts": "^2.2.2", 33 | "rekalogika/file-image": "^2.2.2", 34 | "rekalogika/file-server": "^2.2.2", 35 | "rekalogika/file-symfony-bridge": "^2.2.2", 36 | "rekalogika/temporary-url-bundle": "^1.7.2", 37 | "symfony/dependency-injection": "^6.2 || ^7.0", 38 | "symfony/form": "^6.2 || ^7.0", 39 | "symfony/http-foundation": "^6.2 || ^7.0", 40 | "symfony/http-kernel": "^6.2 || ^7.0", 41 | "symfony/options-resolver": "^6.2 || ^7.0", 42 | "symfony/stimulus-bundle": "^2.12" 43 | }, 44 | "extra": { 45 | "branch-alias": { 46 | "dev-main": "2.3-dev" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/file-filepond/src/RekalogikaFileFilePondBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\FilePond; 15 | 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | use Symfony\Component\HttpKernel\Bundle\Bundle; 18 | 19 | final class RekalogikaFileFilePondBundle extends Bundle 20 | { 21 | #[\Override] 22 | public function getPath(): string 23 | { 24 | return \dirname(__DIR__); 25 | } 26 | 27 | #[\Override] 28 | public function build(ContainerBuilder $container): void 29 | { 30 | parent::build($container); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/file-filepond/templates/filepond_form_theme.html.twig: -------------------------------------------------------------------------------- 1 | {% block rekalogika_file_filepond_widget %} 2 |
3 | {% if data %} 4 | 12 | {% endif %} 13 | {{ block('form_widget_simple') }} 14 |
15 | {% endblock %} 16 | 17 | {% block rekalogika_file_filepond_collection_widget %} 18 |
19 | {% for id, entry in data %} 20 | 29 | {% endfor %} 30 | {{ block('form_widget_simple') }} 31 |
32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /packages/file-image/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-image/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-image/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-image 2 | 3 | Simple image resizing filter for rekalogika/file. Callers provide the maximum 4 | width or height of the desired result, and whether to square-crop it. 5 | 6 | ## Documentation 7 | 8 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 9 | 10 | ## License 11 | 12 | MIT 13 | 14 | ## Contributing 15 | 16 | The `rekalogika/file-image` repository is a read-only repo split from the main 17 | repo. Issues and pull requests should be submitted to the 18 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 19 | -------------------------------------------------------------------------------- /packages/file-image/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-image", 3 | "description": "Simple image resizing filter for rekalogika/file. Callers provide the maximum width or height of the desired result, and whether to square-crop it.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "image", 10 | "resize", 11 | "filter", 12 | "thumbnail", 13 | "jpeg", 14 | "png", 15 | "intervention" 16 | ], 17 | "authors": [ 18 | { 19 | "name": "Priyadi Iman Nurcahyo", 20 | "email": "priyadi@rekalogika.com" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "Rekalogika\\File\\Image\\": "src/" 26 | } 27 | }, 28 | "require": { 29 | "php": "^8.2", 30 | "intervention/image": "^3.3", 31 | "psr/log": "^2 || ^3", 32 | "rekalogika/file-contracts": "^2.2.2", 33 | "rekalogika/file-derivation": "^2.2.2", 34 | "twig/twig": "^3.9" 35 | }, 36 | "extra": { 37 | "branch-alias": { 38 | "dev-main": "2.3-dev" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/file-image/src/ImageTwigExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Image; 15 | 16 | use Twig\Extension\AbstractExtension; 17 | use Twig\TwigFilter; 18 | 19 | final class ImageTwigExtension extends AbstractExtension 20 | { 21 | #[\Override] 22 | public function getFilters(): array 23 | { 24 | return [ 25 | new TwigFilter( 26 | 'image_resize', 27 | [ImageTwigRuntime::class, 'fileImageResize'], 28 | ), 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/file-image/src/ImageTwigRuntime.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Image; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Twig\Extension\RuntimeExtensionInterface; 18 | 19 | final class ImageTwigRuntime implements RuntimeExtensionInterface 20 | { 21 | public function __construct( 22 | private ImageResizer $imageResizer, 23 | ) {} 24 | 25 | /** 26 | * @param ImageResizer::ASPECTRATIO_* $aspect 27 | */ 28 | public function fileImageResize( 29 | ?FileInterface $file, 30 | int $maxWidthOrHeight = 512, 31 | string $aspect = ImageResizer::ASPECTRATIO_ORIGINAL, 32 | ): ?FileInterface { 33 | if ($file === null) { 34 | return null; 35 | } 36 | 37 | return $this->imageResizer 38 | ->take($file) 39 | ->resize($maxWidthOrHeight, $aspect) 40 | ->getResult(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/file-metadata-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-metadata-contracts/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-metadata-contracts/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-metadata-contracts 2 | 3 | Additional abstractions of metadata types for rekalogika/file. 4 | 5 | ## Documentation 6 | 7 | [rekalogika.dev/file](https://rekalogika.dev/file) 8 | 9 | ## License 10 | 11 | MIT 12 | 13 | ## Contributing 14 | 15 | The `rekalogika/file-metadata-contracts` repository is a read-only repo split 16 | from the main repo. Issues and pull requests should be submitted to the 17 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 18 | -------------------------------------------------------------------------------- /packages/file-metadata-contracts/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-metadata-contracts", 3 | "description": "Additional abstractions of metadata types for rekalogika/file.", 4 | "homepage": "https://rekalogika.dev/file", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "image", 10 | "metadata" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Priyadi Iman Nurcahyo", 15 | "email": "priyadi@rekalogika.com" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.2" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Rekalogika\\Contracts\\File\\Metadata\\": "src/" 24 | } 25 | }, 26 | "extra": { 27 | "branch-alias": { 28 | "dev-main": "2.3-dev" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/file-metadata-contracts/src/HttpMetadataInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Metadata; 15 | 16 | /** 17 | * Represent HTTP metadata of a file. 18 | */ 19 | interface HttpMetadataInterface 20 | { 21 | /** 22 | * Returns all HTTP headers. 23 | * 24 | * @return iterable 25 | */ 26 | public function getHeaders(?string $disposition = null): iterable; 27 | 28 | /** 29 | * Gets the Cache-Control header 30 | */ 31 | public function getCacheControl(): ?string; 32 | 33 | /** 34 | * Sets the Cache-Control header 35 | */ 36 | public function setCacheControl(?string $cacheControl): void; 37 | 38 | /** 39 | * Gets the disposition. Will be used in Content-Disposition header. 40 | */ 41 | public function getDisposition(): string; 42 | 43 | /** 44 | * Sets the disposition. Will be used in Content-Disposition header. 45 | */ 46 | public function setDisposition(string $disposition): void; 47 | } 48 | -------------------------------------------------------------------------------- /packages/file-metadata-contracts/src/ImageMetadataInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Contracts\File\Metadata; 15 | 16 | /** 17 | * Represent metadata of an image file. 18 | */ 19 | interface ImageMetadataInterface 20 | { 21 | public function getWidth(): int; 22 | 23 | public function getHeight(): int; 24 | 25 | public function getAspectRatio(): float; 26 | 27 | public function isAspectRatio(float $aspectRatio): bool; 28 | 29 | public function isLandscape(): bool; 30 | 31 | public function isPortrait(): bool; 32 | 33 | public function isSquare(): bool; 34 | } 35 | -------------------------------------------------------------------------------- /packages/file-metadata/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-metadata/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-metadata/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-metadata 2 | 3 | Metadata library for rekalogika/file. 4 | 5 | ## Documentation 6 | 7 | [rekalogika.dev/file](https://rekalogika.dev/file) 8 | 9 | ## License 10 | 11 | MIT 12 | 13 | ## Contributing 14 | 15 | The `rekalogika/file-metadata` repository is a read-only repo split from the 16 | main repo. Issues and pull requests should be submitted to the 17 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 18 | -------------------------------------------------------------------------------- /packages/file-metadata/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-metadata", 3 | "description": "Metadata library for rekalogika/file", 4 | "homepage": "https://rekalogika.dev/file", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "image", 10 | "metadata", 11 | "http", 12 | "http-headers" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Priyadi Iman Nurcahyo", 17 | "email": "priyadi@rekalogika.com" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-4": { 22 | "Rekalogika\\Domain\\File\\Metadata\\": "src/" 23 | } 24 | }, 25 | "require": { 26 | "php": "^8.2", 27 | "cardinalby/content-disposition": "^1.1", 28 | "fileeye/mimemap": "^2.0", 29 | "rekalogika/file-contracts": "^2.2.2", 30 | "rekalogika/file-metadata-contracts": "^2.2.2", 31 | "symfony/translation-contracts": "^3.0" 32 | }, 33 | "extra": { 34 | "branch-alias": { 35 | "dev-main": "2.3-dev" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/file-metadata/src/Constants.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Metadata; 15 | 16 | /** 17 | * List of all the supported metadata tags 18 | * 19 | * @internal 20 | */ 21 | final class Constants 22 | { 23 | public const HTTP_CACHE_CONTROL = 'http.cacheControl'; 24 | 25 | public const HTTP_DISPOSITION = 'http._disposition'; 26 | 27 | public const HTTP_ETAG = 'http.eTag'; 28 | 29 | public const FILE_NAME = 'file.name'; 30 | 31 | public const FILE_SIZE = 'file.size'; 32 | 33 | public const FILE_TYPE = 'file.type'; 34 | 35 | public const FILE_MODIFICATION_TIME = 'file.modificationTime'; 36 | 37 | public const MEDIA_WIDTH = 'media.width'; 38 | 39 | public const MEDIA_HEIGHT = 'media.height'; 40 | } 41 | -------------------------------------------------------------------------------- /packages/file-metadata/src/Metadata/AbstractMetadata.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Metadata\Metadata; 15 | 16 | use Rekalogika\Contracts\File\RawMetadataInterface; 17 | 18 | abstract class AbstractMetadata 19 | { 20 | abstract public static function create( 21 | RawMetadataInterface $metadata, 22 | ): ?static; 23 | } 24 | -------------------------------------------------------------------------------- /packages/file-metadata/src/Model/TranslatableMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Metadata\Model; 15 | 16 | use Symfony\Contracts\Translation\TranslatableInterface; 17 | use Symfony\Contracts\Translation\TranslatorInterface; 18 | 19 | /** 20 | * Translatable string for file name 21 | */ 22 | final readonly class TranslatableMessage implements TranslatableInterface, \Stringable 23 | { 24 | /** 25 | * @param array $parameters 26 | */ 27 | public function __construct( 28 | private string $stringName, 29 | private string $translationId, 30 | private array $parameters = [], 31 | ) {} 32 | 33 | #[\Override] 34 | public function __toString(): string 35 | { 36 | return $this->stringName; 37 | } 38 | 39 | #[\Override] 40 | public function trans( 41 | TranslatorInterface $translator, 42 | ?string $locale = null, 43 | ): string { 44 | return $translator->trans( 45 | $this->translationId, 46 | $this->parameters, 47 | 'rekalogika_file', 48 | $locale, 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/file-null/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-null/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-null/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-null 2 | 3 | Null object pattern implementation for rekalogika/file framework. 4 | 5 | ## Documentation 6 | 7 | [rekalogika.dev/file](https://rekalogika.dev/file) 8 | 9 | ## License 10 | 11 | MIT 12 | 13 | ## Contributing 14 | 15 | The `rekalogika/file-null` repository is a read-only repo split from the main 16 | repo. Issues and pull requests should be submitted to the 17 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 18 | -------------------------------------------------------------------------------- /packages/file-null/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-null", 3 | "description": "Null object pattern implementation for rekalogika/file framework.", 4 | "homepage": "https://rekalogika.dev/file", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "null-object", 10 | "nullfile", 11 | "null-file", 12 | "null", 13 | "null-value" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Priyadi Iman Nurcahyo", 18 | "email": "priyadi@rekalogika.com" 19 | } 20 | ], 21 | "autoload": { 22 | "psr-4": { 23 | "Rekalogika\\Domain\\File\\Null\\": "src/" 24 | } 25 | }, 26 | "require": { 27 | "php": "^8.2", 28 | "psr/http-message": "^1.0 || ^2.0", 29 | "rekalogika/file-contracts": "^2.2.2", 30 | "symfony/translation-contracts": "^3.0" 31 | }, 32 | "extra": { 33 | "branch-alias": { 34 | "dev-main": "2.3-dev" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/file-null/src/InvalidFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Null; 15 | 16 | use Rekalogika\Contracts\File\FileNameInterface; 17 | use Rekalogika\Contracts\File\FileTypeInterface; 18 | use Rekalogika\Contracts\File\NullFileInterface; 19 | 20 | /** 21 | * A null file object that is also an exception 22 | */ 23 | final class InvalidFile extends \RuntimeException implements NullFileInterface 24 | { 25 | use NullFileTrait; 26 | 27 | public function __construct( 28 | private ?string $filesystemIdentifier = null, 29 | private ?string $key = null, 30 | ) {} 31 | 32 | #[\Override] 33 | public function getFilesystemIdentifier(): ?string 34 | { 35 | return $this->filesystemIdentifier; 36 | } 37 | 38 | #[\Override] 39 | public function getKey(): string 40 | { 41 | return $this->key ?? '/dev/null'; 42 | } 43 | 44 | #[\Override] 45 | public function getName(): FileNameInterface 46 | { 47 | return new NullName('Invalid', 'rekalogika_file'); 48 | } 49 | 50 | #[\Override] 51 | public function getType(): FileTypeInterface 52 | { 53 | return new NullType('Invalid file', 'rekalogika_file'); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/file-null/src/NullFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Null; 15 | 16 | use Rekalogika\Contracts\File\FileNameInterface; 17 | use Rekalogika\Contracts\File\FileTypeInterface; 18 | use Rekalogika\Contracts\File\NullFileInterface; 19 | 20 | /** 21 | * A null-value pattern implementation for FileInterface. 22 | */ 23 | final class NullFile implements NullFileInterface 24 | { 25 | use NullFileTrait; 26 | 27 | public function __construct( 28 | private ?string $filesystemIdentifier = null, 29 | private ?string $key = null, 30 | ) {} 31 | 32 | #[\Override] 33 | public function getFilesystemIdentifier(): ?string 34 | { 35 | return $this->filesystemIdentifier; 36 | } 37 | 38 | #[\Override] 39 | public function getKey(): string 40 | { 41 | return $this->key ?? '/dev/null'; 42 | } 43 | 44 | #[\Override] 45 | public function getName(): FileNameInterface 46 | { 47 | return new NullName('Null', 'rekalogika_file'); 48 | } 49 | 50 | #[\Override] 51 | public function getType(): FileTypeInterface 52 | { 53 | return new NullType('Null file', 'rekalogika_file'); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/file-null/src/NullPointer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Null; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\Contracts\File\FilePointerInterface; 18 | use Rekalogika\Contracts\File\NullFilePointerInterface; 19 | 20 | final class NullPointer implements NullFilePointerInterface 21 | { 22 | #[\Override] 23 | public function getFilesystemIdentifier(): ?string 24 | { 25 | return null; 26 | } 27 | 28 | #[\Override] 29 | public function getKey(): string 30 | { 31 | return '/dev/null'; 32 | } 33 | 34 | #[\Override] 35 | public function isEqualTo(FileInterface|FilePointerInterface $other): bool 36 | { 37 | return false; 38 | } 39 | 40 | #[\Override] 41 | public function isSameFilesystem(FileInterface|FilePointerInterface $other): bool 42 | { 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/file-null/src/TranslatableMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\Domain\File\Null; 15 | 16 | use Symfony\Contracts\Translation\TranslatableInterface; 17 | use Symfony\Contracts\Translation\TranslatorInterface; 18 | 19 | final readonly class TranslatableMessage implements \Stringable, TranslatableInterface 20 | { 21 | public function __construct( 22 | private string $name, 23 | private string $translationDomain, 24 | ) {} 25 | 26 | #[\Override] 27 | public function __toString(): string 28 | { 29 | return $this->name; 30 | } 31 | 32 | #[\Override] 33 | public function trans( 34 | TranslatorInterface $translator, 35 | ?string $locale = null, 36 | ): string { 37 | return $translator->trans( 38 | $this->name, 39 | [], 40 | $this->translationDomain, 41 | $locale, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/file-oneup-uploader-bridge/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-oneup-uploader-bridge/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-oneup-uploader-bridge/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-oneup-uploader-bridge 2 | 3 | An adapter to convert Oneup Uploader Bundle's FileInterface to rekalogika/file 4 | FileInterface. 5 | 6 | ## Documentation 7 | 8 | [rekalogika.dev/file](https://rekalogika.dev/file) 9 | 10 | ## License 11 | 12 | MIT 13 | 14 | ## Contributing 15 | 16 | The `rekalogika/file-oneup-uploader-bridge` repository is a read-only repo split 17 | from the main repo. Issues and pull requests should be submitted to the 18 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 19 | -------------------------------------------------------------------------------- /packages/file-oneup-uploader-bridge/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-oneup-uploader-bridge", 3 | "description": "An adapter to convert Oneup Uploader Bundle's FileInterface to rekalogika/file FileInterface.", 4 | "homepage": "https://rekalogika.dev/file", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "oneup", 10 | "uploader", 11 | "upload", 12 | "flysystem" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Priyadi Iman Nurcahyo", 17 | "email": "priyadi@rekalogika.com" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-4": { 22 | "Rekalogika\\File\\Bridge\\OneupUploader\\": "src/" 23 | } 24 | }, 25 | "require": { 26 | "php": "^8.2", 27 | "oneup/uploader-bundle": "^5.0", 28 | "rekalogika/file-contracts": "^2.2.2", 29 | "rekalogika/file": "^2.2.2" 30 | }, 31 | "extra": { 32 | "branch-alias": { 33 | "dev-main": "2.3-dev" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/file-oneup-uploader-bridge/src/FromOneUpUploaderFileAdapter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\OneupUploader; 15 | 16 | use League\Flysystem\FilesystemOperator; 17 | use Oneup\UploaderBundle\Uploader\File\FileInterface as TheirFileInterface; 18 | use Rekalogika\Contracts\File\FileInterface as OurFileInterface; 19 | use Rekalogika\File\File; 20 | 21 | /** 22 | * Adapter to convert a OneUpUploader's FileInterface into our FileInterface 23 | */ 24 | final class FromOneUpUploaderFileAdapter extends File 25 | { 26 | private function __construct(TheirFileInterface $file) 27 | { 28 | /** @psalm-suppress MixedAssignment */ 29 | $filesystem = $file->getFileSystem(); 30 | 31 | if ($filesystem === null) { 32 | $ourFilesystem = null; 33 | } elseif (class_exists(FilesystemOperator::class) && $filesystem instanceof FilesystemOperator) { 34 | $ourFilesystem = $filesystem; 35 | } else { 36 | throw new \InvalidArgumentException('Unsupported filesystem type: ' . get_debug_type($filesystem)); 37 | } 38 | 39 | parent::__construct($file->getPathname(), $ourFilesystem); 40 | } 41 | 42 | public static function adapt(TheirFileInterface $file): OurFileInterface 43 | { 44 | return new self($file); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/file-server/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-server/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-server/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-server 2 | 3 | Temporary URL resource server for rekalogika/file FileInterface. Access files 4 | easily in your application by creating temporary URLs that expire after a set 5 | amount of time. 6 | 7 | ## Documentation 8 | 9 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 10 | 11 | ## License 12 | 13 | MIT 14 | 15 | ## Contributing 16 | 17 | The `rekalogika/file-server` repository is a read-only repo split from the main 18 | repo. Issues and pull requests should be submitted to the 19 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 20 | -------------------------------------------------------------------------------- /packages/file-server/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-server", 3 | "description": "Temporary URL resource server for rekalogika/file FileInterface. Access files easily in your application by creating temporary URLs that expire after a set amount of time.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "temporary", 10 | "temporary url", 11 | "url", 12 | "server" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Priyadi Iman Nurcahyo", 17 | "email": "priyadi@rekalogika.com" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-4": { 22 | "Rekalogika\\File\\Server\\": "src/" 23 | } 24 | }, 25 | "require": { 26 | "php": "^8.2", 27 | "rekalogika/file-contracts": "^2.2.2", 28 | "rekalogika/file-symfony-bridge": "^2.2.2", 29 | "rekalogika/temporary-url-bundle": "^1.7.2", 30 | "symfony/http-foundation": "^6.2 || ^7.0" 31 | }, 32 | "extra": { 33 | "branch-alias": { 34 | "dev-main": "2.3-dev" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/file-server/src/FileInterfaceResourceServer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Server; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\Contracts\File\FilePointerInterface; 18 | use Rekalogika\Contracts\File\FileRepositoryInterface; 19 | use Rekalogika\File\Bridge\Symfony\HttpFoundation\FileResponse; 20 | use Rekalogika\TemporaryUrl\Attribute\AsTemporaryUrlResourceTransformer; 21 | use Rekalogika\TemporaryUrl\Attribute\AsTemporaryUrlServer; 22 | use Symfony\Component\HttpFoundation\Response; 23 | 24 | final readonly class FileInterfaceResourceServer 25 | { 26 | public function __construct( 27 | private FileRepositoryInterface $fileRepository, 28 | ) {} 29 | 30 | #[AsTemporaryUrlResourceTransformer] 31 | public function transform(FileInterface $file): FilePointerInterface 32 | { 33 | return $file->getPointer(); 34 | } 35 | 36 | 37 | #[AsTemporaryUrlServer] 38 | public function respond(FilePointerInterface $filePointer): Response 39 | { 40 | $file = $this->fileRepository->get($filePointer); 41 | 42 | return new FileResponse($file); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-symfony-bridge 2 | 3 | Provides integrations for Rekalogika FileInterface with Symfony HttpFoundation, 4 | Form, and Validator. 5 | 6 | ## Features 7 | 8 | * Adapters to convert HttpFoundation `File` objects to a `FileInterface` and 9 | vice versa, with special handling for `UploadedFile`. 10 | * `FileResponse` for streaming a `FileInterface` to the client web browser. 11 | * `FileType` form that works with `FileInterface` objects. 12 | * A form transformer `FileTransformer` that you can add to an existing Symfony 13 | `FileType` fields so that it gives us a `FileInterface` instead of a 14 | `UploadedFile` object. 15 | * A form extension `FileTypeExtension` that you can optionally register to 16 | automatically convert all the existing Symfony `FileType` so they all give us 17 | a `FileInterface`. 18 | * Subclassed `FileValidator` and `ImageValidator` that works with 19 | `FileInterface` objects. 20 | 21 | ## Documentation 22 | 23 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 24 | 25 | ## License 26 | 27 | MIT 28 | 29 | ## Contributing 30 | 31 | The `rekalogika/file-symfony-bridge` repository is a read-only repo split from 32 | the main repo. Issues and pull requests should be submitted to the 33 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 34 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-symfony-bridge", 3 | "description": "Provides integrations for Rekalogika FileInterface with Symfony HttpFoundation, Form, and Validator.", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "symfony", 10 | "httpfoundation", 11 | "form", 12 | "validator", 13 | "adapter", 14 | "response", 15 | "streaming" 16 | ], 17 | "authors": [ 18 | { 19 | "name": "Priyadi Iman Nurcahyo", 20 | "email": "priyadi@rekalogika.com" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "Rekalogika\\File\\Bridge\\Symfony\\": "src/" 26 | } 27 | }, 28 | "require": { 29 | "php": "^8.2", 30 | "rekalogika/file": "^2.2.2", 31 | "rekalogika/file-contracts": "^2.2.2", 32 | "rekalogika/file-metadata-contracts": "^2.2.2", 33 | "rekalogika/file-metadata": "^2.2.2", 34 | "symfony/form": "^6.2 || ^7.0", 35 | "symfony/http-foundation": "^6.2 || ^7.0", 36 | "symfony/validator": "^6.3 || ^7.1" 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-main": "2.3-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/src/Constraints/File.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\Symfony\Constraints; 15 | 16 | use Symfony\Component\Validator\Constraints\File as SymfonyFile; 17 | 18 | #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] 19 | final class File extends SymfonyFile {} 20 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/src/Constraints/FileValidator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\Symfony\Constraints; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Bridge\Symfony\HttpFoundation\ToHttpFoundationFileAdapter; 18 | use Symfony\Component\Validator\Constraint; 19 | use Symfony\Component\Validator\Constraints\FileValidator as SymfonyFileValidator; 20 | 21 | final class FileValidator extends SymfonyFileValidator 22 | { 23 | #[\Override] 24 | public function validate(mixed $value, Constraint $constraint): void 25 | { 26 | if ($value instanceof FileInterface) { 27 | $value = ToHttpFoundationFileAdapter::adapt($value); 28 | } 29 | 30 | parent::validate($value, $constraint); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/src/Constraints/Image.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\Symfony\Constraints; 15 | 16 | use Symfony\Component\Validator\Constraints\Image as SymfonyImage; 17 | 18 | /** 19 | * @api 20 | */ 21 | #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] 22 | class Image extends SymfonyImage {} 23 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/src/Constraints/ImageValidator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\Symfony\Constraints; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Bridge\Symfony\HttpFoundation\ToHttpFoundationFileAdapter; 18 | use Symfony\Component\Validator\Constraint; 19 | use Symfony\Component\Validator\Constraints\ImageValidator as SymfonyImageValidator; 20 | 21 | class ImageValidator extends SymfonyImageValidator 22 | { 23 | #[\Override] 24 | public function validate(mixed $value, Constraint $constraint): void 25 | { 26 | if ($value instanceof FileInterface) { 27 | $value = ToHttpFoundationFileAdapter::adapt($value); 28 | } 29 | 30 | parent::validate($value, $constraint); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/src/Form/FileType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\Symfony\Form; 15 | 16 | use Symfony\Component\Form\AbstractType; 17 | use Symfony\Component\Form\Extension\Core\Type\FileType as SymfonyFileType; 18 | use Symfony\Component\Form\FormBuilderInterface; 19 | 20 | final class FileType extends AbstractType 21 | { 22 | #[\Override] 23 | public function getParent(): string 24 | { 25 | return SymfonyFileType::class; 26 | } 27 | 28 | #[\Override] 29 | public function buildForm(FormBuilderInterface $builder, array $options): void 30 | { 31 | $builder->addModelTransformer(new FileTransformer()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-symfony-bridge/src/Form/FileTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Bridge\Symfony\Form; 15 | 16 | use Symfony\Component\Form\AbstractTypeExtension; 17 | use Symfony\Component\Form\Extension\Core\Type\FileType; 18 | use Symfony\Component\Form\FormBuilderInterface; 19 | 20 | final class FileTypeExtension extends AbstractTypeExtension 21 | { 22 | #[\Override] 23 | public static function getExtendedTypes(): iterable 24 | { 25 | return [FileType::class]; 26 | } 27 | 28 | #[\Override] 29 | public function buildForm(FormBuilderInterface $builder, array $options): void 30 | { 31 | $builder->addModelTransformer(new FileTransformer()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/file-zip/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file-zip/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file-zip/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file-zip 2 | 3 | Streams a collection of files as a zip file to the client. 4 | 5 | ## Documentation 6 | 7 | [rekalogika.dev/file-bundle](https://rekalogika.dev/file-bundle) 8 | 9 | ## License 10 | 11 | MIT 12 | 13 | ## Contributing 14 | 15 | The `rekalogika/zip` repository is a read-only repo split from the main repo. 16 | Issues and pull requests should be submitted to the 17 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 18 | -------------------------------------------------------------------------------- /packages/file-zip/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rekalogika/file-zip", 3 | "description": "Streams a collection of files as a ZIP file to the client", 4 | "homepage": "https://rekalogika.dev/file-bundle", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "file", 9 | "symfony", 10 | "zipstream", 11 | "zip", 12 | "archive", 13 | "download", 14 | "stream", 15 | "http" 16 | ], 17 | "authors": [ 18 | { 19 | "name": "Priyadi Iman Nurcahyo", 20 | "email": "priyadi@rekalogika.com" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "Rekalogika\\File\\Zip\\": "src/" 26 | } 27 | }, 28 | "require": { 29 | "php": "^8.2", 30 | "psr/http-message": "^1.0 || ^2.0", 31 | "rekalogika/file-contracts": "^2.2.2", 32 | "rekalogika/file-metadata": "^2.2.2", 33 | "rekalogika/temporary-url-bundle": "^1.7.2", 34 | "maennchen/zipstream-php": "^3.1", 35 | "symfony/http-foundation": "^6.2 || ^7.0", 36 | "symfony/translation-contracts": "^3.0" 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-main": "2.3-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/file-zip/src/Model/FilePointer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Zip\Model; 15 | 16 | use Rekalogika\Contracts\File\FilePointerInterface; 17 | use Rekalogika\Contracts\File\Trait\EqualityTrait; 18 | 19 | final class FilePointer implements FilePointerInterface 20 | { 21 | use EqualityTrait; 22 | 23 | public static function createFromInterface( 24 | FilePointerInterface $filePointer, 25 | Directory $directory, 26 | ): self { 27 | return new self( 28 | $filePointer->getFilesystemIdentifier(), 29 | $filePointer->getKey(), 30 | $directory, 31 | ); 32 | } 33 | 34 | public function __construct( 35 | private ?string $filesystemIdentifier, 36 | private string $key, 37 | private ?Directory $directory = null, 38 | ) {} 39 | 40 | #[\Override] 41 | public function getFilesystemIdentifier(): ?string 42 | { 43 | return $this->filesystemIdentifier; 44 | } 45 | 46 | #[\Override] 47 | public function getKey(): string 48 | { 49 | return $this->key; 50 | } 51 | 52 | public function getContainingDirectory(): ?Directory 53 | { 54 | return $this->directory; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/file/.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | -------------------------------------------------------------------------------- /packages/file/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Priyadi Iman Nurcahyo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/file/README.md: -------------------------------------------------------------------------------- 1 | # rekalogika/file 2 | 3 | High-level file abstraction library built on top of Flysystem. It lets you work 4 | with file objects in an object-oriented manner. A file object represents a file 5 | in a Flysystem filesystem. It can be a local file or a file in a cloud storage, 6 | the library lets you work with them in the same way. 7 | 8 | ## Features 9 | 10 | * Rich, high-level abstraction of files built on top of Flysystem. 11 | * Abstractions for file name and media type (MIME type). 12 | * Caches and stores metadata in a sidecar file. Uniform metadata support across 13 | all filesystems. 14 | * Uses the repository pattern for files. 15 | * Remote façade pattern in accessing metadata. Improves performance with remote 16 | filesystems. Two metadata queries require only one round trip. 17 | * Rich metadata support. 18 | * Option to use lazy-loading proxy for files. 19 | * Support for file derivations. 20 | * Separated contracts and implementation. Useful for enforcing architectural 21 | boundaries. Your domain models don't have to depend on the framework. 22 | 23 | ## Documentation 24 | 25 | [rekalogika.dev/file](https://rekalogika.dev/file) 26 | 27 | ## License 28 | 29 | MIT 30 | 31 | ## Contributing 32 | 33 | The `rekalogika/file` repository is a read-only repo split from the main repo. 34 | Issues and pull requests should be submitted to the 35 | [rekalogika/file-src](https://github.com/rekalogika/file-src) monorepo. 36 | -------------------------------------------------------------------------------- /packages/file/src/Adapter/FromSplFileInfoAdapter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Adapter; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\File; 18 | 19 | /** 20 | * Adapter to convert an \SplFileInfo to a FileInterface object. 21 | */ 22 | final class FromSplFileInfoAdapter extends File 23 | { 24 | private function __construct( 25 | private readonly \SplFileInfo $source, 26 | ) { 27 | parent::__construct($this->source->getRealPath()); 28 | } 29 | 30 | public static function adapt(\SplFileInfo $source): FileInterface 31 | { 32 | return new self($source); 33 | } 34 | 35 | public function getWrapped(): \SplFileInfo 36 | { 37 | return $this->source; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/file/src/Contracts/FilesystemRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Contracts; 15 | 16 | use League\Flysystem\FilesystemOperator; 17 | use Rekalogika\File\Exception\FilesystemRepository\FilesystemAlreadyExistsException; 18 | use Rekalogika\File\Exception\FilesystemRepository\FilesystemNotFoundException; 19 | 20 | /** 21 | * Repository for filesystems. Keeps all the filesystems registered in the 22 | * application. A filesystem repository has one filesystem by default: the 23 | * local filesystem identified by null. 24 | */ 25 | interface FilesystemRepositoryInterface 26 | { 27 | /** 28 | * Gets a filesystem by its identifier. Null identifier means the local 29 | * filesystem. 30 | * 31 | * @throws FilesystemNotFoundException 32 | */ 33 | public function getFilesystem( 34 | ?string $identifier, 35 | ): MetadataAwareFilesystemOperator; 36 | 37 | /** 38 | * Adds a filesystem to the repository. 39 | * 40 | * @throws FilesystemAlreadyExistsException 41 | */ 42 | public function addFilesystem( 43 | string $identifier, 44 | FilesystemOperator $filesystem, 45 | ): void; 46 | } 47 | -------------------------------------------------------------------------------- /packages/file/src/Contracts/MetadataAwareFilesystemOperator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Contracts; 15 | 16 | use League\Flysystem\FilesystemOperator; 17 | 18 | interface MetadataAwareFilesystemOperator extends 19 | FilesystemOperator, 20 | MetadataAwareFilesystemReader, 21 | MetadataAwareFilesystemWriter {} 22 | -------------------------------------------------------------------------------- /packages/file/src/Contracts/MetadataAwareFilesystemReader.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Contracts; 15 | 16 | use League\Flysystem\FilesystemOperator; 17 | 18 | interface MetadataAwareFilesystemReader extends FilesystemOperator 19 | { 20 | /** 21 | * Gets the metadata of the file. 22 | * 23 | * @return iterable 24 | */ 25 | public function getMetadata(string $location): iterable; 26 | } 27 | -------------------------------------------------------------------------------- /packages/file/src/Contracts/MetadataAwareFilesystemWriter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Contracts; 15 | 16 | use League\Flysystem\FilesystemOperator; 17 | 18 | interface MetadataAwareFilesystemWriter extends FilesystemOperator 19 | { 20 | /** 21 | * @param iterable $metadata 22 | */ 23 | public function setMetadata(string $location, iterable $metadata): void; 24 | } 25 | -------------------------------------------------------------------------------- /packages/file/src/Directory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File; 15 | 16 | use Rekalogika\Contracts\File\DirectoryInterface; 17 | use Rekalogika\Contracts\File\FileNameInterface; 18 | use Rekalogika\Contracts\File\NodeInterface; 19 | use Rekalogika\Domain\File\Metadata\Model\FileName; 20 | 21 | /** 22 | * A simple implementation of DirectoryInterface 23 | * 24 | * @template TKey of array-key 25 | * @template T of NodeInterface 26 | * @implements \IteratorAggregate 27 | * @implements DirectoryInterface 28 | */ 29 | final readonly class Directory implements DirectoryInterface, \IteratorAggregate 30 | { 31 | /** 32 | * @param array $entries 33 | */ 34 | public function __construct( 35 | private string $name, 36 | private array $entries = [], 37 | ) {} 38 | 39 | #[\Override] 40 | public function getIterator(): \Traversable 41 | { 42 | yield from $this->entries; 43 | } 44 | 45 | #[\Override] 46 | public function getName(): FileNameInterface 47 | { 48 | return new FileName($this->name); 49 | } 50 | 51 | #[\Override] 52 | public function count(): int 53 | { 54 | return \count($this->entries); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/file/src/Exception/FilesystemRepository/FilesystemAlreadyExistsException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Exception\FilesystemRepository; 15 | 16 | final class FilesystemAlreadyExistsException extends \LogicException implements FilesystemRepositoryException 17 | { 18 | public function __construct(string $filesystemId) 19 | { 20 | parent::__construct(\sprintf('Filesystem with identifier "%s" is already exists', $filesystemId)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/file/src/Exception/FilesystemRepository/FilesystemNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Exception\FilesystemRepository; 15 | 16 | final class FilesystemNotFoundException extends \RuntimeException implements FilesystemRepositoryException 17 | { 18 | public function __construct(string $filesystemId) 19 | { 20 | parent::__construct(\sprintf('Filesystem with identifier "%s" is not found', $filesystemId)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/file/src/Exception/FilesystemRepository/FilesystemRepositoryException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Exception\FilesystemRepository; 15 | 16 | use Rekalogika\Contracts\File\Exception\FileException; 17 | 18 | interface FilesystemRepositoryException extends FileException {} 19 | -------------------------------------------------------------------------------- /packages/file/src/FilePointer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File; 15 | 16 | use Rekalogika\Contracts\File\FilePointerInterface; 17 | use Rekalogika\Contracts\File\Trait\EqualityTrait; 18 | 19 | final class FilePointer implements FilePointerInterface 20 | { 21 | use EqualityTrait; 22 | 23 | public function __construct( 24 | private ?string $filesystemIdentifier, 25 | private string $key, 26 | ) {} 27 | 28 | #[\Override] 29 | public function getFilesystemIdentifier(): ?string 30 | { 31 | return $this->filesystemIdentifier; 32 | } 33 | 34 | #[\Override] 35 | public function getKey(): string 36 | { 37 | return $this->key; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/file/src/MetadataGenerator/MetadataGeneratorInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\MetadataGenerator; 15 | 16 | use Rekalogika\Contracts\File\RawMetadataInterface; 17 | 18 | /** 19 | * Examines a local file, string, or stream, and generate metadata from it. 20 | */ 21 | interface MetadataGeneratorInterface 22 | { 23 | /** 24 | * Generates metadata from a local file 25 | */ 26 | public function generateMetadataFromFile( 27 | RawMetadataInterface $rawMetadata, 28 | string|\SplFileInfo $file, 29 | ): void; 30 | 31 | /** 32 | * Generates metadata from a string 33 | */ 34 | public function generateMetadataFromString( 35 | RawMetadataInterface $rawMetadata, 36 | string $content, 37 | ): void; 38 | 39 | /** 40 | * Generates metadata from a stream 41 | * 42 | * @param resource $stream 43 | */ 44 | public function generateMetadataFromStream( 45 | RawMetadataInterface $rawMetadata, 46 | mixed $stream, 47 | int $length = 32768, 48 | ): void; 49 | } 50 | -------------------------------------------------------------------------------- /packages/file/src/MetadataSerializer/MetadataSerializerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\MetadataSerializer; 15 | 16 | use Rekalogika\Contracts\File\RawMetadataInterface; 17 | 18 | /** 19 | * Serialize and deserialize metadata 20 | */ 21 | interface MetadataSerializerInterface 22 | { 23 | public function serialize(RawMetadataInterface $metadata): string; 24 | 25 | public function deserialize(string $serialized): RawMetadataInterface; 26 | } 27 | -------------------------------------------------------------------------------- /packages/file/src/Repository/LocalFilesystemAdapter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Repository; 15 | 16 | use League\Flysystem\Local\LocalFilesystemAdapter as FlysystemLocalFilesystemAdapter; 17 | 18 | final class LocalFilesystemAdapter extends FlysystemLocalFilesystemAdapter 19 | { 20 | #[\Override] 21 | protected function ensureDirectoryExists(string $dirname, int $visibility): void {} 22 | } 23 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | paths: 4 | - packages 5 | - tests/src 6 | # exceptions: 7 | # check: 8 | # missingCheckedExceptionInThrows: true 9 | # tooWideThrowType: true 10 | ignoreErrors: 11 | - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children#' 12 | - 13 | message: '#Attribute class Override does not exist#' 14 | reportUnmatched: false 15 | - 16 | identifier: method.nonObject 17 | path: packages/file-bundle/src/DependencyInjection/Configuration.php 18 | - 19 | identifier: return.unusedType 20 | path: packages/file-symfony-bridge/src/HttpFoundation/ToHttpFoundationFileAdapter.php 21 | - 22 | message: '#Call to an undefined method ReflectionClass<.*>::isUninitializedLazyObject#' 23 | reportUnmatched: false 24 | - 25 | message: '#Call to an undefined method ReflectionClass<.*>::initializeLazyObject#' 26 | reportUnmatched: false 27 | 28 | includes: 29 | - vendor/phpstan/phpstan-phpunit/extension.neon 30 | - vendor/phpstan/phpstan-phpunit/rules.neon 31 | - vendor/phpstan/phpstan-deprecation-rules/rules.neon 32 | - vendor/phpstan/phpstan-mockery/extension.neon 33 | - vendor/bnf/phpstan-psr-container/extension.neon 34 | - vendor/ekino/phpstan-banned-code/extension.neon 35 | - vendor/phpat/phpat/extension.neon 36 | 37 | services: 38 | - 39 | class: Rekalogika\File\Tests\Tests\Architecture\Architecture 40 | tags: 41 | - phpat.test 42 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | tests/src/ 22 | 23 | 24 | 25 | 26 | 27 | packages 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/assets/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap.js'; 2 | /* 3 | * Welcome to your app's main JavaScript file! 4 | * 5 | * This file will be included onto the page via the importmap() Twig function, 6 | * which should already be in your base.html.twig. 7 | */ 8 | import './styles/app.css'; 9 | 10 | console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉'); 11 | -------------------------------------------------------------------------------- /tests/assets/bootstrap.js: -------------------------------------------------------------------------------- 1 | import { startStimulusApp } from '@symfony/stimulus-bundle'; 2 | 3 | const app = startStimulusApp(); 4 | // register any custom, 3rd party controllers here 5 | // app.register('some_controller_name', SomeImportedController); 6 | -------------------------------------------------------------------------------- /tests/assets/controllers.json: -------------------------------------------------------------------------------- 1 | { 2 | "controllers": { 3 | "@rekalogika/temporary-url-bundle": { 4 | "autoexpire": { 5 | "enabled": true, 6 | "fetch": "eager", 7 | "autoimport": [] 8 | } 9 | } 10 | }, 11 | "entrypoints": [] 12 | } 13 | -------------------------------------------------------------------------------- /tests/assets/controllers/hello_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from '@hotwired/stimulus'; 2 | 3 | /* 4 | * This is an example Stimulus controller! 5 | * 6 | * Any element with a data-controller="hello" attribute will cause 7 | * this controller to be executed. The name "hello" comes from the filename: 8 | * hello_controller.js -> "hello" 9 | * 10 | * Delete this file or adapt it for your use! 11 | */ 12 | export default class extends Controller { 13 | connect() { 14 | this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/assets/controllers/rekalogika/file-filepond/filepond.js: -------------------------------------------------------------------------------- 1 | ../../../../../packages/file-filepond/assets/dist/filepond.js -------------------------------------------------------------------------------- /tests/assets/styles/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: skyblue; 3 | } 4 | -------------------------------------------------------------------------------- /tests/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 10 | * 11 | * For the full copyright and license information, please view the LICENSE file 12 | * that was distributed with this source code. 13 | */ 14 | 15 | namespace Rekalogika\Mapper\Tests; 16 | 17 | use Rekalogika\File\Tests\TestKernel; 18 | use Symfony\Bundle\FrameworkBundle\Console\Application; 19 | 20 | require_once dirname(__DIR__).'/../vendor/autoload_runtime.php'; 21 | 22 | return function (array $context) { 23 | $env = $context['APP_ENV'] ?? 'test'; 24 | assert(is_string($env)); 25 | 26 | $kernel = new TestKernel($env, (bool) $context['APP_DEBUG']); 27 | 28 | return new Application($kernel); 29 | }; 30 | -------------------------------------------------------------------------------- /tests/config/packages/asset_mapper.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | asset_mapper: 3 | # The paths to make available to the asset mapper. 4 | paths: 5 | - assets/ -------------------------------------------------------------------------------- /tests/config/packages/debug.yaml: -------------------------------------------------------------------------------- 1 | debug: 2 | # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. 3 | # See the "server:dump" command to start a new server. 4 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 5 | -------------------------------------------------------------------------------- /tests/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | driver: pdo_sqlite 4 | memory: true 5 | charset: UTF8 6 | 7 | orm: 8 | auto_generate_proxy_classes: true 9 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 10 | controller_resolver: 11 | auto_mapping: false 12 | enable_lazy_ghost_objects: true 13 | report_fields_where_declared: true 14 | mappings: 15 | App: 16 | is_bundle: false 17 | type: attribute 18 | dir: "%kernel.project_dir%/src/App/Entity" 19 | prefix: 'Rekalogika\File\Tests\App\Entity' 20 | alias: App 21 | -------------------------------------------------------------------------------- /tests/config/packages/flysystem.yaml: -------------------------------------------------------------------------------- 1 | flysystem: 2 | storages: 3 | default.storage: 4 | adapter: memory 5 | -------------------------------------------------------------------------------- /tests/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | kernel.secret: test 3 | 4 | framework: 5 | test: true 6 | http_method_override: false 7 | handle_all_throwables: true 8 | php_errors: 9 | log: true 10 | validation: 11 | email_validation_mode: html5 12 | http_client: 13 | enabled: true 14 | max_host_connections: 1 15 | default_options: 16 | timeout: 10 17 | -------------------------------------------------------------------------------- /tests/config/packages/rekalogika_file.yaml: -------------------------------------------------------------------------------- 1 | rekalogika_file: 2 | filesystems: 3 | # 'default.storage' is the filesystem key under 'flysystem.storages' 4 | # in config/packages/flysystem.yaml 5 | default: 'default.storage' 6 | -------------------------------------------------------------------------------- /tests/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | utf8: true 4 | -------------------------------------------------------------------------------- /tests/config/packages/translation.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | default_locale: en 3 | translator: 4 | default_path: '%kernel.project_dir%/translations' -------------------------------------------------------------------------------- /tests/config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | form_themes: ['bootstrap_5_horizontal_layout.html.twig'] 3 | -------------------------------------------------------------------------------- /tests/config/packages/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: 7 | enabled: true 8 | collect: true 9 | only_exceptions: false 10 | collect_serializer_data: true 11 | only_main_requests: false 12 | -------------------------------------------------------------------------------- /tests/config/routes/test.yaml: -------------------------------------------------------------------------------- 1 | test: 2 | resource: 3 | path: '../../src/App/Controller/' 4 | namespace: 'Rekalogika\File\Tests\App\Controller' 5 | type: attribute 6 | -------------------------------------------------------------------------------- /tests/config/routes/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler_wdt: 2 | resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" 3 | prefix: /_wdt 4 | 5 | web_profiler_profiler: 6 | resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" 7 | prefix: /_profiler 8 | -------------------------------------------------------------------------------- /tests/config/services.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 15 | 16 | return static function (ContainerConfigurator $containerConfigurator): void { 17 | $services = $containerConfigurator->services(); 18 | 19 | $services->defaults() 20 | ->autowire() 21 | ->autoconfigure() 22 | ->public(); 23 | 24 | $services 25 | ->load('Rekalogika\\File\\Tests\\App\\', '../src/App/*') 26 | ->exclude('../src/App/{TestKernel.php,Entity}'); 27 | 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /tests/public/.htaccess: -------------------------------------------------------------------------------- 1 | DirectoryIndex index.php 2 | 3 | Options -MultiViews 4 | 5 | 6 | RewriteEngine On 7 | 8 | RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$ 9 | RewriteRule .* - [E=BASE:%1] 10 | 11 | # Sets the HTTP_AUTHORIZATION header removed by Apache 12 | RewriteCond %{HTTP:Authorization} .+ 13 | RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0] 14 | 15 | # Removes the /index.php/ part from a URL, if present 16 | RewriteCond %{ENV:REDIRECT_STATUS} ="" 17 | RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L] 18 | 19 | # If the requested filename exists, simply serve it. 20 | # Otherwise rewrite all other queries to the front controller. 21 | RewriteCond %{REQUEST_FILENAME} !-f 22 | RewriteRule ^ %{ENV:BASE}/index.php [L] 23 | -------------------------------------------------------------------------------- /tests/public/index.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * For the full copyright and license information, please view the LICENSE file 13 | * that was distributed with this source code. 14 | */ 15 | 16 | require_once __DIR__ . '/../../vendor/autoload_runtime.php'; 17 | 18 | return fn(array $context): TestKernel => new TestKernel(); 19 | -------------------------------------------------------------------------------- /tests/src/App/Controller/TestController.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\App\Controller; 15 | 16 | use Rekalogika\File\Association\Contracts\ObjectManagerInterface; 17 | use Rekalogika\File\TemporaryFile; 18 | use Rekalogika\File\Tests\Tests\Model\Entity; 19 | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; 20 | use Symfony\Component\HttpFoundation\Response; 21 | use Symfony\Component\Routing\Attribute\Route; 22 | 23 | final class TestController extends AbstractController 24 | { 25 | #[Route('/', name: 'index')] 26 | public function index(ObjectManagerInterface $objectManager): Response 27 | { 28 | // create 29 | $entity = new Entity('1'); 30 | 31 | $newFile = TemporaryFile::createFromString('testContent'); 32 | $newFile->setName('newname.txt'); 33 | 34 | $entity->setFile($newFile); 35 | 36 | $objectManager->flushObject($entity); 37 | 38 | return $this->render('index.html.twig'); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/src/App/Entity/DummyEntity.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\App\Entity; 15 | 16 | class DummyEntity {} 17 | -------------------------------------------------------------------------------- /tests/src/App/Entity/User.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\App\Entity; 15 | 16 | use Doctrine\DBAL\Types\Types; 17 | use Doctrine\ORM\Mapping as ORM; 18 | use Rekalogika\Contracts\File\FileInterface; 19 | use Rekalogika\File\Association\Attribute\AsFileAssociation; 20 | use Rekalogika\File\Association\Attribute\WithFileAssociation; 21 | 22 | #[ORM\Entity] 23 | #[WithFileAssociation] 24 | class User 25 | { 26 | #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: Types::INTEGER)] 27 | private ?int $id = null; // @phpstan-ignore property.unusedType 28 | 29 | #[AsFileAssociation] 30 | private ?FileInterface $image = null; 31 | 32 | public function __construct(#[ORM\Column(type: Types::STRING, length: 255)] 33 | private string $name) {} 34 | 35 | public function getId(): ?int 36 | { 37 | return $this->id; 38 | } 39 | 40 | public function getName(): string 41 | { 42 | return $this->name; 43 | } 44 | 45 | public function getImage(): ?FileInterface 46 | { 47 | return $this->image; 48 | } 49 | 50 | public function setImage(?FileInterface $image): self 51 | { 52 | $this->image = $image; 53 | 54 | return $this; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/src/Factory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests; 15 | 16 | use League\Flysystem\Filesystem; 17 | use League\Flysystem\FilesystemOperator; 18 | use League\Flysystem\InMemory\InMemoryFilesystemAdapter; 19 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 20 | 21 | final class Factory 22 | { 23 | public static function createUrlGenerator(): UrlGeneratorInterface 24 | { 25 | $urlGenerator = \Mockery::mock(UrlGeneratorInterface::class); 26 | $urlGenerator->shouldReceive('generate') 27 | ->andReturn('__route__'); 28 | 29 | return $urlGenerator; 30 | } 31 | 32 | public static function createTestFilesystem(): FilesystemOperator 33 | { 34 | return new Filesystem(new InMemoryFilesystemAdapter()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/src/Tests/File/FromSplFileInfoAdapterTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\File; 15 | 16 | use PHPUnit\Framework\TestCase; 17 | use Rekalogika\File\Adapter\FromSplFileInfoAdapter; 18 | 19 | final class FromSplFileInfoAdapterTest extends TestCase 20 | { 21 | use FileTestTrait; 22 | 23 | public function testSplFileInfoAdapter(): void 24 | { 25 | $path = realpath(__DIR__ . '/../Resources/localFile.txt'); 26 | $this->assertNotFalse($path); 27 | $splFileInfo = new \SplFileInfo($path); 28 | $file = FromSplFileInfoAdapter::adapt($splFileInfo); 29 | 30 | $this->assertFileInterface( 31 | file: $file, 32 | filesystemIdentifier: null, 33 | key: $path, 34 | fileName: 'localFile.txt', 35 | content: 'test', 36 | type: 'text/plain', 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/src/Tests/FileImage/Fixtures/image_resize.test: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | image_resize 3 | --TEMPLATE-- 4 | {{ (getvar('image')|image_resize(10)).pointer.key }} 5 | --DATA-- 6 | return []; 7 | --CONFIG-- 8 | return [] 9 | --EXPECT-- 10 | smiley.d/original-10 11 | -------------------------------------------------------------------------------- /tests/src/Tests/FileImage/TwigTestExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\FileImage; 15 | 16 | use Twig\Extension\AbstractExtension; 17 | use Twig\TwigFunction; 18 | 19 | final class TwigTestExtension extends AbstractExtension 20 | { 21 | /** 22 | * @param array $variables 23 | */ 24 | public function __construct( 25 | private array $variables, 26 | ) {} 27 | 28 | #[\Override] 29 | public function getFunctions(): array 30 | { 31 | return [ 32 | new TwigFunction( 33 | 'getvar', 34 | function (string $name): mixed { 35 | return $this->variables[$name] ?? throw new \InvalidArgumentException( 36 | \sprintf('Variable "%s" not found', $name), 37 | ); 38 | }, 39 | ), 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/Tests/FileSymfonyBridge/FileTransformerTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\FileSymfonyBridge; 15 | 16 | use PHPUnit\Framework\TestCase; 17 | use Rekalogika\File\Bridge\Symfony\Form\FileTransformer; 18 | use Rekalogika\File\TemporaryFile; 19 | use Symfony\Component\HttpFoundation\File\File; 20 | 21 | final class FileTransformerTest extends TestCase 22 | { 23 | public function testTransform(): void 24 | { 25 | $file = TemporaryFile::createFromString('foo'); 26 | $transformer = new FileTransformer(); 27 | $httpFoundationFile = $transformer->transform($file); 28 | $this->assertSame($file->getKey(), $httpFoundationFile->getPathname()); 29 | } 30 | 31 | public function testReverseTransform(): void 32 | { 33 | $httpFoundationFile = new File(__FILE__); 34 | $transformer = new FileTransformer(); 35 | $file = $transformer->reverseTransform($httpFoundationFile); 36 | $this->assertSame($file->getKey(), $httpFoundationFile->getPathname()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/Entity.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\Contracts\File\Association\FileAssociationInterface; 17 | use Rekalogika\Contracts\File\FileInterface; 18 | 19 | final class Entity implements FileAssociationInterface 20 | { 21 | private ?FileInterface $file = null; 22 | 23 | public function __construct( 24 | private readonly string $id, 25 | ) {} 26 | 27 | #[\Override] 28 | public static function getFileAssociationPropertyList(): array 29 | { 30 | return ['file']; 31 | } 32 | 33 | public function getId(): string 34 | { 35 | return $this->id; 36 | } 37 | 38 | /** 39 | * @impure 40 | * @phpstan-ignore impureMethod.pure 41 | */ 42 | public function getFile(): ?FileInterface 43 | { 44 | return $this->file; 45 | } 46 | 47 | public function setFile(?FileInterface $file): self 48 | { 49 | $this->file = $file; 50 | 51 | return $this; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityExtendingAbstractFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Doctrine\ORM\Mapping\Entity; 17 | use Rekalogika\Domain\File\Association\Entity\AbstractFile; 18 | 19 | #[Entity()] 20 | final class EntityExtendingAbstractFile extends AbstractFile {} 21 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithAnyId.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | final readonly class EntityWithAnyId 17 | { 18 | public function __construct( 19 | private mixed $id, 20 | ) {} 21 | 22 | public function getId(): mixed 23 | { 24 | return $this->id; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithFileProxyUtilGetterSetter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\Contracts\File\FileProxy; 18 | 19 | final class EntityWithFileProxyUtilGetterSetter 20 | { 21 | public function __construct( 22 | private ?FileInterface $file = null, 23 | ) {} 24 | 25 | public function getFile(): ?FileInterface 26 | { 27 | return FileProxy::getFile($this->file); 28 | } 29 | 30 | public function setFile(?FileInterface $file): self 31 | { 32 | $this->file = $file; 33 | 34 | return $this; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithLazyFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Association\Attribute\AsFileAssociation; 18 | use Rekalogika\File\Association\Attribute\WithFileAssociation; 19 | use Rekalogika\File\Association\Model\FetchMode; 20 | 21 | #[WithFileAssociation] 22 | final class EntityWithLazyFile 23 | { 24 | #[AsFileAssociation(fetch: FetchMode::Lazy)] 25 | private ?FileInterface $file = null; 26 | 27 | public function __construct(private readonly string $id) {} 28 | 29 | /** 30 | * @impure 31 | * @phpstan-ignore impureMethod.pure 32 | */ 33 | public function getFile(): ?FileInterface 34 | { 35 | return $this->file; 36 | } 37 | 38 | /** 39 | * Set the value of file 40 | */ 41 | public function setFile(?FileInterface $file): self 42 | { 43 | $this->file = $file; 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * Get the value of id 50 | */ 51 | public function getId(): string 52 | { 53 | return $this->id; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithMandatoryFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | use Rekalogika\File\Association\Attribute\AsFileAssociation; 18 | use Rekalogika\File\Association\Attribute\WithFileAssociation; 19 | 20 | #[WithFileAssociation] 21 | final class EntityWithMandatoryFile 22 | { 23 | #[AsFileAssociation] 24 | private FileInterface $file; 25 | 26 | public function __construct(private readonly string $id) {} 27 | 28 | /** 29 | * Get the value of file 30 | */ 31 | public function getFile(): FileInterface 32 | { 33 | return $this->file; 34 | } 35 | 36 | /** 37 | * Set the value of file 38 | */ 39 | public function setFile(FileInterface $file): self 40 | { 41 | $this->file = $file; 42 | 43 | return $this; 44 | } 45 | 46 | /** 47 | * Get the value of id 48 | */ 49 | public function getId(): string 50 | { 51 | return $this->id; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithOverridenSignature.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\File\Association\Attribute\WithFileAssociation; 17 | 18 | #[WithFileAssociation(signature: 'foo')] 19 | final class EntityWithOverridenSignature {} 20 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithPlainGetterSetter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | 18 | final class EntityWithPlainGetterSetter 19 | { 20 | public function __construct( 21 | private ?FileInterface $file = null, 22 | ) {} 23 | 24 | public function getFile(): ?FileInterface 25 | { 26 | return $this->file; 27 | } 28 | 29 | public function setFile(?FileInterface $file): self 30 | { 31 | $this->file = $file; 32 | 33 | return $this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/Tests/Model/EntityWithoutId.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE file 11 | * that was distributed with this source code. 12 | */ 13 | 14 | namespace Rekalogika\File\Tests\Tests\Model; 15 | 16 | use Rekalogika\Contracts\File\FileInterface; 17 | 18 | final class EntityWithoutId 19 | { 20 | private ?FileInterface $file = null; 21 | 22 | public function getFile(): ?FileInterface 23 | { 24 | return $this->file; 25 | } 26 | 27 | public function setFile(?FileInterface $file): self 28 | { 29 | $this->file = $file; 30 | 31 | return $this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/src/Tests/Resources/corrupt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rekalogika/file-src/3df82ce6f74bd005e401daafda385c207de6dd8c/tests/src/Tests/Resources/corrupt.jpg -------------------------------------------------------------------------------- /tests/src/Tests/Resources/legacyImageMetadata.json: -------------------------------------------------------------------------------- 1 | {"metadata":{"date":"2023-09-02T05:08:09+00:00","disposition":"inline","content-length":97481,"content-type":"image\/jpeg","last-modified":"2023-09-02T05:08:09+00:00","width":1024,"height":1024}} 2 | -------------------------------------------------------------------------------- /tests/src/Tests/Resources/legacyMetadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "date": "2023-09-17T13:26:36+00:00", 4 | "disposition": "inline", 5 | "file-name": null, 6 | "content-length": 4, 7 | "content-type": "text\/plain", 8 | "last-modified": "2023-09-17T13:26:36+00:00" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/src/Tests/Resources/localFile.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /tests/src/Tests/Resources/smiley.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rekalogika/file-src/3df82ce6f74bd005e401daafda385c207de6dd8c/tests/src/Tests/Resources/smiley.png -------------------------------------------------------------------------------- /tests/templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | 7 | {% block stylesheets %} 8 | {% endblock %} 9 | 10 | {% block javascripts %} 11 | {% block importmap %}{{ importmap('app') }}{% endblock %} 12 | {% endblock %} 13 | 14 | 15 | {% block body %}{% endblock %} 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/templates/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | --------------------------------------------------------------------------------