├── .github
├── ISSUE_TEMPLATE
│ ├── Bug.md
│ ├── Feature_Request.md
│ └── Question.md
├── stale.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .php-cs-fixer.dist.php
├── .php-version
├── LICENSE
├── README.md
├── composer.json
├── doc
├── adapter_async_aws_s3.md
├── adapter_awss3.md
├── adapter_azure_blob_storage.md
├── adapter_custom.md
├── adapter_ftp.md
├── adapter_gitlab.md
├── adapter_google_cloud_storage.md
├── adapter_in_memory.md
├── adapter_local.md
├── adapter_sftp.md
├── filesystem_create.md
├── filesystem_php_config.md
├── index.md
└── tests.md
├── phpstan.neon
├── phpunit.xml.dist
├── src
├── DependencyInjection
│ ├── Compiler
│ │ └── FilesystemPass.php
│ ├── Configuration.php
│ ├── Factory
│ │ ├── Adapter
│ │ │ ├── AsyncAwsS3Factory.php
│ │ │ ├── AwsS3V3Factory.php
│ │ │ ├── AzureBlobFactory.php
│ │ │ ├── CustomAdapterFactory.php
│ │ │ ├── FtpFactory.php
│ │ │ ├── GitlabFactory.php
│ │ │ ├── GoogleCloudStorageFactory.php
│ │ │ ├── InMemoryFactory.php
│ │ │ ├── LocalFactory.php
│ │ │ └── SftpFactory.php
│ │ ├── AdapterFactoryInterface.php
│ │ └── FactoryInterface.php
│ └── OneupFlysystemExtension.php
├── OneupFlysystemBundle.php
└── Resources
│ └── config
│ ├── adapters.xml
│ ├── factories.xml
│ └── flysystem.xml
└── tests
├── App
├── Kernel.php
└── config
│ └── config.yml
├── DependencyInjection
├── Compiler
│ └── FilesystemPassTest.php
└── OneupFlysystemExtensionTest.php
└── bootstrap.php
/.github/ISSUE_TEMPLATE/Bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐛 Bug
3 | about: Did you encounter a bug?
4 | ---
5 |
6 | ### Bug Report
7 |
8 |
9 |
10 | | Q | A
11 | |------------ | ------
12 | | BC Break | yes
13 | | Version | x.y.z
14 |
15 | #### Summary
16 |
17 |
18 |
19 | #### How to reproduce
20 |
21 |
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_Request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🎉 Feature Request
3 | about: Do you have a new feature in mind?
4 | ---
5 |
6 | ### Feature Request
7 |
8 |
9 |
10 | | Q | A
11 | |------------ | ------
12 | | New Feature | yes
13 | | BC Break | yes/no
14 |
15 | #### Scenario
16 |
17 |
18 |
19 | #### Summary
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ❓ Question
3 | about: Are you unsure about something?
4 | ---
5 |
6 | ### Question
7 |
8 |
9 |
10 | | Q | A
11 | |------------ | ------
12 | | Version | x.y.z
13 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - postponed
8 | - enhancement
9 | - bug
10 | - question
11 | - Easy Pick
12 | # Label to use when marking an issue as stale
13 | staleLabel: stale
14 | # Comment to post when marking an issue as stale. Set to `false` to disable
15 | markComment: >
16 | This issue has been automatically marked as stale because it has not had
17 | recent activity. It will be closed if no further activity occurs. Thank you
18 | for your contributions.
19 | # Comment to post when closing a stale issue. Set to `false` to disable
20 | closeComment: false
21 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request: ~
5 | push:
6 | branches:
7 | - main
8 | tags:
9 | - '*'
10 |
11 | jobs:
12 | coding-style:
13 | name: Coding Style
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Setup PHP
17 | uses: shivammathur/setup-php@2.30.0
18 | with:
19 | php-version: 8.1
20 | extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, pcre, pdo, zlib
21 | coverage: none
22 |
23 | - name: Checkout
24 | uses: actions/checkout@v4
25 |
26 | - name: Install the dependencies
27 | run: composer install --no-interaction --no-suggest
28 | - name: Check the coding style
29 | run: vendor/bin/php-cs-fixer fix --diff --dry-run
30 | - name: Analyze the code
31 | run: vendor/bin/phpstan analyze --no-progress
32 |
33 | tests:
34 | name: PHP ${{ matrix.php }} / SF ^${{ matrix.symfony }}
35 | runs-on: ubuntu-latest
36 | strategy:
37 | fail-fast: false
38 | matrix:
39 | php: [8.1, 8.2, 8.3, 8.4]
40 | symfony: [5.4, 6.0, 7.0]
41 | exclude:
42 | - symfony: 7.0
43 | php: 8.0
44 | - symfony: 7.0
45 | php: 8.1
46 | steps:
47 | - name: Setup PHP
48 | uses: shivammathur/setup-php@2.30.0
49 | with:
50 | php-version: ${{ matrix.php }}
51 | extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, pcre, pdo_mysql, zlib
52 | coverage: none
53 |
54 | - name: Checkout
55 | uses: actions/checkout@v4
56 |
57 | - name: Install the dependencies
58 | run: |
59 | composer require symfony/framework-bundle:^${{ matrix.symfony }} symfony/translation:^${{ matrix.symfony }} symfony/console:^${{ matrix.symfony }} -W
60 | composer install --no-interaction --no-suggest
61 | - name: Run the unit tests
62 | run: vendor/bin/phpunit --colors=always
63 |
64 | prefer-lowest:
65 | name: Prefer Lowest
66 | runs-on: ubuntu-latest
67 | steps:
68 | - name: Setup PHP
69 | uses: shivammathur/setup-php@2.30.0
70 | with:
71 | php-version: 8.1
72 | extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, pcre, pdo_mysql, zlib
73 | coverage: none
74 |
75 | - name: Checkout
76 | uses: actions/checkout@v4
77 |
78 | - name: Install the dependencies
79 | run: composer update --prefer-lowest --prefer-stable --no-interaction --no-suggest
80 | - name: Run the unit tests
81 | run: vendor/bin/phpunit --colors=always
82 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | phpunit.xml
3 | .phpunit.result.cache
4 | composer.lock
5 | vendor
6 | tests/App/cache
7 | tests/App/logs
8 | tests/App/var
9 | cache
10 | var/cache
11 | var/log
12 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | in([__DIR__ . '/src', __DIR__ . '/tests'])
5 | ->exclude(['App/cache', 'App/var'])
6 | ;
7 |
8 | return (new PhpCsFixer\Config())
9 | ->setRules([
10 | '@DoctrineAnnotation' => true,
11 | '@Symfony' => true,
12 | '@Symfony:risky' => true,
13 | '@PHP71Migration' => true,
14 | '@PHP71Migration:risky' => true,
15 | '@PHPUnit60Migration:risky' => true,
16 | '@PHPUnit75Migration:risky' => true,
17 | 'align_multiline_comment' => true,
18 | 'array_syntax' => ['syntax' => 'short'],
19 | 'concat_space' => [
20 | 'spacing' => 'one',
21 | ],
22 | 'combine_consecutive_issets' => true,
23 | 'combine_consecutive_unsets' => true,
24 | 'general_phpdoc_annotation_remove' => [
25 | 'annotations' => [
26 | 'author',
27 | 'expectedException',
28 | 'expectedExceptionMessage',
29 | ],
30 | ],
31 | 'heredoc_to_nowdoc' => true,
32 | 'linebreak_after_opening_tag' => true,
33 | 'list_syntax' => ['syntax' => 'short'],
34 | 'no_superfluous_elseif' => true,
35 | 'no_unreachable_default_argument_value' => true,
36 | 'no_useless_else' => true,
37 | 'no_useless_return' => true,
38 | 'ordered_class_elements' => true,
39 | 'ordered_imports' => true,
40 | 'php_unit_strict' => true,
41 | 'phpdoc_add_missing_param_annotation' => true,
42 | 'phpdoc_order' => true,
43 | 'phpdoc_types_order' => [
44 | 'null_adjustment' => 'always_last',
45 | 'sort_algorithm' => 'none',
46 | ],
47 | 'strict_comparison' => true,
48 | 'strict_param' => true,
49 | 'void_return' => true,
50 | ])
51 | ->setFinder($finder)
52 | ->setRiskyAllowed(true)
53 | ->setUsingCache(false)
54 | ;
55 |
--------------------------------------------------------------------------------
/.php-version:
--------------------------------------------------------------------------------
1 | 8.0
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 1up GmbH
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, 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,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OneupFlysystemBundle
2 | ====================
3 |
4 | [](https://github.com/1up-lab/OneupFlysystemBundle/actions)
5 | [](https://packagist.org/packages/oneup/flysystem-bundle)
6 |
7 | **Now 4.x available with [Flysystem V2 & V3](https://flysystem.thephpleague.com/docs/what-is-new/) support!**
8 |
9 | The OneupFlysystemBundle provides a [Flysystem](https://github.com/thephpleague/flysystem) integration for your Symfony projects. Flysystem is a filesystem abstraction which allows you to easily swap out a local filesystem for a remote one. Currently you can configure the following adapters to use in your Symfony project.
10 |
11 | * [Google Cloud Storage](https://cloud.google.com/storage)
12 | * [AsyncAwsS3](https://async-aws.com/)
13 | * [AwsS3](https://aws.amazon.com/de/sdk-for-php/)
14 | * [AzureBlobStorage](https://azure.microsoft.com/en-us/services/storage/blobs/)
15 | * [Ftp](https://www.php.net/manual/en/book.ftp.php)
16 | * [Local filesystem](https://www.php.net/manual/en/ref.filesystem.php)
17 | * [Sftp](https://phpseclib.sourceforge.net/sftp/intro.html)
18 |
19 | Documentation
20 | -------------
21 |
22 | The entry point of the documentation can be found in the file `doc/index.md`
23 |
24 | [Read the documentation for the latest release](doc/index.md)
25 |
26 |
27 | Flysystem 1.x
28 | -------------
29 | If you're looking for Flysystem 1.x support, check out the [3.x-branch](https://github.com/1up-lab/OneupFlysystemBundle/tree/release/3.x) of this bundle.
30 |
31 |
32 | License
33 | -------
34 |
35 | This bundle is under the MIT license. See the [complete license](LICENSE) in the bundle:
36 |
37 | Reporting an issue or a feature request
38 | ---------------------------------------
39 |
40 | Issues and feature requests are tracked in the [Github issue tracker](https://github.com/1up-lab/OneupFlysystemBundle/issues).
41 |
42 | When reporting a bug, it may be a good idea to reproduce it in a basic project
43 | built using the [symfony/website-skeleton](https://symfony.com/doc/current/setup.html#creating-symfony-applications)
44 | to allow developers of the bundle to reproduce the issue by simply cloning it
45 | and following some steps.
46 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oneup/flysystem-bundle",
3 | "type": "symfony-bundle",
4 | "description": "Integrates Flysystem filesystem abstraction library to your Symfony project.",
5 | "keywords": [
6 | "symfony",
7 | "flysystem",
8 | "filesystem",
9 | "abstraction"
10 | ],
11 | "homepage": "https://1up.io",
12 | "license": "MIT",
13 | "authors": [
14 | {
15 | "name": "Jim Schmid",
16 | "email": "js@1up.io",
17 | "homepage": "https://1up.io",
18 | "role": "Developer"
19 | },
20 | {
21 | "name": "David Greminger",
22 | "email": "dg@1up.io",
23 | "homepage": "https://1up.io",
24 | "role": "Developer"
25 | }
26 | ],
27 | "require": {
28 | "php": "^8.1",
29 | "league/flysystem": "^2.0 || ^3.0",
30 | "symfony/config": "^5.4 || ^6.0 || ^7.0",
31 | "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0",
32 | "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0"
33 | },
34 | "require-dev": {
35 | "ext-simplexml": "*",
36 | "friendsofphp/php-cs-fixer": "^3.51",
37 | "league/flysystem-async-aws-s3": "^2.0 || ^3.0",
38 | "league/flysystem-aws-s3-v3": "^2.0 || ^3.0",
39 | "league/flysystem-ftp": "^2.0 || ^3.0",
40 | "league/flysystem-google-cloud-storage": "^2.0 || ^3.0",
41 | "league/flysystem-memory": "^2.0 || ^3.0",
42 | "league/flysystem-sftp-v3": "^2.0 || ^3.0",
43 | "league/flysystem-azure-blob-storage": "^3.0",
44 | "phpstan/phpstan": "^1.10",
45 | "phpunit/phpunit": "^9.6.17",
46 | "royvoetman/flysystem-gitlab-storage": "^2.0 || ^3.0",
47 | "symfony/asset": "^5.4 || ^6.0 || ^7.0",
48 | "symfony/browser-kit": "^5.4 || ^6.0 || ^7.0",
49 | "symfony/finder": "^5.4 || ^6.0 || ^7.0",
50 | "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0",
51 | "symfony/phpunit-bridge": "^7.0",
52 | "symfony/translation": "^5.4 || ^6.0 || ^7.0",
53 | "symfony/yaml": "^5.4 || ^6.0 || ^7.0"
54 | },
55 | "suggest": {
56 | "ext-fileinfo": "Required for MimeType",
57 | "ext-ftp": "Required for FTP and SFTP",
58 | "league/flysystem-async-aws-s3": "Use flysystem S3 adapter from AsyncAws",
59 | "league/flysystem-aws-s3-v3": "Use S3 storage with AWS SDK v3",
60 | "league/flysystem-google-cloud-storage": "Use Google Cloud Storage Adapter for Flysystem",
61 | "league/flysystem-sftp-v3": "Allows SFTP server storage via phpseclib",
62 | "royvoetman/flysystem-gitlab-storage": "Use Gitlab Storage filesystem for Flysystem"
63 | },
64 | "config": {
65 | "sort-packages": true
66 | },
67 | "autoload": {
68 | "psr-4": {
69 | "Oneup\\FlysystemBundle\\": "src"
70 | }
71 | },
72 | "autoload-dev": {
73 | "psr-4": {
74 | "Oneup\\FlysystemBundle\\Tests\\": "tests"
75 | },
76 | "classmap": [
77 | "tests/App/Kernel.php"
78 | ]
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/doc/adapter_async_aws_s3.md:
--------------------------------------------------------------------------------
1 | # Use the AsyncAwsS3 adapter
2 |
3 | In order to use the AsyncAwsS3 adapter, you first need to create a S3 client service.
4 | This can be done with the following configuration. Read more about instantiating
5 | the S3 client at [async-aws.com](https://async-aws.com/clients/) or use the
6 | [AsyncAws SymfonyBundle](https://async-aws.com/integration/symfony-bundle.html).
7 |
8 | ```yml
9 | services:
10 | acme.async.portable_visibility_converter:
11 | class: League\Flysystem\AsyncAwsS3\PortableVisibilityConverter
12 |
13 | acme.async_s3_client:
14 | class: AsyncAws\S3\S3Client
15 | arguments:
16 | - region: 'eu-central-1'
17 | accessKeyId: 'AKIAIOSFODNN7EXAMPLE'
18 | accessKeySecret: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
19 | ```
20 |
21 | Set this service as the value of the `client` key in the `oneup_flysystem` configuration.
22 |
23 | ```yml
24 | oneup_flysystem:
25 | adapters:
26 | acme.flysystem_adapter:
27 | async_aws_s3:
28 | client: acme.async_s3_client
29 | bucket: 'my_image_bucket'
30 | prefix: ''
31 | visibilityConverter: acme.async.portable_visibility_converter
32 |
33 | ```
34 |
35 | ## More to know
36 |
37 | * [Create and use your filesystem](filesystem_create.md)
38 |
--------------------------------------------------------------------------------
/doc/adapter_awss3.md:
--------------------------------------------------------------------------------
1 | # Use the AwsS3 adapter
2 |
3 | There are two AwsS3 adapters that you can use with this bundle. Below is the configuration for AwsS3v3 and AwsS3v2 adapters.
4 |
5 | ## AwsS3v3
6 |
7 | In order to use the AwsS3v3 adapter, you first need to create
8 | a client object defined as a service. This Flysystem adapter
9 | works with the [aws/aws-sdk-php](https://packagist.org/packages/aws/aws-sdk-php) package version 3 and above.
10 | This version requires you to use the "v4" of the signature.
11 |
12 | ```yml
13 | services:
14 | acme.awss3v3.portable_visibility_converter:
15 | class: League\Flysystem\AwsS3V3\PortableVisibilityConverter
16 |
17 | acme.s3_client:
18 | class: Aws\S3\S3Client
19 | arguments:
20 | -
21 | version: '2006-03-01' # or 'latest'
22 | region: "region-id" # 'eu-central-1' for example
23 | credentials:
24 | key: "s3-key"
25 | secret: "s3-secret"
26 | ```
27 |
28 | Set this service as the value of the `client` key in the `oneup_flysystem` configuration.
29 |
30 | ```yml
31 | oneup_flysystem:
32 | adapters:
33 | acme.flysystem_adapter:
34 | awss3v3:
35 | client: acme.s3_client
36 | bucket: 'bucket-name'
37 | prefix: 'path/prefix' # Optional path prefix, you can set empty string
38 | visibilityConverter: acme.awss3v3.portable_visibility_converter
39 | ```
40 |
41 | For more details on the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/v2/docs/adapter/aws-s3-v3/).
42 |
43 | ## Additional parameters
44 | Use `options` config to pass additional parameters to AWS.
45 |
46 | ### Example use case
47 |
48 | Sending files to S3 bucket owned by another AWS account.
49 | In this situation default `ACL` value, which is `private`, prevents bucket owner from viewing the file.
50 |
51 | To allow bucket owner full control over an object use below config example:
52 |
53 | ```yml
54 | oneup_flysystem:
55 | adapters:
56 | acme.flysystem_adapter:
57 | awss3v3:
58 | client: acme.s3_client
59 | bucket: 'bucket-name'
60 | prefix: 'path/prefix' # Optional path prefix, you can set empty string
61 | options:
62 | ACL: bucket-owner-full-control
63 | ```
64 |
65 | ## More to know
66 | * [Create and use your filesystem](filesystem_create.md)
67 |
--------------------------------------------------------------------------------
/doc/adapter_azure_blob_storage.md:
--------------------------------------------------------------------------------
1 | # Use the Azure Blob Storage adapter
2 |
3 | This adapter connects to the filesystem in the Azure Blob Storage.
4 |
5 |
6 | ```yml
7 | oneup_flysystem:
8 | adapters:
9 | acme.flysystem_adapter:
10 | azureblob:
11 | client: 'azure_blob_storage_client' # Service ID of the MicrosoftAzure\Storage\Blob\BlobRestProxy
12 | container: 'container-name'
13 | prefix: 'optional/prefix'
14 | ```
15 |
16 | For more details on the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/docs/adapter/azure-blob-storage/).
17 |
18 | ## More to know
19 |
20 | * [Create and use your filesystem](filesystem_create.md)
21 |
--------------------------------------------------------------------------------
/doc/adapter_custom.md:
--------------------------------------------------------------------------------
1 | # Use the Custom adapter
2 |
3 | In order to use a custom adapter, you first need to create
4 | a service implementing the `League\Flysystem\AdapterInterface`.
5 |
6 | Set this service as the value of the `service` key in the `oneup_flysystem` configuration.
7 |
8 | ```yml
9 | oneup_flysystem:
10 | adapters:
11 | acme.flysystem_adapter:
12 | custom:
13 | service: my_flysystem_service
14 | ```
15 |
16 | ## More to know
17 | * [Create and use your filesystem](filesystem_create.md)
18 |
--------------------------------------------------------------------------------
/doc/adapter_ftp.md:
--------------------------------------------------------------------------------
1 | # Use the FTP adapter
2 |
3 | This adapter works with the standard PHP FTP implementation which is documented in [the manual](http://www.php.net/manual/en/book.ftp.php).
4 | You have to provide at least a value for the `host` key.
5 |
6 | ```yml
7 | # app/config/config.yml
8 | oneup_flysystem:
9 | acme.ftp.portable_visibility_converter:
10 | class: League\Flysystem\UnixVisibility\PortableVisibilityConverter
11 |
12 | adapters:
13 | my_adapter:
14 | ftp:
15 | options:
16 | host: 'ftp.hostname.com' # required
17 | root: '/upload' # required
18 | username: 'username' # required
19 | password: 'password' # required
20 | visibilityConverter: acme.ftp.portable_visibility_converter
21 | ```
22 |
23 | For more details on the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/v2/docs/adapter/ftp/).
24 |
25 | ## More to know
26 | * [Create and use your filesystem](filesystem_create.md)
27 |
--------------------------------------------------------------------------------
/doc/adapter_gitlab.md:
--------------------------------------------------------------------------------
1 | # Use the Gitlab adapter
2 |
3 | In order to use the Gitlab adapter, you first need to create a Gitlab client service.
4 | This can be done with the following configuration. Read more about instantiating
5 | the Gitlab client at [github.com/RoyVoetman/flysystem-gitlab-storage](https://github.com/RoyVoetman/flysystem-gitlab-storage#usage).
6 |
7 | ```yml
8 | services:
9 | acme.gitlab_client:
10 | class: RoyVoetman\FlysystemGitlab\Client
11 | arguments:
12 | - 'project-id'
13 | - 'branch'
14 | - 'base-url'
15 | - 'personal-access-token'
16 | ```
17 |
18 | Set this service as the value of the `client` key in the `oneup_flysystem` configuration.
19 |
20 | ```yml
21 | oneup_flysystem:
22 | adapters:
23 | acme.gitlab_adapter:
24 | gitlab:
25 | client: acme.gitlab_client
26 | prefix: 'optional/path/prefix'
27 | ```
28 |
29 | ## More to know
30 |
31 | * [Create and use your filesystem](filesystem_create.md)
32 |
--------------------------------------------------------------------------------
/doc/adapter_google_cloud_storage.md:
--------------------------------------------------------------------------------
1 | # Use the Google Cloud Storage adapter
2 |
3 | This adapter connects to the filesystem in the Google Cloud Storage.
4 |
5 |
6 | ```yml
7 | oneup_flysystem:
8 | adapters:
9 | acme.flysystem_adapter:
10 | googlecloudstorage:
11 | client: 'google_cloud_storage_client' # Service ID of the Google\Cloud\Storage\StorageClient
12 | bucket: 'my_gcs_bucket'
13 | prefix: ''
14 | ```
15 |
16 | For more details on the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/docs/adapter/google-cloud-storage/).
17 |
18 | ## More to know
19 |
20 | * [Create and use your filesystem](filesystem_create.md)
21 |
--------------------------------------------------------------------------------
/doc/adapter_in_memory.md:
--------------------------------------------------------------------------------
1 | # Use the MemoryAdapter
2 |
3 | This adapter keeps the filesystem solely in the memory.
4 |
5 | ```yml
6 | oneup_flysystem:
7 | adapters:
8 | memory_adapter:
9 | memory: ~
10 | ```
11 |
12 | For more details on the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/v2/docs/adapter/in-memory/).
13 |
14 | ## More to know
15 | * [Create and use your filesystem](filesystem_create.md)
16 |
--------------------------------------------------------------------------------
/doc/adapter_local.md:
--------------------------------------------------------------------------------
1 | # Use the local adapter
2 |
3 | To use the local adapter which stores files on the same server the Symfony instance runs, you have
4 | to provide a directory.
5 |
6 | ```yml
7 | # app/config/config.yml
8 | oneup_flysystem:
9 | adapters:
10 | my_adapter:
11 | local:
12 | location: "%kernel.root_dir%/../uploads"
13 | lazy: ~ # boolean (default "false")
14 | writeFlags: ~
15 | linkHandling: ~
16 | permissions:
17 | file:
18 | public: 0o644
19 | private: 0o600
20 | dir:
21 | public: 0o755
22 | private: 0o700
23 | lazyRootCreation: ~ # boolean (default "false")
24 | ```
25 |
26 | For more details on the `lazy` parameter, take a look at the [Symfony documentation](http://symfony.com/doc/current/components/dependency_injection/lazy_services.html).
27 | For the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/v2/docs/adapter/local/).
28 |
29 | ## More to know
30 | * [Create and use your filesystem](filesystem_create.md)
31 |
--------------------------------------------------------------------------------
/doc/adapter_sftp.md:
--------------------------------------------------------------------------------
1 | # Use the SFTP adapter
2 |
3 | You have to provide at least a value for the `host` key.
4 |
5 | ```yml
6 | # app/config/config.yml
7 | oneup_flysystem:
8 | adapters:
9 | my_adapter:
10 | sftp:
11 | options:
12 | host: 'ftp.domain.com'
13 | username: 'foo'
14 | root: '/upload'
15 | ```
16 |
17 | For more details on the other parameters, take a look at the [Flysystem documentation](https://flysystem.thephpleague.com/docs/adapter/sftp-v3/).
18 |
19 | ## More to know
20 | * [Create and use your filesystem](filesystem_create.md)
21 |
--------------------------------------------------------------------------------
/doc/filesystem_create.md:
--------------------------------------------------------------------------------
1 | # Create and use your filesystems
2 |
3 | After successfully configured the adapters, create a filesystem and inject an adapter of your choice.
4 |
5 | ```yml
6 | oneup_flysystem:
7 | adapter: ~
8 | filesystems:
9 | acme:
10 | adapter: my_adapter
11 | alias: ~
12 | mount: ~
13 | visibility: ~
14 | directory_visibility: ~
15 | ```
16 |
17 | This will expose a new service for you to use.
18 |
19 | ```php
20 | $filesystem = $container->get('oneup_flysystem.acme_filesystem');
21 | ```
22 |
23 | The naming scheme follows a simple rule: `oneup_flysystem.%s_filesystem` whereas `%s` is the name (config key) of your filesystem.
24 |
25 | The `$filesystem` variable is of the type [`\League\Flysystem\Filesystem`](https://github.com/thephpleague/flysystem/blob/master/src/Filesystem.php).
26 | Please refer to the [*General Usage* section](http://flysystem.thephpleague.com/api/#general-usage) in the official documentation for details.
27 |
28 | ## Alias your filesystem
29 |
30 | You can alias your filesystem by providing an `alias` key.
31 |
32 | ```yml
33 | oneup_flysystem:
34 | adapter: ~
35 | filesystems:
36 | acme:
37 | adapter: my_adapter
38 | alias: acme_filesystem
39 | ```
40 | Afterwards, the filesystem service is aliased with the provided value and can be retrieved like this:
41 |
42 | ```php
43 | $filesystem = $container->get('acme_filesystem');
44 | ```
45 |
46 | ## Inject your filesystem in your services
47 |
48 | If you use Dependency Injection instead of the container as service locator, you can inject your filesystems in your services:
49 |
50 | ```yml
51 | services:
52 | app.my_service:
53 | class: App\MyService
54 | arguments:
55 | - '@oneup_flysystem.acme_filesystem' # Inject the resolved service name, or the alias (see previous section)
56 | ```
57 |
58 | Dependency Injection is considered a **good practice** since you do not need the container, and you can then refer to it in your class like this:
59 |
60 | ```php
61 |
62 | use League\Flysystem\FilesystemOperator;
63 |
64 | class MyService
65 | {
66 | public function __construct(FilesystemOperator $acmeFilesystem)
67 | {
68 | $this->filesystem = $acmeFilesystem;
69 | }
70 | }
71 | ```
72 |
73 | 💡 Pro tip: when using **Symfony 4.2**, you can completely omit the service arguments definition in your config files,
74 | and instead you can automatically inject your filesystems by **providing the exact same type-hint as above**, and
75 | replace `acme` with the name of your filesystem. Thanks to the ``ContainerBuilder::registerAliasForArgument()`` method!
76 |
77 | ## Use the Mount Manager
78 |
79 | Details on the usage of the MountManager can be found in the [Flysystem documentation](https://flysystem.thephpleague.com/docs/advanced/mount-manager/).
80 |
81 | ## Add caching
82 |
83 | In version 1.x of Flysystem you could provide a cache per each adapter. [The cached adapter was not ported to V2 of Flysystem](https://flysystem.thephpleague.com/docs/upgrade-from-1.x/#miscellaneous-changes).
84 |
85 | If you want to use cached adapters, give a try to [lustmored/flysystem-v2-simple-cache-adapter](https://github.com/Lustmored/flysystem-v2-simple-cache-adapter).
86 |
--------------------------------------------------------------------------------
/doc/filesystem_php_config.md:
--------------------------------------------------------------------------------
1 | # Config based on PHP files
2 |
3 | If you're using Symfony 5 and want to configure this bundle with a PHP file instead of a YAML,
4 | you can set up the `config/packages/oneup_flysystem.php` file like this:
5 |
6 | ```php
7 |
8 | extension('oneup_flysystem', [
15 | 'adapters' => [
16 | 'my_adapter' => [
17 | 'local' => [
18 | 'directory' => '%kernel.root_dir%/cache'
19 | ]
20 | ]
21 | ],
22 | 'filesystems' => [
23 | 'my_filesystem' => [
24 | 'adapter' => 'my_adapter',
25 | 'visibility' => 'private'
26 | 'directory_visibility' => 'private'
27 | ]
28 | ]
29 | ]);
30 | };
31 | ```
32 |
--------------------------------------------------------------------------------
/doc/index.md:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 | The OneupFlysystemBundle was developed and tested for Symfony version 4.4+.
4 |
5 | ## Installation
6 | Perform the following steps to install and use the basic functionality of the OneupFlysystemBundle:
7 |
8 | * Download OneupFlysystemBundle using Composer
9 | * Enable the bundle
10 | * Configure your filesystems
11 |
12 | ### Step 1: Download the bundle
13 |
14 | Download the bundle via composer:
15 |
16 | ```sh
17 | composer require oneup/flysystem-bundle
18 | ```
19 |
20 | Composer will now fetch and install this bundle in the vendor directory `vendor/oneup`
21 |
22 | **Note**: There are some additional dependencies you will need to install for some features:
23 |
24 | * The AwsS3v3 adapter requires `"league/flysystem-aws-s3-v3"`
25 | * The FTP adapter requires `"league/flysystem-ftp"`
26 | * The SFTP (V3) adapter requires `"league/flysystem-sftp-v3"`
27 | * The Google Cloud Storage adapter requires `"league/flysystem-google-cloud-storage"`
28 | * The InMemory adapter requires `"league/flysystem-memory"`
29 | * The AsyncAwsS3 adapter requires `"league/flysystem-async-aws-s3"`
30 | * The Gitlab adapter requires `"royvoetman/flysystem-gitlab-storage"`
31 | * The Azure Blob Storage adapter requires `"league/flysystem-azure-blob-storage"`
32 |
33 | ### Step 2: Enable the bundle
34 | Enable the bundle in the kernel:
35 |
36 | ``` php
37 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | ./tests
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Compiler/FilesystemPass.php:
--------------------------------------------------------------------------------
1 | hasDefinition('oneup_flysystem.mount_manager')) {
16 | return;
17 | }
18 |
19 | $mountManager = $container->getDefinition('oneup_flysystem.mount_manager');
20 | $configuredFilesystems = $container->findTaggedServiceIds('oneup_flysystem.filesystem');
21 | $filesystems = [];
22 |
23 | foreach ($configuredFilesystems as $id => $attributes) {
24 | $filesystems[$attributes[0]['mount'] ?? $attributes[0]['key'] ?? $id] = new Reference($id);
25 | }
26 |
27 | $mountManager->replaceArgument(0, $filesystems);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 | getRootNode();
22 |
23 | $this->addAdapterSection($rootNode);
24 | $this->addFilesystemSection($rootNode);
25 |
26 | $rootNode
27 | ->children()
28 | ->end()
29 | ;
30 |
31 | return $treeBuilder;
32 | }
33 |
34 | private function addAdapterSection(ArrayNodeDefinition $node): void
35 | {
36 | $adapterNodeBuilder = $node
37 | ->fixXmlConfig('adapter')
38 | ->children()
39 | ->arrayNode('adapters')
40 | ->useAttributeAsKey('name')
41 | ->prototype('array')
42 | ->performNoDeepMerging()
43 | ->children()
44 | ;
45 |
46 | foreach ($this->adapterFactories as $name => $factory) {
47 | $factoryNode = $adapterNodeBuilder->arrayNode($name)->canBeUnset();
48 |
49 | $factory->addConfiguration($factoryNode);
50 | }
51 | }
52 |
53 | private function addFilesystemSection(ArrayNodeDefinition $node): void
54 | {
55 | $supportedVisibilities = [
56 | Visibility::PRIVATE,
57 | Visibility::PUBLIC,
58 | 'noPredefinedVisibility',
59 | ];
60 |
61 | $node
62 | ->fixXmlConfig('filesystem')
63 | ->children()
64 | ->arrayNode('filesystems')
65 | ->useAttributeAsKey('name')
66 | ->prototype('array')
67 | ->children()
68 | ->scalarNode('adapter')->isRequired()->end()
69 | ->scalarNode('alias')->defaultNull()->end()
70 | ->scalarNode('mount')->defaultNull()->end()
71 | ->scalarNode('visibility')
72 | ->validate()
73 | ->ifNotInArray($supportedVisibilities)
74 | ->thenInvalid('The visibility %s is not supported.')
75 | ->end()
76 | ->end()
77 | ->scalarNode('directory_visibility')
78 | ->validate()
79 | ->ifNotInArray($supportedVisibilities)
80 | ->thenInvalid('The visibility %s is not supported.')
81 | ->end()
82 | ->end()
83 | ->end()
84 | ->end()
85 | ->end()
86 | ;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/AsyncAwsS3Factory.php:
--------------------------------------------------------------------------------
1 | setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.async_aws_s3'))
26 | ->replaceArgument(0, new Reference($config['client']))
27 | ->replaceArgument(1, $config['bucket'])
28 | ->replaceArgument(2, $config['prefix'])
29 | ->replaceArgument(3, $visibilityConverter)
30 | ;
31 | }
32 |
33 | public function addConfiguration(NodeDefinition $node): void
34 | {
35 | $node
36 | ->children()
37 | ->scalarNode('client')->isRequired()->end()
38 | ->scalarNode('bucket')->isRequired()->end()
39 | ->scalarNode('prefix')->treatNullLike('')->defaultValue('')->end()
40 | ->scalarNode('visibilityConverter')->defaultNull()->end()
41 | ->end()
42 | ;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/AwsS3V3Factory.php:
--------------------------------------------------------------------------------
1 | setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.awss3v3'))
26 | ->replaceArgument(0, new Reference($config['client']))
27 | ->replaceArgument(1, $config['bucket'])
28 | ->replaceArgument(2, $config['prefix'])
29 | ->replaceArgument(3, $visibilityConverter)
30 | ->replaceArgument(4, $config['mimeTypeDetector'])
31 | ->replaceArgument(5, (array) $config['options'])
32 | ->replaceArgument(6, $config['streamReads'])
33 | ;
34 | }
35 |
36 | public function addConfiguration(NodeDefinition $node): void
37 | {
38 | $node
39 | ->children()
40 | ->scalarNode('client')->isRequired()->end()
41 | ->scalarNode('bucket')->isRequired()->end()
42 | ->scalarNode('prefix')->defaultValue('')->end()
43 | ->scalarNode('visibilityConverter')->defaultNull()->end()
44 | ->scalarNode('mimeTypeDetector')->defaultNull()->end()
45 | ->arrayNode('options')
46 | ->scalarPrototype()->end()
47 | ->end()
48 | ->booleanNode('streamReads')->defaultTrue()->end()
49 | ->end()
50 | ;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/AzureBlobFactory.php:
--------------------------------------------------------------------------------
1 | setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.azureblob'))
24 | ->replaceArgument(0, new Reference($config['client']))
25 | ->replaceArgument(1, $config['container'])
26 | ->replaceArgument(2, $config['prefix'])
27 | ;
28 | }
29 |
30 | public function addConfiguration(NodeDefinition $node): void
31 | {
32 | $node
33 | ->children()
34 | ->scalarNode('client')->isRequired()->end()
35 | ->scalarNode('container')->isRequired()->end()
36 | ->scalarNode('prefix')->defaultNull()->end()
37 | ->end()
38 | ;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/CustomAdapterFactory.php:
--------------------------------------------------------------------------------
1 | setAlias($id, $config['service']);
21 | }
22 |
23 | public function addConfiguration(NodeDefinition $node): void
24 | {
25 | $node
26 | ->children()
27 | ->variableNode('service')->isRequired()->cannotBeEmpty()->end()
28 | ->end()
29 | ;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/FtpFactory.php:
--------------------------------------------------------------------------------
1 | setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.ftp'))
28 | ->replaceArgument(0,
29 | (new Definition(FtpConnectionOptions::class))
30 | ->setFactory([FtpConnectionOptions::class, 'fromArray'])
31 | ->addArgument($config['options'])
32 | ->setShared(false)
33 | )
34 | ->replaceArgument(1, $config['connectionProvider'])
35 | ->replaceArgument(2, $config['connectivityChecker'])
36 | ->replaceArgument(3, $visibilityConverter)
37 | ->replaceArgument(4, $config['mimeTypeDetector'])
38 | ;
39 | }
40 |
41 | public function addConfiguration(NodeDefinition $node): void
42 | {
43 | $node
44 | ->children()
45 | ->arrayNode('options')
46 | ->children()
47 | ->scalarNode('host')->isRequired()->end()
48 | ->scalarNode('root')->isRequired()->end()
49 | ->scalarNode('username')->isRequired()->end()
50 | ->scalarNode('password')->isRequired()->end()
51 | ->scalarNode('port')->defaultValue(21)->end()
52 | ->booleanNode('ssl')->defaultFalse()->end()
53 | ->scalarNode('timeout')->defaultValue(90)->end()
54 | ->booleanNode('utf8')->defaultFalse()->end()
55 | ->booleanNode('passive')->defaultTrue()->end()
56 | ->scalarNode('transferMode')->defaultValue(\defined('FTP_BINARY') ? \FTP_BINARY : null)->end()
57 | ->scalarNode('systemType')->defaultNull()->end()
58 | ->booleanNode('ignorePassiveAddress')->defaultNull()->end()
59 | ->booleanNode('timestampsOnUnixListingsEnabled')->defaultFalse()->end()
60 | ->booleanNode('recurseManually')->defaultFalse()->end()
61 | ->booleanNode('useRawListOptions')->defaultFalse()->end()
62 | ->end()
63 | ->end()
64 | ->scalarNode('connectionProvider')->defaultNull()->end()
65 | ->scalarNode('connectivityChecker')->defaultNull()->end()
66 | ->scalarNode('visibilityConverter')->defaultNull()->end()
67 | ->scalarNode('mimeTypeDetector')->defaultNull()->end()
68 | ->end()
69 | ;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/GitlabFactory.php:
--------------------------------------------------------------------------------
1 | setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.gitlab'))
24 | ->replaceArgument(0, new Reference($config['client']))
25 | ->replaceArgument(1, $config['prefix'])
26 | ;
27 | }
28 |
29 | public function addConfiguration(NodeDefinition $node): void
30 | {
31 | $node
32 | ->children()
33 | ->scalarNode('client')->isRequired()->end()
34 | ->scalarNode('prefix')->treatNullLike('')->defaultValue('')->end()
35 | ->end()
36 | ;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/GoogleCloudStorageFactory.php:
--------------------------------------------------------------------------------
1 | setFactory([new Reference($config['client']), 'bucket']);
26 | $bucket->setArgument(0, $config['bucket']);
27 |
28 | $visibilityHandler = $config['visibilityHandler'] ? new Definition($config['visibilityHandler']) : null;
29 |
30 | $container
31 | ->setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.googlecloudstorage'))
32 | ->replaceArgument(0, $bucket)
33 | ->replaceArgument(1, $config['prefix'])
34 | ->replaceArgument(2, $visibilityHandler)
35 | ->replaceArgument(3, $config['defaultVisibility'])
36 | ->replaceArgument(4, $config['mimeTypeDetector'])
37 | ;
38 | }
39 |
40 | public function addConfiguration(NodeDefinition $node): void
41 | {
42 | $node
43 | ->children()
44 | ->scalarNode('client')->isRequired()->end()
45 | ->scalarNode('bucket')->isRequired()->end()
46 | ->scalarNode('prefix')->treatNullLike('')->defaultValue('')->end()
47 | ->scalarNode('visibilityHandler')->defaultNull()->end()
48 | ->scalarNode('defaultVisibility')->defaultValue(Visibility::PRIVATE)->end()
49 | ->scalarNode('mimeTypeDetector')->defaultNull()->end()
50 | ->end()
51 | ;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/InMemoryFactory.php:
--------------------------------------------------------------------------------
1 | setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.memory'))
24 | ->replaceArgument(0, $config['defaultVisibility'])
25 | ;
26 | }
27 |
28 | public function addConfiguration(NodeDefinition $node): void
29 | {
30 | $node
31 | ->children()
32 | ->scalarNode('defaultVisibility')->defaultValue(Visibility::PUBLIC)->end()
33 | ->end()
34 | ;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/LocalFactory.php:
--------------------------------------------------------------------------------
1 | setFactory([PortableVisibilityConverter::class, 'fromArray']);
29 | $visibilityConverter->setArgument(0, $config['permissions']);
30 | }
31 |
32 | $container
33 | ->setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.local'))
34 | ->setLazy($config['lazy'])
35 | ->replaceArgument(0, $config['location'])
36 | ->replaceArgument(1, $visibilityConverter)
37 | ->replaceArgument(2, $config['writeFlags'])
38 | ->replaceArgument(3, $config['linkHandling'])
39 | ->replaceArgument(4, $config['mimeTypeDetector'])
40 | ->replaceArgument(5, $config['lazyRootCreation'])
41 | ;
42 | }
43 |
44 | public function addConfiguration(NodeDefinition $node): void
45 | {
46 | $parseOctal = \Closure::fromCallable([self::class, 'parseOctal']);
47 |
48 | $node
49 | ->children()
50 | ->booleanNode('lazy')->defaultValue(false)->end()
51 | ->scalarNode('location')->isRequired()->end()
52 | ->arrayNode('permissions')
53 | ->children()
54 | ->arrayNode('file')
55 | ->children()
56 | ->integerNode('public')
57 | ->beforeNormalization()
58 | ->ifString()
59 | ->then($parseOctal)
60 | ->end()
61 | ->defaultNull()
62 | ->end()
63 | ->integerNode('private')
64 | ->beforeNormalization()
65 | ->ifString()
66 | ->then($parseOctal)
67 | ->end()
68 | ->defaultNull()
69 | ->end()
70 | ->end()
71 | ->end()
72 | ->arrayNode('dir')
73 | ->children()
74 | ->integerNode('public')
75 | ->beforeNormalization()
76 | ->ifString()
77 | ->then($parseOctal)
78 | ->end()
79 | ->defaultNull()
80 | ->end()
81 | ->integerNode('private')
82 | ->beforeNormalization()
83 | ->ifString()
84 | ->then($parseOctal)
85 | ->end()
86 | ->defaultNull()
87 | ->end()
88 | ->end()
89 | ->end()
90 | ->end()
91 | ->end()
92 | ->scalarNode('writeFlags')->defaultValue(\LOCK_EX)->end()
93 | ->scalarNode('linkHandling')->defaultValue(LocalFilesystemAdapter::DISALLOW_LINKS)->end()
94 | ->scalarNode('mimeTypeDetector')->defaultNull()->end()
95 | ->scalarNode('lazyRootCreation')->defaultValue(false)->end()
96 | ->end()
97 | ;
98 | }
99 |
100 | /**
101 | * Backward compatibility (BC) between symfony/yaml <= 5.4 and >= 6.0.
102 | *
103 | * @see https://github.com/symfony/symfony/pull/34813
104 | */
105 | private static function parseOctal(string $scalar): int
106 | {
107 | if (!preg_match('/^(?:\+|-)?0o?(?P[0-7_]++)$/', $scalar, $matches)) {
108 | throw new \InvalidArgumentException("The scalar \"$scalar\" is not a valid octal number.");
109 | }
110 |
111 | $value = str_replace('_', '', $matches['value']);
112 |
113 | if ('-' === $scalar[0]) {
114 | return (int) -octdec($value);
115 | }
116 |
117 | return (int) octdec($value);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/Adapter/SftpFactory.php:
--------------------------------------------------------------------------------
1 | setFactory([PortableVisibilityConverter::class, 'fromArray']);
37 | $visibilityConverter->setArgument(0, $config['permissions']);
38 | }
39 |
40 | $container
41 | ->setDefinition($id, new ChildDefinition('oneup_flysystem.adapter.sftp'))
42 | ->replaceArgument(0, (new Definition(SftpConnectionProvider::class))
43 | ->setFactory([SftpConnectionProvider::class, 'fromArray'])
44 | ->addArgument($config['options'])
45 | ->setShared(false)
46 | )
47 | ->replaceArgument(1, $root)
48 | ->replaceArgument(2, $visibilityConverter)
49 | ->replaceArgument(3, $config['mimeTypeDetector'])
50 | ;
51 | }
52 |
53 | public function addConfiguration(NodeDefinition $node): void
54 | {
55 | $parseOctal = \Closure::fromCallable([self::class, 'parseOctal']);
56 |
57 | $node
58 | ->children()
59 | ->arrayNode('options')->isRequired()
60 | ->children()
61 | ->scalarNode('host')->isRequired()->end()
62 | ->scalarNode('username')->isRequired()->end()
63 | ->scalarNode('password')->defaultNull()->end()
64 | ->scalarNode('privateKey')->defaultNull()->end()
65 | ->scalarNode('passphrase')->defaultNull()->end()
66 | ->scalarNode('port')->defaultValue(22)->end()
67 | ->booleanNode('useAgent')->defaultFalse()->end()
68 | ->scalarNode('timeout')->defaultValue(10)->end()
69 | ->scalarNode('maxTries')->defaultValue(4)->end()
70 | ->scalarNode('hostFingerprint')->defaultNull()->end()
71 | ->scalarNode('connectivityChecker')->defaultNull()->end()
72 | ->scalarNode('root')->isRequired()->end()
73 | ->end()
74 | ->end()
75 | ->arrayNode('permissions')
76 | ->children()
77 | ->arrayNode('file')
78 | ->children()
79 | ->integerNode('public')
80 | ->beforeNormalization()
81 | ->ifString()
82 | ->then($parseOctal)
83 | ->end()
84 | ->defaultNull()
85 | ->end()
86 | ->integerNode('private')
87 | ->beforeNormalization()
88 | ->ifString()
89 | ->then($parseOctal)
90 | ->end()
91 | ->defaultNull()
92 | ->end()
93 | ->end()
94 | ->end()
95 | ->arrayNode('dir')
96 | ->children()
97 | ->integerNode('public')
98 | ->beforeNormalization()
99 | ->ifString()
100 | ->then($parseOctal)
101 | ->end()
102 | ->defaultNull()
103 | ->end()
104 | ->integerNode('private')
105 | ->beforeNormalization()
106 | ->ifString()
107 | ->then($parseOctal)
108 | ->end()
109 | ->defaultNull()
110 | ->end()
111 | ->end()
112 | ->end()
113 | ->end()
114 | ->end()
115 | ->scalarNode('mimeTypeDetector')->defaultNull()->end()
116 | ->end()
117 | ;
118 | }
119 |
120 | /**
121 | * Backward compatibility (BC) between symfony/yaml <= 5.4 and >= 6.0.
122 | *
123 | * @see https://github.com/symfony/symfony/pull/34813
124 | */
125 | private static function parseOctal(string $scalar): int
126 | {
127 | if (!preg_match('/^(?:\+|-)?0o?(?P[0-7_]++)$/', $scalar, $matches)) {
128 | throw new \InvalidArgumentException("The scalar \"$scalar\" is not a valid octal number.");
129 | }
130 |
131 | $value = str_replace('_', '', $matches['value']);
132 |
133 | if ('-' === $scalar[0]) {
134 | return (int) -octdec($value);
135 | }
136 |
137 | return (int) octdec($value);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Factory/AdapterFactoryInterface.php:
--------------------------------------------------------------------------------
1 | load('factories.xml');
26 |
27 | $adapterFactories = $this->getFactories($container);
28 |
29 | $configuration = new Configuration($adapterFactories);
30 | $config = $this->processConfiguration($configuration, $configs);
31 |
32 | $loader->load('adapters.xml');
33 | $loader->load('flysystem.xml');
34 |
35 | $adapters = [];
36 |
37 | foreach ($config['adapters'] as $name => $adapter) {
38 | $adapters[$name] = $this->createAdapter($name, $adapter, $container, $adapterFactories);
39 | }
40 |
41 | foreach ($config['filesystems'] as $name => $filesystem) {
42 | $this->createFilesystem($name, $filesystem, $container, $adapters);
43 | }
44 | }
45 |
46 | public function getConfiguration(array $config, ContainerBuilder $container): Configuration
47 | {
48 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
49 | $loader->load('factories.xml');
50 |
51 | $adapterFactories = $this->getFactories($container);
52 |
53 | return new Configuration($adapterFactories);
54 | }
55 |
56 | private function createAdapter(string $name, array $config, ContainerBuilder $container, array $factories): string
57 | {
58 | foreach ($config as $key => $adapter) {
59 | if (\array_key_exists($key, $factories)) {
60 | $id = \sprintf('oneup_flysystem.%s_adapter', $name);
61 | $factories[$key]->create($container, $id, $adapter);
62 |
63 | return $id;
64 | }
65 | }
66 |
67 | throw new \LogicException(\sprintf('The adapter \'%s\' is not configured.', $name));
68 | }
69 |
70 | private function createFilesystem(string $name, array $config, ContainerBuilder $container, array $adapters): Reference
71 | {
72 | if (!\array_key_exists($config['adapter'], $adapters)) {
73 | throw new \LogicException(\sprintf('The adapter \'%s\' is not defined.', $config['adapter']));
74 | }
75 |
76 | $adapter = $adapters[$config['adapter']];
77 | $id = \sprintf('oneup_flysystem.%s_filesystem', $name);
78 |
79 | $tagParams = ['key' => $name];
80 |
81 | if ($config['mount']) {
82 | $tagParams['mount'] = $config['mount'];
83 | }
84 |
85 | $options = [];
86 |
87 | if (\array_key_exists('visibility', $config)) {
88 | $options[Config::OPTION_VISIBILITY] = $config['visibility'];
89 | }
90 |
91 | if (\array_key_exists('directory_visibility', $config)) {
92 | $options[Config::OPTION_DIRECTORY_VISIBILITY] = $config['directory_visibility'];
93 | }
94 |
95 | $container
96 | ->setDefinition($id, new ChildDefinition('oneup_flysystem.filesystem'))
97 | ->replaceArgument(0, new Reference($adapter))
98 | ->replaceArgument(1, $options)
99 | ->addTag('oneup_flysystem.filesystem', $tagParams)
100 | ->setPublic(true)
101 | ;
102 |
103 | if (!empty($config['alias'])) {
104 | $container->getDefinition($id)->setPublic(false);
105 |
106 | try {
107 | $alias = $container->setAlias($config['alias'], $id);
108 | } catch (InvalidArgumentException $exception) {
109 | $alias = $container->getAlias($config['alias']);
110 | }
111 |
112 | $alias->setPublic(true);
113 | }
114 |
115 | $aliasName = $name;
116 |
117 | if (!preg_match('~filesystem$~i', $aliasName)) {
118 | $aliasName .= 'Filesystem';
119 | }
120 |
121 | $container->registerAliasForArgument($id, FilesystemOperator::class, $aliasName)->setPublic(false);
122 |
123 | return new Reference($id);
124 | }
125 |
126 | private function getFactories(ContainerBuilder $container): array
127 | {
128 | return $this->getAdapterFactories($container);
129 | }
130 |
131 | private function getAdapterFactories(ContainerBuilder $container): array
132 | {
133 | if (null !== $this->adapterFactories) {
134 | return $this->adapterFactories;
135 | }
136 |
137 | $factories = [];
138 | $services = $container->findTaggedServiceIds('oneup_flysystem.adapter_factory');
139 |
140 | foreach (array_keys($services) as $id) {
141 | /** @var FactoryInterface $factory */
142 | $factory = $container->get($id);
143 | $factories[(string) str_replace('-', '_', $factory->getKey())] = $factory;
144 | }
145 |
146 | return $this->adapterFactories = $factories;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/OneupFlysystemBundle.php:
--------------------------------------------------------------------------------
1 | addCompilerPass(new FilesystemPass());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Resources/config/adapters.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/Resources/config/factories.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
9 |
10 |
11 |
13 |
14 |
15 |
17 |
18 |
19 |
21 |
22 |
23 |
25 |
26 |
27 |
29 |
30 |
31 |
33 |
34 |
35 |
37 |
38 |
39 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/Resources/config/flysystem.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Filesystems -->
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/App/Kernel.php:
--------------------------------------------------------------------------------
1 | load(__DIR__ . '/config/config.yml');
25 | }
26 |
27 | public function getProjectDir(): string
28 | {
29 | return __DIR__;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/App/config/config.yml:
--------------------------------------------------------------------------------
1 | services:
2 | _defaults:
3 | autowire: true
4 | autoconfigure: true
5 |
6 | Oneup\FlysystemBundle\Tests\DependencyInjection\TestService:
7 | public: true
8 |
9 | google_cloud_storage_client:
10 | class: Google\Cloud\Storage\StorageClient
11 |
12 | framework:
13 | translator: { fallback: en }
14 | secret: secret
15 | router:
16 | resource: '%kernel.project_dir%/config/routing.yml'
17 | strict_requirements: '%kernel.debug%'
18 | default_locale: en
19 | session: ~
20 | test: true
21 | trusted_hosts: ~
22 |
23 | oneup_flysystem:
24 | adapters:
25 | local:
26 | local:
27 | location: '%kernel.cache_dir%/1up'
28 | permissions:
29 | file:
30 | public: 0o644
31 | private: 0o600
32 | dir:
33 | public: 0o755
34 | private: 0o700
35 |
36 | memory:
37 | memory: ~
38 |
39 | async_aws_s3:
40 | async_aws_s3:
41 | client: 'test'
42 | bucket: 'test'
43 |
44 | custom:
45 | custom:
46 | service: 'test'
47 |
48 | ftp:
49 | ftp:
50 | options:
51 | host: hostname # required
52 | root: /root/path/ # required
53 | username: username # required
54 | password: password # required
55 |
56 | gitlab:
57 | gitlab:
58 | client: 'test'
59 |
60 | sftp:
61 | sftp:
62 | options:
63 | host: localhost
64 | username: foo
65 | root: '/upload'
66 |
67 | googlecloudstorage:
68 | googlecloudstorage:
69 | client: 'google_cloud_storage_client'
70 | bucket: 'test'
71 | prefix: 'prefix'
72 |
73 | filesystems:
74 | myfilesystem:
75 | adapter: local
76 |
77 | myfilesystem2:
78 | adapter: local
79 | visibility: public
80 | mount: 'local'
81 |
82 | myfilesystem3:
83 | adapter: local
84 | visibility: private
85 |
86 | myfilesystem4:
87 | adapter: googlecloudstorage
88 |
89 | myfilesystem5:
90 | adapter: local
91 |
92 | myfilesystem6:
93 | adapter: local
94 | directory_visibility: public
95 |
--------------------------------------------------------------------------------
/tests/DependencyInjection/Compiler/FilesystemPassTest.php:
--------------------------------------------------------------------------------
1 | get('oneup_flysystem.mount_manager');
17 | /** @var Filesystem $filesystem1 */
18 | $filesystem1 = self::getContainer()->get('oneup_flysystem.myfilesystem_filesystem');
19 | /** @var Filesystem $filesystem2 */
20 | $filesystem2 = self::getContainer()->get('oneup_flysystem.myfilesystem2_filesystem');
21 |
22 | self::assertFalse($filesystem1->fileExists('foo'));
23 | self::assertFalse($filesystem2->fileExists('bar'));
24 |
25 | $mountManager->write('myfilesystem://foo', 'foo');
26 | $mountManager->write('local://bar', 'bar');
27 |
28 | self::assertTrue($filesystem1->fileExists('foo'));
29 | self::assertTrue($filesystem2->fileExists('bar'));
30 |
31 | $mountManager->delete('myfilesystem://foo');
32 | $mountManager->delete('local://bar');
33 |
34 | self::assertFalse($filesystem1->fileExists('foo'));
35 | self::assertFalse($filesystem2->fileExists('bar'));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/DependencyInjection/OneupFlysystemExtensionTest.php:
--------------------------------------------------------------------------------
1 | get('oneup_flysystem.myfilesystem_filesystem');
32 |
33 | /**
34 | * Visibility flag is set to "public".
35 | *
36 | * @var Filesystem $filesystem2
37 | */
38 | $filesystem2 = self::getContainer()->get('oneup_flysystem.myfilesystem2_filesystem');
39 |
40 | /**
41 | * Visibility flag is set to "private".
42 | *
43 | * @var Filesystem $filesystem3
44 | */
45 | $filesystem3 = self::getContainer()->get('oneup_flysystem.myfilesystem3_filesystem');
46 |
47 | $filesystem1->write('1/meep', 'meep\'s content');
48 | $filesystem2->write('2/meep', 'meep\'s content');
49 | $filesystem3->write('3/meep', 'meep\'s content');
50 |
51 | self::assertSame(Visibility::PUBLIC, $filesystem1->visibility('1/meep'));
52 | self::assertSame(Visibility::PUBLIC, $filesystem2->visibility('2/meep'));
53 | self::assertSame(Visibility::PRIVATE, $filesystem3->visibility('3/meep'));
54 |
55 | $filesystem1->delete('1/meep');
56 | $filesystem1->delete('2/meep');
57 | $filesystem1->delete('3/meep');
58 | }
59 |
60 | public function testDirectoryVisibilitySettings(): void
61 | {
62 | if (version_compare((string) InstalledVersions::getVersion('league/flysystem'), '2.3.1', '<')) {
63 | $this->markTestSkipped('Flysystem >= 2.3.1 is required (see https://github.com/thephpleague/flysystem/pull/1368).');
64 | }
65 |
66 | /**
67 | * No directory visibility flag set, default to "private".
68 | *
69 | * @var Filesystem $filesystem5
70 | */
71 | $filesystem5 = self::getContainer()->get('oneup_flysystem.myfilesystem5_filesystem');
72 |
73 | /**
74 | * Visibility flag is set to "public".
75 | *
76 | * @var Filesystem $filesystem6
77 | */
78 | $filesystem6 = self::getContainer()->get('oneup_flysystem.myfilesystem6_filesystem');
79 |
80 | $filesystem5->createDirectory('5');
81 | $filesystem6->createDirectory('6');
82 |
83 | /** @var DirectoryAttributes $directory5Attributes */
84 | [$directory5Attributes] = $filesystem5->listContents('')->filter(static fn (StorageAttributes $attributes) => $attributes->isDir() && '5' === $attributes->path())->toArray();
85 | /** @var DirectoryAttributes $directory6Attributes */
86 | [$directory6Attributes] = $filesystem5->listContents('')->filter(static fn (StorageAttributes $attributes) => $attributes->isDir() && '6' === $attributes->path())->toArray();
87 |
88 | self::assertSame(Visibility::PRIVATE, $directory5Attributes->visibility());
89 | self::assertSame(Visibility::PUBLIC, $directory6Attributes->visibility());
90 | }
91 |
92 | public function testAdapterAvailability(): void
93 | {
94 | /** @var \SimpleXMLElement $adapters */
95 | $adapters = simplexml_load_string((string) file_get_contents(__DIR__ . '/../../src/Resources/config/adapters.xml'));
96 |
97 | foreach ($adapters->children()->children() as $service) {
98 | if (null === $service->attributes()) {
99 | continue;
100 | }
101 |
102 | foreach ($service->attributes() as $key => $attribute) {
103 | if ('class' === (string) $key) {
104 | self::assertTrue(class_exists((string) $attribute), 'Could not load class: ' . $attribute);
105 | }
106 | }
107 | }
108 | }
109 |
110 | public function testGetConfiguration(): void
111 | {
112 | $extension = new OneupFlysystemExtension();
113 | $configuration = $extension->getConfiguration([], new ContainerBuilder());
114 |
115 | self::assertInstanceOf('Oneup\FlysystemBundle\DependencyInjection\Configuration', $configuration);
116 | }
117 |
118 | public function testServiceAliasWithFilesystemSuffix(): void
119 | {
120 | $container = $this->loadExtension([
121 | 'oneup_flysystem' => [
122 | 'adapters' => [
123 | 'default_adapter' => [
124 | 'local' => [
125 | 'location' => '.',
126 | 'permissions' => [
127 | 'file' => [
128 | 'public' => '0644',
129 | 'private' => '0644',
130 | ],
131 | 'dir' => [
132 | 'public' => '0755',
133 | 'private' => '0700',
134 | ],
135 | ],
136 | ],
137 | ],
138 | ],
139 | 'filesystems' => [
140 | 'acme_filesystem' => [
141 | 'alias' => Filesystem::class,
142 | 'adapter' => 'default_adapter',
143 | ],
144 | ],
145 | ],
146 | ]);
147 |
148 | $aliasName = 'League\Flysystem\FilesystemOperator $acmeFilesystem';
149 |
150 | self::assertTrue($container->hasAlias($aliasName));
151 | self::assertSame('oneup_flysystem.acme_filesystem_filesystem', (string) $container->getAlias($aliasName));
152 |
153 | self::assertTrue($container->hasAlias(Filesystem::class));
154 | self::assertSame('oneup_flysystem.acme_filesystem_filesystem', (string) $container->getAlias(Filesystem::class));
155 | }
156 |
157 | public function testServiceAliasWithoutFilesystemSuffix(): void
158 | {
159 | $container = $this->loadExtension([
160 | 'oneup_flysystem' => [
161 | 'adapters' => [
162 | 'default_adapter' => [
163 | 'local' => [
164 | 'location' => '.',
165 | ],
166 | ],
167 | ],
168 | 'filesystems' => [
169 | 'acme' => [
170 | 'alias' => Filesystem::class,
171 | 'adapter' => 'default_adapter',
172 | ],
173 | ],
174 | ],
175 | ]);
176 |
177 | $aliasName = 'League\Flysystem\FilesystemOperator $acmeFilesystem';
178 |
179 | self::assertTrue($container->hasAlias($aliasName));
180 | self::assertSame('oneup_flysystem.acme_filesystem', (string) $container->getAlias($aliasName));
181 |
182 | self::assertTrue($container->hasAlias(Filesystem::class));
183 | self::assertSame('oneup_flysystem.acme_filesystem', (string) $container->getAlias(Filesystem::class));
184 | }
185 |
186 | public function testServiceAliasInjection(): void
187 | {
188 | /** @var TestService $testService */
189 | $testService = self::getContainer()->get(TestService::class);
190 |
191 | self::assertInstanceOf(TestService::class, $testService);
192 | self::assertInstanceOf(Filesystem::class, $testService->filesystem);
193 | }
194 |
195 | public function testGoogleCloudAdapter(): void
196 | {
197 | $this->assertInstanceOf(Filesystem::class, self::getContainer()->get('oneup_flysystem.myfilesystem4_filesystem'));
198 | }
199 |
200 | private function loadExtension(array $config): ContainerBuilder
201 | {
202 | $extension = new OneupFlysystemExtension();
203 | $extension->load($config, $container = new ContainerBuilder());
204 |
205 | return $container;
206 | }
207 | }
208 |
209 | /**
210 | * @internal
211 | */
212 | final class TestService
213 | {
214 | public FilesystemOperator $filesystem;
215 |
216 | public function __construct(FilesystemOperator $myfilesystem)
217 | {
218 | $this->filesystem = $myfilesystem;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |