diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67594392673..30b988edb4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,95 +4,104 @@ on: [push, pull_request] env: DEFAULT_COMPOSER_FLAGS: "--prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi" - PHPUNIT_EXCLUDE_GROUP: mssql,oci,wincache,xcache,zenddata,cubrid + PHPUNIT_EXCLUDE_GROUP: db,wincache,xcache,zenddata XDEBUG_MODE: coverage, develop +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: phpunit: name: PHP ${{ matrix.php }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} - services: - mysql: - image: mysql:5.7 - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: yiitest - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - postgres: - image: postgres:9.6 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: yiitest - ports: - - 5432:5432 - options: --name=postgres --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3 + strategy: fail-fast: false matrix: - os: [ubuntu-latest] - php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1] + include: + - php: 5.4 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 5.5 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 5.6 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 7.0 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 7.1 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 7.2 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 7.3 + coverage: none + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 7.4 + coverage: xdebug + extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 8.0 + coverage: none + extensions: apcu, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 8.1 + coverage: none + extensions: apcu, curl, dom, imagick, intl, mbstring, mcrypt, memcached + os: ubuntu-latest + - php: 8.2 + extensions: apcu, curl, dom, imagick, intl, mbstring, mcrypt, memcached + coverage: none + os: ubuntu-latest steps: - - name: Generate french locale + - name: Generate french locale. run: sudo locale-gen fr_FR.UTF-8 - - name: Checkout + + - name: Checkout. uses: actions/checkout@v3 - - name: Install PHP + + - name: Install PHP. uses: shivammathur/setup-php@v2 with: + coverage: ${{ matrix.coverage }} + extensions: ${{ matrix.extensions }} + ini-values: apc.enabled=1,apc.shm_size=32M,apc.enable_cli=1, date.timezone='UTC', session.save_path="${{ runner.temp }}" php-version: ${{ matrix.php }} tools: pecl - extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached, mysql, pdo, pdo_mysql, pdo_pgsql, pdo_sqlite, pgsql, sqlite - ini-values: date.timezone='UTC', session.save_path="${{ runner.temp }}" - - name: Install Memcached + + - name: Install Memcached. uses: niden/actions-memcached@v7 - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - name: Cache composer dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- - - name: Install dependencies + + - name: Install dependencies. run: composer update $DEFAULT_COMPOSER_FLAGS - - name: PHP Unit tests + + - name: Run tests with PHPUnit. + if: matrix.php < '7.4' || matrix.php >= '8.1' run: vendor/bin/phpunit --verbose --exclude-group $PHPUNIT_EXCLUDE_GROUP --colors=always - npm: - name: NPM 6 on ubuntu-latest - runs-on: ubuntu-latest + - name: Run tests with PHPUnit. + if: matrix.php == '8.0' + run: vendor/bin/phpunit --verbose --exclude-group $PHPUNIT_EXCLUDE_GROUP --colors=always - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 7.2 - ini-values: session.save_path=${{ runner.temp }} - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - name: Cache composer dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- - - name: Install dependencies - run: composer update $DEFAULT_COMPOSER_FLAGS - - name: Install node.js - uses: actions/setup-node@v1 + - name: Run tests with PHPUnit and generate coverage. + if: matrix.php == '7.4' + run: vendor/bin/phpunit --verbose --exclude-group $PHPUNIT_EXCLUDE_GROUP --coverage-clover=coverage.xml --colors=always + + - name: Upload coverage to Codecov. + if: matrix.php == '7.4' + uses: codecov/codecov-action@v3 with: - node-version: 6 - - name: Tests - run: | - npm install - npm test -# env: -# CI: true + file: ./coverage.xml diff --git a/.github/workflows/ci-mssql.yml b/.github/workflows/ci-mssql.yml index f8ad3eff475..94255d93ebe 100644 --- a/.github/workflows/ci-mssql.yml +++ b/.github/workflows/ci-mssql.yml @@ -4,42 +4,31 @@ on: name: ci-mssql +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: name: PHP ${{ matrix.php }}-mssql-${{ matrix.mssql }} env: - key: cache + EXTENSIONS: pdo, pdo_sqlsrv + XDEBUG_MODE: coverage, develop runs-on: ubuntu-latest strategy: matrix: include: - - php: 7.0 - extensions: pdo, pdo_sqlsrv-5.8.1 - mssql: server:2017-latest - - php: 7.1 - extensions: pdo, pdo_sqlsrv-5.8.1 - mssql: server:2017-latest - - php: 7.2 - extensions: pdo, pdo_sqlsrv-5.8.1 - mssql: server:2017-latest - - php: 7.3 - extensions: pdo, pdo_sqlsrv-5.8.1 - mssql: server:2017-latest - php: 7.4 - extensions: pdo, pdo_sqlsrv - mssql: server:2017-latest - - php: 7.4 - extensions: pdo, pdo_sqlsrv - mssql: server:2019-latest - - php: 8.0 - extensions: pdo, pdo_sqlsrv mssql: server:2017-latest - php: 8.0 - extensions: pdo, pdo_sqlsrv mssql: server:2019-latest + - php: 8.1 + mssql: server:2019-latest + - php: 8.2 + mssql: server:2022-latest services: mssql: @@ -62,42 +51,28 @@ jobs: - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php }} - extensions: ${{ matrix.extensions }} + coverage: xdebug + extensions: ${{ env.EXTENSIONS }} ini-values: date.timezone='UTC' + php-version: ${{ matrix.php }} tools: composer:v2, pecl - - name: Determine composer cache directory on Linux - run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV - - - name: Cache dependencies installed with composer - uses: actions/cache@v3 - with: - path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: | - php${{ matrix.php }}-composer- - - name: Update composer run: composer self-update - name: Install dependencies with composer run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - name: Install dependencies with composer php 8.0 - if: matrix.php == '8.0' - run: composer update --ignore-platform-reqs --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + - name: Run MSSQL tests with PHPUnit and generate coverage. + if: matrix.php == '7.4' + run: vendor/bin/phpunit --group mssql --coverage-clover=coverage.xml --colors=always - - name: PHP Unit tests for PHP 7.1 - run: vendor/bin/phpunit --coverage-clover=coverage.clover --group mssql --colors=always - if: matrix.php == '7.1' - - - name: Run tests with phpunit without coverage + - name: Run MSSQL tests with PHPUnit. + if: matrix.php > '7.4' run: vendor/bin/phpunit --group mssql --colors=always - - name: Code coverage - run: | - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.clover - if: matrix.php == '7.1' - continue-on-error: true # if is fork + - name: Upload coverage to Codecov. + if: matrix.php == '7.4' + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 639609b0e84..a948a9e4cae 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -4,12 +4,16 @@ on: name: ci-mysql +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: - name: PHP ${{ matrix.php-version }}-mysql-${{ matrix.mysql-version }} + name: PHP ${{ matrix.php }}-mysql-${{ matrix.mysql }} env: extensions: curl, intl, pdo, pdo_mysql - key: cache-v1 + XDEBUG_MODE: coverage, develop runs-on: ${{ matrix.os }} @@ -18,15 +22,19 @@ jobs: os: - ubuntu-latest - php-version: + php: - 7.4 + - 8.0 + - 8.1 + - 8.2 - mysql-version: + mysql: + - 5.7 - latest services: mysql: - image: mysql:${{ matrix.mysql-version }} + image: mysql:${{ matrix.mysql }} env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: yiitest @@ -35,46 +43,31 @@ jobs: options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - name: Checkout + - name: Checkout. uses: actions/checkout@v3 - - name: Setup cache environment - id: cache-env - uses: shivammathur/cache-extensions@v1 - with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.extensions }} - key: ${{ env.key }} - - - name: Cache extensions - uses: actions/cache@v3 - with: - path: ${{ steps.cache-env.outputs.dir }} - key: ${{ steps.cache-env.outputs.key }} - restore-keys: ${{ steps.cache-env.outputs.key }} - - - name: Install PHP with extensions + - name: Install PHP with extensions. uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.extensions }} + coverage: xdebug + extensions: ${{ env.EXTENSIONS }} ini-values: date.timezone='UTC' - coverage: pcov - - - name: Determine composer cache directory - if: matrix.os == 'ubuntu-latest' - run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + php-version: ${{ matrix.php }} + tools: composer:v2, pecl - - name: Cache dependencies installed with composer - uses: actions/cache@v1 - with: - path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }} - restore-keys: | - php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- - - - name: Install dependencies with composer + - name: Install dependencies with composer. run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - name: Run mysql tests with phpunit + - name: Run MySQL tests with PHPUnit and generate coverage. + if: matrix.php == '7.4' + run: vendor/bin/phpunit --group mysql --coverage-clover=coverage.xml --colors=always + + - name: Run MySQL tests with PHPUnit. + if: matrix.php > '7.4' run: vendor/bin/phpunit --group mysql --colors=always + + - name: Upload coverage to Codecov. + if: matrix.php == '7.4' + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml diff --git a/.github/workflows/ci-node.yml b/.github/workflows/ci-node.yml new file mode 100644 index 00000000000..0d937733554 --- /dev/null +++ b/.github/workflows/ci-node.yml @@ -0,0 +1,36 @@ +name: build-node + +on: [push, pull_request] + +env: + DEFAULT_COMPOSER_FLAGS: "--prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: NPM 6 on ubuntu-latest + + runs-on: ubuntu-latest + + steps: + - name: Checkout. + uses: actions/checkout@v3 + + - name: Install dependencies. + run: composer update $DEFAULT_COMPOSER_FLAGS + + - name: Install JQuery `3.6.*@stable` for tests. + run: composer require "bower-asset/jquery:3.6.*@stable" + + - name: Install node.js. + uses: actions/setup-node@v1 + with: + node-version: 6 + + - name: Tests. + run: | + npm install + npm test diff --git a/.github/workflows/ci-oracle.yml b/.github/workflows/ci-oracle.yml index d6c9db0f428..21520a1045b 100644 --- a/.github/workflows/ci-oracle.yml +++ b/.github/workflows/ci-oracle.yml @@ -4,13 +4,17 @@ on: name: ci-oracle +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: name: PHP ${{ matrix.php }}-${{ matrix.os }} env: extensions: oci8, pdo, pdo_oci - key: cache-v1 + XDEBUG_MODE: coverage, develop runs-on: ${{ matrix.os }} @@ -30,52 +34,28 @@ jobs: options: --name=oci steps: - - name: Checkout + - name: Checkout. uses: actions/checkout@v3 - - name: Setup cache environment - id: cache-env - uses: shivammathur/cache-extensions@v1 - with: - php-version: ${{ matrix.php }} - extensions: ${{ env.extensions }} - key: ${{ env.key }} - - - name: Cache extensions - uses: actions/cache@v1 - with: - path: ${{ steps.cache-env.outputs.dir }} - key: ${{ steps.cache-env.outputs.key }} - restore-keys: ${{ steps.cache-env.outputs.key }} - - - name: Install PHP with extensions + - name: Install PHP with extensions. uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php }} - extensions: ${{ env.extensions }} + coverage: xdebug + extensions: ${{ env.EXTENSIONS }} ini-values: date.timezone='UTC' - coverage: pcov + php-version: ${{ matrix.php }} tools: composer:v2, pecl - - name: Determine composer cache directory - run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + - name: Update composer. + run: composer self-update - - name: Cache dependencies installed with composer - uses: actions/cache@v3 - with: - path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: | - php${{ matrix.php }}-composer- - - - name: Install dependencies with composer + - name: Install dependencies with composer. run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - name: PHP Unit tests - run: vendor/bin/phpunit --coverage-clover=coverage.clover --group oci --colors=always + - name: Run Oracle tests with PHPUnit and generate coverage. + run: vendor/bin/phpunit --group oci --coverage-clover=coverage.xml --colors=always - - name: Code coverage - run: | - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.clover - continue-on-error: true # if is fork + - name: Upload coverage to Codecov. + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml diff --git a/.github/workflows/ci-pgsql.yml b/.github/workflows/ci-pgsql.yml index 7bfa4d44659..edaf4a5df5d 100644 --- a/.github/workflows/ci-pgsql.yml +++ b/.github/workflows/ci-pgsql.yml @@ -4,12 +4,16 @@ on: name: ci-pgsql +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: - name: PHP ${{ matrix.php-version }}-pgsql-${{ matrix.pgsql-version }} + name: PHP ${{ matrix.php }}-pgsql-${{ matrix.pgsql }} env: extensions: curl, intl, pdo, pdo_pgsql - key: cache-v1 + XDEBUG_MODE: coverage, develop runs-on: ${{ matrix.os }} @@ -18,19 +22,22 @@ jobs: os: - ubuntu-latest - php-version: + php: - 7.4 + - 8.0 + - 8.1 - pgsql-version: + pgsql: - 10 - 11 - 12 - 13 - 14 + - 15 services: postgres: - image: postgres:${{ matrix.pgsql-version }} + image: postgres:${{ matrix.pgsql }} env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -40,46 +47,34 @@ jobs: options: --name=postgres --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - name: Checkout + - name: Checkout. uses: actions/checkout@v3 - - name: Setup cache environment - id: cache-env - uses: shivammathur/cache-extensions@v1 - with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.extensions }} - key: ${{ env.key }} - - - name: Cache extensions - uses: actions/cache@v1 - with: - path: ${{ steps.cache-env.outputs.dir }} - key: ${{ steps.cache-env.outputs.key }} - restore-keys: ${{ steps.cache-env.outputs.key }} - - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.extensions }} + coverage: xdebug + extensions: ${{ env.EXTENSIONS }} ini-values: date.timezone='UTC' - coverage: pcov + php-version: ${{ matrix.php }} + tools: composer:v2, pecl - - name: Determine composer cache directory - if: matrix.os == 'ubuntu-latest' - run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + - name: Update composer. + run: composer self-update - - name: Cache dependencies installed with composer - uses: actions/cache@v3 - with: - path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }} - restore-keys: | - php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}- - - - name: Install dependencies with composer + - name: Install dependencies with composer. run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - name: Run pgsql tests with phpunit + - name: Run Pgsql tests with PHPUnit and generate coverage. + if: matrix.php == '7.4' + run: vendor/bin/phpunit --group pgsql --coverage-clover=coverage.xml --colors=always + + - name: Run Pgsql tests with PHPUnit. + if: matrix.php > '7.4' run: vendor/bin/phpunit --group pgsql --colors=always + + - name: Upload coverage to Codecov. + if: matrix.php == '7.4' + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml diff --git a/.github/workflows/ci-sqlite.yml b/.github/workflows/ci-sqlite.yml new file mode 100644 index 00000000000..b82d187ef9a --- /dev/null +++ b/.github/workflows/ci-sqlite.yml @@ -0,0 +1,63 @@ +on: + - pull_request + - push + +name: ci-sqlite + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + tests: + name: PHP ${{ matrix.php }}-sqlite + + env: + EXTENSIONS: pdo, pdo_sqlite, sqlite3 + XDEBUG_MODE: coverage, develop + + runs-on: ubuntu-latest + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - 7.4 + - 8.0 + - 8.1 + - 8.2 + + steps: + - name: Checkout. + uses: actions/checkout@v3 + + - name: Install PHP with extensions. + uses: shivammathur/setup-php@v2 + with: + coverage: xdebug + extensions: ${{ env.EXTENSIONS }} + ini-values: date.timezone='UTC' + php-version: ${{ matrix.php }} + tools: composer:v2, pecl + + - name: Update composer. + run: composer self-update + + - name: Install dependencies with composer. + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run SQLite tests with PHPUnit and generate coverage. + if: matrix.php == '7.4' + run: vendor/bin/phpunit --group sqlite --coverage-clover=coverage.xml --colors=always + + - name: Run SQLite tests with PHPUnit. + if: matrix.php > '7.4' + run: vendor/bin/phpunit --group sqlite --colors=always + + - name: Upload coverage to Codecov. + if: matrix.php == '7.4' + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml diff --git a/Dockerfile b/Dockerfile index 86f3a3a85c7..4e779bdeba4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,10 +4,7 @@ FROM ${DOCKER_YII2_PHP_IMAGE} # Project source-code WORKDIR /project ADD composer.* /project/ -# Apply testing patches -ADD tests/phpunit_mock_objects.patch /project/tests/phpunit_mock_objects.patch -ADD tests/phpunit_getopt.patch /project/tests/phpunit_getopt.patch -# Install packgaes +# Install packages RUN /usr/local/bin/composer install --prefer-dist ADD ./ /project ENV PATH /project/vendor/bin:${PATH} diff --git a/build/controllers/MimeTypeController.php b/build/controllers/MimeTypeController.php index 29d8e59e3ca..9da23f67fcd 100644 --- a/build/controllers/MimeTypeController.php +++ b/build/controllers/MimeTypeController.php @@ -49,14 +49,19 @@ class MimeTypeController extends Controller * @var array MIME types to add to the ones parsed from Apache files */ private $additionalMimeTypes = [ + 'apng' => 'image/apng', + 'avif' => 'image/avif', + 'jfif' => 'image/jpeg', 'mjs' => 'text/javascript', + 'pjp' => 'image/jpeg', + 'pjpeg' => 'image/jpeg', ]; /** * @param string $outFile the mime file to update. Defaults to @yii/helpers/mimeTypes.php * @param string $aliasesOutFile the aliases file to update. Defaults to @yii/helpers/mimeAliases.php */ - public function actionIndex($outFile = null, $aliasesOutFile = null) + public function actionIndex($outFile = null, $aliasesOutFile = null, $extensionsOutFile = null) { if ($outFile === null) { $outFile = Yii::getAlias('@yii/helpers/mimeTypes.php'); @@ -66,11 +71,16 @@ public function actionIndex($outFile = null, $aliasesOutFile = null) $aliasesOutFile = Yii::getAlias('@yii/helpers/mimeAliases.php'); } + if ($extensionsOutFile === null) { + $extensionsOutFile = Yii::getAlias('@yii/helpers/mimeExtensions.php'); + } + $this->stdout('Downloading mime-type file from apache httpd repository...'); if ($apacheMimeTypesFileContent = file_get_contents('https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co')) { $this->stdout("Done.\n", Console::FG_GREEN); $this->generateMimeTypesFile($outFile, $apacheMimeTypesFileContent); $this->generateMimeAliasesFile($aliasesOutFile); + $this->generateMimeExtensionsFile($extensionsOutFile, $apacheMimeTypesFileContent); } else { $this->stderr("Failed to download mime.types file from apache SVN.\n"); } @@ -97,8 +107,8 @@ private function generateMimeTypesFile($outFile, $content) } } } - $mimeMap = array_merge($mimeMap, $this->additionalMimeTypes); - ksort($mimeMap); + $mimeMap = array_replace($mimeMap, $this->additionalMimeTypes); + ksort($mimeMap, SORT_STRING); $array = VarDumper::export($mimeMap); $content = <<stdout("done.\n", Console::FG_GREEN); @@ -140,6 +151,67 @@ private function generateMimeAliasesFile($outFile) */ return $array; +EOD; + file_put_contents($outFile, $content); + $this->stdout("done.\n", Console::FG_GREEN); + } + + /** + * @param string $outFile + * @param string $content + */ + private function generateMimeExtensionsFile($outFile, $content) + { + $this->stdout("Generating file $outFile..."); + + $extensionMap = []; + foreach (explode("\n", $content) as $line) { + $line = trim($line); + if (empty($line) || strpos($line, '#') === 0) { // skip comments and empty lines + continue; + } + $parts = preg_split('/\s+/', $line); + $mime = array_shift($parts); + if (!empty($parts)) { + $extensionMap[$mime] = []; + foreach ($parts as $ext) { + if (!empty($ext)) { + $extensionMap[$mime][] = $ext; + } + } + } + } + + foreach ($this->additionalMimeTypes as $ext => $mime) { + if (!array_key_exists($mime, $extensionMap)) { + $extensionMap[$mime] = []; + } + $extensionMap[$mime][] = $ext; + } + + foreach ($extensionMap as $mime => $extensions) { + if (count($extensions) === 1) { + $extensionMap[$mime] = $extensions[0]; + } + } + + ksort($extensionMap, SORT_STRING); + $array = VarDumper::export($extensionMap); + + $content = <<stdout("done.\n", Console::FG_GREEN); diff --git a/build/controllers/ReleaseController.php b/build/controllers/ReleaseController.php index d8398852707..cec8456309c 100644 --- a/build/controllers/ReleaseController.php +++ b/build/controllers/ReleaseController.php @@ -218,10 +218,10 @@ public function actionRelease(array $what) $gitDir = reset($what) === 'framework' ? 'framework/' : ''; $gitVersion = $versions[reset($what)]; if (strncmp('app-', reset($what), 4) !== 0) { - $this->stdout("- no accidentally added CHANGELOG lines for other versions than this one?\n\n git diff $gitVersion.. ${gitDir}CHANGELOG.md\n\n"); + $this->stdout("- no accidentally added CHANGELOG lines for other versions than this one?\n\n git diff $gitVersion.. {$gitDir}CHANGELOG.md\n\n"); $this->stdout("- are all new `@since` tags for this release version?\n"); } - $this->stdout("- other issues with code changes?\n\n git diff -w $gitVersion.. ${gitDir}\n\n"); + $this->stdout("- other issues with code changes?\n\n git diff -w $gitVersion.. {$gitDir}\n\n"); $travisUrl = reset($what) === 'framework' ? '' : '-' . reset($what); $this->stdout("- are unit tests passing on travis? https://travis-ci.com/yiisoft/yii2$travisUrl/builds\n"); $this->stdout("- also make sure the milestone on github is complete and no issues or PRs are left open.\n\n"); diff --git a/composer.json b/composer.json index 93899a17415..8a78d259b26 100644 --- a/composer.json +++ b/composer.json @@ -75,7 +75,7 @@ "yiisoft/yii2-composer": "~2.0.4", "ezyang/htmlpurifier": "^4.6", "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", - "bower-asset/jquery": "3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", + "bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", "bower-asset/inputmask": "~3.2.2 | ~3.3.5", "bower-asset/punycode": "1.3.*", "bower-asset/yii2-pjax": "~2.0.1", diff --git a/composer.lock b/composer.lock index d87852913bd..9f827e28afb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4ee549b10bc158d5dfef9871605e1ae8", + "content-hash": "fc0abacf592fc59d2c2e99a0a746f7ef", "packages": [ { "name": "bower-asset/inputmask", @@ -208,33 +208,29 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.21", + "version": "v9.99.100", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae" + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/96c132c7f2f7bc3230723b66e89f8f150b29d5ae", - "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { - "php": ">=5.2.0" + "php": ">= 7" }, "require-dev": { - "phpunit/phpunit": "*" + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -258,7 +254,7 @@ "issues": "https://github.com/paragonie/random_compat/issues", "source": "https://github.com/paragonie/random_compat" }, - "time": "2022-02-16T17:07:03+00:00" + "time": "2020-10-15T08:29:30+00:00" }, { "name": "yiisoft/yii2-composer", @@ -375,30 +371,102 @@ }, "time": "2014-05-23T14:40:08+00:00" }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" + }, { "name": "composer/semver", - "version": "1.7.2", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/647490bbcaf7fc4891c58f47b825eb99d19c377a", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -437,7 +505,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/1.7.2" + "source": "https://github.com/composer/semver/tree/3.3.2" }, "funding": [ { @@ -453,29 +521,31 @@ "type": "tidelift" } ], - "time": "2020-12-03T15:47:16+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.4.6", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c" + "reference": "ced299686f41dce890debac69273b47ffe98a40c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpstan/phpstan": "^0.12.55", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" }, "type": "library", "autoload": { @@ -501,7 +571,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/1.4.6" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" }, "funding": [ { @@ -517,7 +587,7 @@ "type": "tidelift" } ], - "time": "2021-03-25T17:01:18+00:00" + "time": "2022-02-25T21:32:43+00:00" }, { "name": "cweagans/composer-patches", @@ -569,32 +639,36 @@ }, { "name": "doctrine/annotations", - "version": "v1.4.0", + "version": "1.14.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/ad785217c1e9555a7d6c6c8c9f406395a5e2882b", + "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" + "doctrine/lexer": "^1 || ^2", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "vimeo/psalm": "^4.10" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, + "type": "library", "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" @@ -605,6 +679,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -613,10 +691,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -627,7 +701,7 @@ } ], "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", @@ -635,40 +709,81 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/v1.4.0" + "source": "https://github.com/doctrine/annotations/tree/1.14.2" + }, + "time": "2022-12-15T06:48:22+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" }, - "time": "2017-02-24T16:22:25+00:00" + "time": "2022-05-02T15:47:09+00:00" }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1 || ^8.0" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -682,50 +797,64 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/master" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, - "time": "2015-06-14T21:17:01+00:00" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" }, { "name": "doctrine/lexer", - "version": "1.0.2", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", "shasum": "" }, "require": { - "php": ">=5.3.2" + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5" + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -733,14 +862,14 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -757,61 +886,76 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.0.2" + "source": "https://github.com/doctrine/lexer/tree/2.1.0" }, - "time": "2019-06-08T11:03:04+00:00" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-12-14T08:49:07+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.2.20", + "version": "v3.9.5", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "f1631f0747ad2a9dd3de8d7873b71f6573f8d0c2" + "reference": "4465d70ba776806857a1ac2a6f877e582445ff36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/f1631f0747ad2a9dd3de8d7873b71f6573f8d0c2", - "reference": "f1631f0747ad2a9dd3de8d7873b71f6573f8d0c2", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/4465d70ba776806857a1ac2a6f877e582445ff36", + "reference": "4465d70ba776806857a1ac2a6f877e582445ff36", "shasum": "" }, "require": { - "composer/semver": "^1.4", - "composer/xdebug-handler": "^1.0", - "doctrine/annotations": "^1.2", + "composer/semver": "^3.2", + "composer/xdebug-handler": "^3.0.3", + "doctrine/annotations": "^1.13", "ext-json": "*", "ext-tokenizer": "*", - "php": "^5.3.6 || >=7.0 <7.3", - "sebastian/diff": "^1.4", - "symfony/console": "^2.4 || ^3.0 || ^4.0", - "symfony/event-dispatcher": "^2.1 || ^3.0 || ^4.0", - "symfony/filesystem": "^2.4 || ^3.0 || ^4.0", - "symfony/finder": "^2.2 || ^3.0 || ^4.0", - "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0", - "symfony/polyfill-php54": "^1.0", - "symfony/polyfill-php55": "^1.3", - "symfony/polyfill-php70": "^1.0", - "symfony/polyfill-php72": "^1.4", - "symfony/process": "^2.3 || ^3.0 || ^4.0", - "symfony/stopwatch": "^2.5 || ^3.0 || ^4.0" - }, - "conflict": { - "hhvm": "<3.18" + "php": "^7.4 || ^8.0", + "php-cs-fixer/diff": "^2.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php81": "^1.25", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" }, "require-dev": { - "johnkary/phpunit-speedtrap": "^1.0.1 || ^2.0 || ^3.0", - "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.1", - "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^1.0.2", - "phpunit/phpunit": "^4.8.35 || ^5.4.3", - "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^1.5", + "mikey179/vfsstream": "^1.6.10", + "php-coveralls/php-coveralls": "^2.5.2", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.5", + "phpunitgoodpractices/traits": "^1.9.1", + "symfony/phpunit-bridge": "^6.0", + "symfony/yaml": "^5.4 || ^6.0" }, "suggest": { - "ext-mbstring": "For handling non-UTF8 characters in cache signature.", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", - "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." }, "bin": [ "php-cs-fixer" @@ -820,84 +964,34 @@ "autoload": { "psr-4": { "PhpCsFixer\\": "src/" - }, - "classmap": [ - "tests/Test/AbstractFixerTestCase.php", - "tests/Test/AbstractIntegrationCaseFactory.php", - "tests/Test/AbstractIntegrationTestCase.php", - "tests/Test/IntegrationCase.php", - "tests/Test/IntegrationCaseFactory.php", - "tests/Test/IntegrationCaseFactoryInterface.php", - "tests/Test/InternalIntegrationCaseFactory.php", - "tests/TestCase.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], "description": "A tool to automatically fix PHP code style", "support": { "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/2.2" - }, - "time": "2018-06-02T17:26:04+00:00" - }, - { - "name": "ircmaxell/password-compat", - "version": "v1.0.4", - "source": { - "type": "git", - "url": "https://github.com/ircmaxell/password_compat.git", - "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", - "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "autoload": { - "files": [ - "lib/password.php" - ] + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.9.5" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Anthony Ferrara", - "email": "ircmaxell@php.net", - "homepage": "http://blog.ircmaxell.com" + "url": "https://github.com/keradus", + "type": "github" } ], - "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", - "homepage": "https://github.com/ircmaxell/password_compat", - "keywords": [ - "hashing", - "password" - ], - "support": { - "issues": "https://github.com/ircmaxell/password_compat/issues", - "source": "https://github.com/ircmaxell/password_compat/tree/v1.0" - }, - "time": "2014-11-20T16:49:30+00:00" + "time": "2022-07-22T08:43:51+00:00" }, { "name": "johnkary/phpunit-speedtrap", @@ -952,143 +1046,91 @@ "time": "2017-03-25T17:14:26+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "name": "php-cs-fixer/diff", + "version": "v2.0.2", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "symfony/process": "^3.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "sebastian/diff v3 backport support for PHP 5.6+", + "homepage": "https://github.com/PHP-CS-Fixer", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "diff" ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" + "issues": "https://github.com/PHP-CS-Fixer/diff/issues", + "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" }, - "time": "2017-09-11T18:02:19+00:00" + "abandoned": true, + "time": "2020-10-14T08:32:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.3.2", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" + "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", + "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "php": ">=5.3.3" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/3.x" - }, - "time": "2017-11-10T14:09:06+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" + "phpunit/phpunit": "~4.0" }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ + "psr-0": { + "phpDocumentor": [ "src/" ] } @@ -1100,49 +1142,46 @@ "authors": [ { "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "email": "mike.vanriel@naenius.com" } ], "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x" }, - "time": "2017-07-14T14:27:02+00:00" + "time": "2016-01-25T08:17:30+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.3", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", + "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" + "psr-0": { + "Prophecy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1172,9 +1211,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" + "source": "https://github.com/phpspec/prophecy/tree/master" }, - "time": "2020-03-05T15:02:03+00:00" + "time": "2015-08-13T10:07:40+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1342,30 +1381,25 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "~4|~5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -1388,10 +1422,11 @@ "timer" ], "support": { + "irc": "irc://irc.freenode.net/phpunit", "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/master" }, - "time": "2017-02-26T11:10:40+00:00" + "time": "2016-05-12T18:03:57+00:00" }, { "name": "phpunit/php-token-stream", @@ -1586,31 +1621,31 @@ "time": "2015-10-02T06:51:40+00:00" }, { - "name": "psr/log", - "version": "1.1.4", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1623,7 +1658,159 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", "keywords": [ "log", @@ -1631,9 +1818,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "sebastian/comparator", @@ -1705,23 +1892,23 @@ }, { "name": "sebastian/diff", - "version": "1.4.3", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "~4.8" }, "type": "library", "extra": { @@ -1755,29 +1942,29 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/1.4" + "source": "https://github.com/sebastianbergmann/diff/tree/master" }, - "time": "2017-05-22T07:24:03+00:00" + "time": "2015-12-08T07:14:41+00:00" }, { "name": "sebastian/environment", - "version": "1.3.8", + "version": "1.3.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", + "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { @@ -1809,9 +1996,9 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/1.3.7" }, - "time": "2016-08-18T05:49:44+00:00" + "time": "2016-05-17T03:18:57+00:00" }, { "name": "sebastian/exporter", @@ -2037,37 +2224,43 @@ }, { "name": "symfony/console", - "version": "v3.4.47", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" + "reference": "0f579613e771dba2dbb8211c382342a641f5da06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", - "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "url": "https://api.github.com/repos/symfony/console/zipball/0f579613e771dba2dbb8211c382342a641f5da06", + "reference": "0f579613e771dba2dbb8211c382342a641f5da06", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" }, "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" }, "suggest": { "psr/log": "For using the console logger", @@ -2098,10 +2291,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], "support": { - "source": "https://github.com/symfony/console/tree/v3.4.47" + "source": "https://github.com/symfony/console/tree/v6.2.3" }, "funding": [ { @@ -2117,39 +2316,38 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-12-28T14:26:22+00:00" }, { - "name": "symfony/debug", - "version": "v3.4.47", + "name": "symfony/deprecation-contracts", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", - "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" + "php": ">=8.1" }, "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" }, - "exclude-from-classmap": [ - "/Tests/" + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2158,18 +2356,18 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Debug Component", + "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug/tree/v3.4.47" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { @@ -2185,36 +2383,42 @@ "type": "tidelift" } ], - "abandoned": "symfony/error-handler", - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.47", + "version": "v6.2.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "31fde73757b6bad247c54597beef974919ec6860" + "reference": "3ffeb31139b49bf6ef0bc09d1db95eac053388d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/31fde73757b6bad247c54597beef974919ec6860", - "reference": "31fde73757b6bad247c54597beef974919ec6860", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3ffeb31139b49bf6ef0bc09d1db95eac053388d1", + "reference": "3ffeb31139b49bf6ef0bc09d1db95eac053388d1", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2|^3" }, "conflict": { - "symfony/dependency-injection": "<3.3" + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/debug": "~3.4|~4.4", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" }, "suggest": { "symfony/dependency-injection": "", @@ -2243,10 +2447,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v3.4.47" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.2" }, "funding": [ { @@ -2262,25 +2466,105 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-12-14T16:11:27+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0782b0b52a737a05b4383d0df35a474303cabdae", + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/filesystem", - "version": "v3.4.47", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3" + "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e58d7841cddfed6e846829040dca2cca0ebbbbb3", - "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/50b2523c874605cf3d4acf7a9e2b30b6a440a016", + "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "type": "library", "autoload": { @@ -2305,10 +2589,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v3.4.47" + "source": "https://github.com/symfony/filesystem/tree/v6.2.0" }, "funding": [ { @@ -2324,24 +2608,27 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-11-20T13:01:27+00:00" }, { "name": "symfony/finder", - "version": "v3.4.47", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e" + "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", - "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", + "url": "https://api.github.com/repos/symfony/finder/zipball/81eefbddfde282ee33b437ba5e13d7753211ae8e", + "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" }, "type": "library", "autoload": { @@ -2366,10 +2653,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v3.4.47" + "source": "https://github.com/symfony/finder/tree/v6.2.3" }, "funding": [ { @@ -2385,24 +2672,25 @@ "type": "tidelift" } ], - "time": "2020-11-16T17:02:08+00:00" + "time": "2022-12-22T17:55:15+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.4.47", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744" + "reference": "d28f02acde71ff75e957082cd36e973df395f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744", - "reference": "c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d28f02acde71ff75e957082cd36e973df395f626", + "reference": "d28f02acde71ff75e957082cd36e973df395f626", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", "autoload": { @@ -2427,7 +2715,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony OptionsResolver Component", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ "config", @@ -2435,7 +2723,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v3.4.47" + "source": "https://github.com/symfony/options-resolver/tree/v6.2.0" }, "funding": [ { @@ -2451,24 +2739,27 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-11-02T09:08:04+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.19.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b", - "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" }, "suggest": { "ext-ctype": "For best performance" @@ -2476,7 +2767,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.19-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2514,7 +2805,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -2530,32 +2821,32 @@ "type": "tidelift" } ], - "time": "2020-10-23T09:01:57+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.19.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b5f7b932ee6fa802fc792eabd77c4c88084517ce", - "reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { - "ext-mbstring": "For best performance" + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.19-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2567,7 +2858,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2584,17 +2875,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", + "grapheme", + "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.19.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { @@ -2610,29 +2902,32 @@ "type": "tidelift" } ], - "time": "2020-10-23T09:01:57+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php54", - "version": "v1.19.0", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "c248bab30dad46a5f3917e7d92907e148bdc50c6" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/c248bab30dad46a5f3917e7d92907e148bdc50c6", - "reference": "c248bab30dad46a5f3917e7d92907e148bdc50c6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.19-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2644,7 +2939,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php54\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, "classmap": [ "Resources/stubs" @@ -2664,16 +2959,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "intl", + "normalizer", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php54/tree/v1.19.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { @@ -2689,30 +2986,35 @@ "type": "tidelift" } ], - "time": "2020-10-23T09:01:57+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php55", - "version": "v1.19.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "248a5c9877b126493abb661e4fb47792e418035b" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/248a5c9877b126493abb661e4fb47792e418035b", - "reference": "248a5c9877b126493abb661e4fb47792e418035b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { - "ircmaxell/password-compat": "~1.0", - "php": ">=5.3.3" + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.19-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2724,7 +3026,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php55\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2741,16 +3043,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php55/tree/v1.19.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -2766,30 +3069,29 @@ "type": "tidelift" } ], - "time": "2020-10-23T09:01:57+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php70", - "version": "v1.19.0", + "name": "symfony/polyfill-php80", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3fe414077251a81a1b15b1c709faf5c2fbae3d4e", - "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.19-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2801,7 +3103,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "classmap": [ "Resources/stubs" @@ -2812,6 +3114,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -2821,7 +3127,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -2830,7 +3136,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php70/tree/v1.19.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -2846,29 +3152,29 @@ "type": "tidelift" } ], - "time": "2020-10-23T09:01:57+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.19.0", + "name": "symfony/polyfill-php81", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "beecef6b463b06954638f02378f52496cb84bacc" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/beecef6b463b06954638f02378f52496cb84bacc", - "reference": "beecef6b463b06954638f02378f52496cb84bacc", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.19-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2880,8 +3186,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2897,7 +3206,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -2906,7 +3215,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.19.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" }, "funding": [ { @@ -2922,24 +3231,24 @@ "type": "tidelift" } ], - "time": "2020-10-23T09:01:57+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", - "version": "v3.4.47", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca" + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca", - "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca", + "url": "https://api.github.com/repos/symfony/process/zipball/ba6e55359f8f755fe996c58a81e00eaa67a35877", + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -2964,10 +3273,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v3.4.47" + "source": "https://github.com/symfony/process/tree/v6.2.0" }, "funding": [ { @@ -2983,24 +3292,110 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-11-02T09:08:04+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.4.47", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "298b81faad4ce60e94466226b2abbb8c9bca7462" + "reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/298b81faad4ce60e94466226b2abbb8c9bca7462", - "reference": "298b81faad4ce60e94466226b2abbb8c9bca7462", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/266636bb8f3fbdccc302491df7b3a1b9a8c238a7", + "reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.1", + "symfony/service-contracts": "^1|^2|^3" }, "type": "library", "autoload": { @@ -3025,10 +3420,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Stopwatch Component", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v3.4.47" + "source": "https://github.com/symfony/stopwatch/tree/v6.2.0" }, "funding": [ { @@ -3044,39 +3439,46 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-09-28T16:00:52+00:00" }, { - "name": "symfony/yaml", - "version": "v3.4.47", + "name": "symfony/string", + "version": "v6.2.2", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "88289caa3c166321883f67fe5130188ebbb47094" + "url": "https://github.com/symfony/string.git", + "reference": "863219fd713fa41cbcd285a79723f94672faff4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", - "reference": "88289caa3c166321883f67fe5130188ebbb47094", + "url": "https://api.github.com/repos/symfony/string/zipball/863219fd713fa41cbcd285a79723f94672faff4d", + "reference": "863219fd713fa41cbcd285a79723f94672faff4d", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/console": "<3.4" + "symfony/translation-contracts": "<2.0" }, "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Component\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3088,18 +3490,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], "support": { - "source": "https://github.com/symfony/yaml/tree/v3.4.47" + "source": "https://github.com/symfony/string/tree/v6.2.2" }, "funding": [ { @@ -3115,38 +3525,43 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2022-12-14T16:11:27+00:00" }, { - "name": "webmozart/assert", - "version": "1.9.1", + "name": "symfony/yaml", + "version": "v3.4.47", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "url": "https://github.com/symfony/yaml.git", + "reference": "88289caa3c166321883f67fe5130188ebbb47094" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", + "reference": "88289caa3c166321883f67fe5130188ebbb47094", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", - "symfony/polyfill-ctype": "^1.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "symfony/console": "<3.4" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" }, "type": "library", "autoload": { "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3154,21 +3569,34 @@ ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.9.1" + "source": "https://github.com/symfony/yaml/tree/v3.4.47" }, - "time": "2020-07-08T17:02:28+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" } ], "aliases": [], @@ -3185,8 +3613,5 @@ "lib-pcre": "*" }, "platform-dev": [], - "platform-overrides": { - "php": "5.6" - }, "plugin-api-version": "2.3.0" } diff --git a/docs/guide-es/README.md b/docs/guide-es/README.md index 80d5266fe87..c432298bb50 100644 --- a/docs/guide-es/README.md +++ b/docs/guide-es/README.md @@ -117,7 +117,7 @@ Seguridad * **TBD** [Autenticación](security-authentication.md) * **TBD** [Autorización](security-authorization.md) * **TBD** [Trabajar con contraseñas](security-passwords.md) -* **TBD** [Autenticar Clientes](security-auth-clients.md) +* [Autenticar Clientes](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide) * **TBD** [Buenas prácticas](security-best-practices.md) diff --git a/docs/guide-pt-BR/translators.json b/docs/guide-pt-BR/translators.json index cc52faec676..0a0ae20fddf 100644 --- a/docs/guide-pt-BR/translators.json +++ b/docs/guide-pt-BR/translators.json @@ -7,5 +7,6 @@ "Lucas Barros", "Raphael de Almeida", "Sidney da Silva Lins", - "Wanderson Bragança" + "Wanderson Bragança", + "Anthony Tesche" ] diff --git a/docs/guide-pt-BR/tutorial-docker.md b/docs/guide-pt-BR/tutorial-docker.md new file mode 100644 index 00000000000..8d26b95bb65 --- /dev/null +++ b/docs/guide-pt-BR/tutorial-docker.md @@ -0,0 +1,103 @@ +Yii e Docker +============== + +Para o desenvolvimento e implantação de aplicativos Yii, eles podem ser executados como contêineres Docker. Um contêiner é como uma máquina virtual isolada e leve que mapeia seus serviços para as portas do host, ou seja, um servidor da web em um contêiner na porta 80 está disponível na porta 8888 do seu (local) host. + +Os contêineres podem resolver muitos problemas, como ter versões idênticas de software no computador do desenvolvedor e no servidor, implantações rápidas ou simulação de arquitetura multi-servidor durante o desenvolvimento. + +Você pode ler mais sobre contêineres Docker em [docker.com](https://www.docker.com/why-docker). + +## Requisitos + +- `docker` +- `docker-compose` + +Visite a [página de download](https://www.docker.com/products/container-runtime) para obter as ferramentas do Docker. + +## Instalação + +Após a instalação, você deve ser capaz de executar o comando docker ps e ver uma saída semelhante a esta: + +``` +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +``` + +Isso significa que o seu daemon Docker está em execução. + +Além disso, execute o comando docker-compose version, a saída deve ser semelhante a esta: + +``` +docker-compose version 1.20.0, build unknown +docker-py version: 3.1.3 +CPython version: 3.6.4 +OpenSSL version: OpenSSL 1.1.0g 2 Nov 2017 +``` + +Com o Compose, você pode configurar e gerenciar todos os serviços necessários para a sua aplicação, como bancos de dados e cache. + +## Recursos + +- As imagens base do PHP para Yii podem ser encontradas em [yii2-docker](https://github.com/yiisoft/yii2-docker) +- Suporte do Docker para [yii2-app-basic](https://github.com/yiisoft/yii2-app-basic#install-with-docker) +- Suporte do Docker para [yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced/pull/347) está em desenvolvimento + +## Uso + +Os comandos básicos do Docker são + + docker-compose up -d + +para iniciar todos os serviços em sua pilha, em segundo plano + + docker-compose ps + +para listar os serviços em execução + + docker-compose logs -f + +para visualizar os logs de todos os serviços continuamente + + docker-compose stop + +para interromper todos os serviços em sua pilha de forma elegante + + docker-compose kill + +para interromper todos os serviços em sua pilha imediatamente + + docker-compose down -v + +para parar e remover todos os serviços, **atenção à perda de dados ao não usar volumes do host** + +Para executar comandos em um contêiner: + + docker-compose run --rm php composer install + +executa a instalação do Composer em um novo contêiner + + docker-compose exec php bash + +executa um shell bash em um serviço php que está em *execução*. + + +## Tópicos avançados + +### Testes do framework Yii + +Você pode executar os testes do framework Yii em um contêiner Docker, conforme descrito [aqui](https://github.com/yiisoft/yii2/blob/master/tests/README.md#dockerized-testing). + +### Database administration tools + +Ao executar o MySQL como (`mysql`), você pode adicionar um contêiner do phpMyAdmin à sua pilha, como mostrado abaixo: + +``` + phpmyadmin: + image: phpmyadmin/phpmyadmin + ports: + - '8888:80' + environment: + - PMA_ARBITRARY=1 + - PMA_HOST=mysql + depends_on: + - mysql +``` diff --git a/docs/guide-ru/db-query-builder.md b/docs/guide-ru/db-query-builder.md index 41b164124e6..50bf94771ef 100644 --- a/docs/guide-ru/db-query-builder.md +++ b/docs/guide-ru/db-query-builder.md @@ -373,7 +373,7 @@ $query->orderBy([ ``` В данном коде, ключи массива - это имена столбцов, а значения массива - это соответствующее направление сортировки. -PHP константа `SORT_ASC` определяет сортировку по возрастанию и `SORT_DESC` сортировка по умолчанию. +PHP константа `SORT_ASC` определяет сортировку по возрастанию и `SORT_DESC` сортировку по убыванию. Если `ORDER BY` содержит только простые имена столбцов, вы можете определить их с помощью столбцов, также как и при написании обычного SQL. Например, diff --git a/docs/guide-ru/runtime-bootstrapping.md b/docs/guide-ru/runtime-bootstrapping.md index 0222473af58..4e74ac8c0c2 100644 --- a/docs/guide-ru/runtime-bootstrapping.md +++ b/docs/guide-ru/runtime-bootstrapping.md @@ -10,7 +10,7 @@ В конструкторе приложения происходит следующий процесс предзагрузки: -1. Вызывается метод [[yii\base\Application::preInit()|preInit()]], которые конфигурирует свойства приложения, имеющие +1. Вызывается метод [[yii\base\Application::preInit()|preInit()]], который конфигурирует свойства приложения, имеющие наивысший приоритет, такие как [[yii\base\Application::basePath|basePath]]; 2. Регистрируется [[yii\base\Application::errorHandler|обработчик ошибок]]; 3. Происходит инициализация свойств приложения согласно заданной конфигурации; diff --git a/docs/guide/caching-data.md b/docs/guide/caching-data.md index f17b4a07c8c..1c712e801d0 100644 --- a/docs/guide/caching-data.md +++ b/docs/guide/caching-data.md @@ -82,6 +82,8 @@ and accessible. The following code shows how to configure the `cache` applicatio You can then access the above cache component using the expression `Yii::$app->cache`. +If no cache component is specified, then Yii will use [yii\caching\FileCache](https://www.yiiframework.com/doc/api/2.0/yii-caching-filecache) as default. + Because all cache components support the same set of APIs, you can swap the underlying cache component with a different one by reconfiguring it in the application configuration without modifying the code that uses the cache. For example, you can modify the above configuration to use [[yii\caching\ApcCache|APC cache]]: diff --git a/docs/guide/security-best-practices.md b/docs/guide/security-best-practices.md index bafede12ae1..291575c846f 100644 --- a/docs/guide/security-best-practices.md +++ b/docs/guide/security-best-practices.md @@ -263,6 +263,12 @@ Further reading on the topic: - +Avoiding arbitrary object instantiations +---------------------------------------- + +Yii [configurations](concept-configurations.md) are associative arrays used by the framework to instantiate new objects through `Yii::createObject($config)`. These arrays specify the class name for instantiation, and it is important to ensure that this class name does not originate from untrusted sources. Otherwise, it can lead to Unsafe Reflection, a vulnerability that allows the execution of malicious code by exploiting the loading of specific classes. Additionally, when you need to dynamically add keys to an object derived from a framework class, such as the base `Component` class, it's essential to validate these dynamic properties using a whitelist approach. This precaution is necessary because the framework might employ `Yii::createObject($config)` within the `__set()` magic method. + + Avoiding file exposure ---------------------- diff --git a/docs/guide/structure-models.md b/docs/guide/structure-models.md index 928df70ab62..db8c1361f71 100644 --- a/docs/guide/structure-models.md +++ b/docs/guide/structure-models.md @@ -293,6 +293,8 @@ public function rules() // username and password are required in "login" scenario [['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN], + + [['username'], 'string'], // username must always be a string, this rule applies to all scenarios ]; } ``` diff --git a/framework/BaseYii.php b/framework/BaseYii.php index efc453add05..496f6aa121a 100644 --- a/framework/BaseYii.php +++ b/framework/BaseYii.php @@ -68,7 +68,7 @@ class BaseYii */ public static $classMap = []; /** - * @var \yii\console\Application|\yii\web\Application|\yii\base\Application the application instance + * @var \yii\console\Application|\yii\web\Application the application instance */ public static $app; /** @@ -93,7 +93,7 @@ class BaseYii */ public static function getVersion() { - return '2.0.48-dev'; + return '2.0.50-dev'; } /** diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 1cb31c08dcb..4459fe7741a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -1,27 +1,77 @@ Yii Framework 2 Change Log ========================== -2.0.48 under development +2.0.50 under development ------------------------ -- Enh #19766: Add support for PHP generators to JSON helper (vladis84) -- Bug #19683: Updated `framework\mimeType.php` to the actual value. Fix typo in `build/controllers/MimeTypeController.php` (DeryabinSergey) -- Bug #19705: Add binary and other data type to `$typeMap` list for MySQL (sohelahmed7) -- Enh #19741: Added option to use a closure for `$variations` definition in `yii\filters\PageCache` (nadar) +- Bug #19925: Improved PHP version check when handling MIME types (schmunk42) +- Bug #19940: File Log writer without newline (terabytesoftw) +- Bug #19951: Removed unneeded MIME file tests (schmunk42) +- Bug #19950: Fix `Query::groupBy(null)` causes error for PHP 8.1: `trim(): Passing null to parameter #1 ($string) of type string is deprecated` (uaoleg) + + +2.0.49 August 29, 2023 +---------------------- + +- Bug #9899: Fix caching a MSSQL query with BLOB data type (terabytesoftw) +- Bug #16208: Fix `yii\log\FileTarget` to not export empty messages (terabytesoftw) +- Bug #18859: Fix `yii\web\Controller::bindInjectedParams()` to not throw error when argument of `ReflectionUnionType` type is passed (bizley) +- Bug #19857: Fix AttributeTypecastBehavior::resetOldAttributes() causes "class has no attribute named" InvalidArgumentException (uaoleg) +- Bug #19868: Added whitespace sanitation for tests, due to updates in ICU 72 (schmunk42) +- Bug #19872: Fixed the definition of dirty attributes in AR properties for a non-associative array in case of changing the order of elements (eegusakov) +- Bug #19899: Fixed `GridView` in some cases calling `Model::generateAttributeLabel()` to generate label values that are never used (PowerGamer1) +- Bug #19906: Fixed multiline strings in the `\yii\console\widgets\Table` widget (rhertogh) +- Bug #19908: Fix associative array cell content rendering in Table widget (rhertogh) +- Bug #19911: Resolved inconsistency in `ActiveRecord::getAttributeLabel()` with regard of overriding in primary model labels for attributes of related model in favor of allowing such overriding for all levels of relation nesting (PowerGamer1) +- Bug #19914: Fixed `ArrayHelper::keyExists()` and `::remove()` functions when the key is a float and the value is `null` (rhertogh) +- Bug #19924: Fix `yii\i18n\Formatter` to not throw error `Unknown named parameter` under PHP 8 (arollmann) +- Enh #19841: Allow jQuery 3.7 to be installed (wouter90) +- Enh #19853: Added support for default value for `\yii\helpers\Console::select()` (rhertogh) +- Enh #19884: Added support Enums in Query Builder (sk1t0n) +- Enh #19920: Broadened the accepted type of `Cookie::$expire` from `int` to `int|string|\DateTimeInterface|null` (rhertogh) + + +2.0.48.1 May 24, 2023 +--------------------- + +- Bug #19847: Fix regression introduced in #15376 that caused `DbManager::getRolesByUser()` to return stale data (michaelarnauts) + + +2.0.48 May 22, 2023 +------------------- + - Bug #15376: Added $userId for RBAC roles cache (manchenkoff) +- Bug #17194: Fix unnecessary SQL updates in the database on attributes typecast via `yii\behaviors\AttributeTypecastBehavior` (aivchen) - Bug #18867: Fixed multiple issues with `yii\grid\CheckboxColumn`: "check all" checkbox not being checked on page load when all data row checkboxes are initially checked; clicking checkboxes triggered "change" event for other checkboxes that do not change their state; "check all" checkbox not being checked when disabled checkboxes are present and clicking last non-checked data row checkbox (PowerGamer1) - Bug #19635: PHP 8.2 compatibility fix for `yii\validators\DateValidator` (PowerGamer1) -- Bug #17194: Fix unnecessary SQL updates in the database on attributes typecast via `yii\behaviors\AttributeTypecastBehavior` (aivchen) +- Bug #19683: Updated `framework\mimeType.php` to the actual value. Fix typo in `build/controllers/MimeTypeController.php` (DeryabinSergey) - Bug #19693: Fix db/Command not caching `NULL` result with scalar fetchMode (Arkeins) -- Enh #15376: Added cache usage for `yii\rbac\DbManager::getRolesByUser()` (manchenkoff) -- Enh #9740: Usage of DI instead of new keyword in Schemas (manchenkoff) -- Enh #19689: Remove empty elements from the `class` array in `yii\helpers\BaseHtml::renderTagAttributes()` to prevent unwanted spaces (MoritzLost) -- Chg #19696: Change visibility of `yii\web\View::isPageEnded` to `protected` (lubosdz, samdark) +- Bug #19705: Add binary and other data types to `$typeMap` list for MySQL (sohelahmed7) - Bug #19712: Cast shell_exec() output to string for jsCompressor (impayru) +- Bug #19720: Fix "zh-HK" locale causing [error][yii\i18n\PhpMessageSource::loadFallbackMessages] The message file for category 'yii' doesn't exist (uaoleg) +- Bug #19731: Fix `yii\data\Sort` to generate a proper link when multisort is on and attribute has a default sort order set (bizley) - Bug #19734: PHP 8.1 compatibility fix for `$query->orderBy(null)` (uaoleg) -- Bug #19731: Fix `yii\data\Sort` to generate proper link when multisort is on and attribute has a default sort order set (bizley) -- Bug #19735: Fix `yii\validators\NumberValidator` to use programmable message for the value validation (bizley) +- Bug #19735: Fix `yii\validators\NumberValidator` to use a programmable message for the value validation (bizley) +- Bug #19736: Fix `StringHelper::truncate(null, 10)` causes error `Deprecated: mb_strlen(): Passing null to parameter #1 ($string) of type string is deprecated` (uaoleg) +- Bug #19743: Non-associative array values in AR weren't considered dirty when reordered (samdark) +- Bug #19749: Add PHP 8.2 support (samdark, schmunk42, aldok10, DanaLuther) - Bug #19770: Fix `yii\mutex\MysqlMutex` `keyPrefix` expression param binding (kamarton) +- Bug #19795: Fix `yii\web\Response::redirect()` to prevent setting headers with URL containing new line character (bizley) +- Bug #19807: Fix REST serializer not using `serializeModel()` when working with an array of models (zucha) +- Bug #19813: Fix `yii\base\DynamicModel` validation with validators that reference missing attributes (michaelarnauts) +- Bug #19828: Fix "strtr(): Passing null to parameter #1 ($string) of type string is deprecated" (uaoleg) +- Bug #19837: Fixed processing of numeric file extensions in `yii\build\controllers\MimeTypeController::generateMimeTypesFile()` (rhertogh) +- Enh #9740: Usage of DI instead of new keyword in Schemas (manchenkoff) +- Enh #15376: Added cache usage for `yii\rbac\DbManager::getRolesByUser()` (manchenkoff) +- Enh #19689: Remove empty elements from the `class` array in `yii\helpers\BaseHtml::renderTagAttributes()` to prevent unwanted spaces (MoritzLost) +- Enh #19741: Added option to use a closure for `$variations` definition in `yii\filters\PageCache` (nadar) +- Enh #19766: Add support for PHP generators to JSON helper (vladis84) +- Enh #19794: Add caching in `yii\web\Request` for `getUserIP()` and `getSecureForwardedHeaderTrustedParts()` (rhertogh) +- Enh #19804: Remove the unnecessary call to `$this->oldAttributes` in `BaseActiveRecord::getDirtyAttributes()` (thiagotalma) +- Enh #19816: Explicitly pass `$fallbackToMaster` as `true` to `getSlavePdo()` to ensure it isn't affected by child class with changed defaults (developedsoftware) +- Enh #19838: Added `yii\helpers\BaseFileHelper::getExtensionByMimeType()` to get the most common extension for a given MIME type (rhertogh) +- Chg #19696: Change visibility of `yii\web\View::isPageEnded` to `protected` (lubosdz, samdark) + 2.0.47 November 18, 2022 ------------------------ diff --git a/framework/UPGRADE.md b/framework/UPGRADE.md index 898f77c6944..3a9569a9822 100644 --- a/framework/UPGRADE.md +++ b/framework/UPGRADE.md @@ -52,6 +52,17 @@ version B between A and C, you need to follow the instructions for both A and B. +Upgrade from Yii 2.0.48 +----------------------- + +* Since Yii 2.0.49 the `yii\console\Controller::select()` function supports a default value and respects + the `yii\console\Controller::$interactive` setting. Before the user was always prompted to select an option + regardless of the `$interactive` setting. Now the `$default` value is automatically returned when `$interactive` is + `false`. +* The function signature for `yii\console\Controller::select()` and `yii\helpers\BaseConsole::select()` have changed. + They now have an additional `$default = null` parameter. In case those methods are overwritten you will need to + update your child classes accordingly. + Upgrade from Yii 2.0.46 ----------------------- @@ -89,6 +100,11 @@ Upgrade from Yii 2.0.45 2.0.45 behavior, [introduce your own method](https://github.com/yiisoft/yii2/pull/19495/files). * `yii\log\FileTarget::$rotateByCopy` is now deprecated and setting it to `false` has no effect since rotating of the files is done only by copy. +* `yii\validators\UniqueValidator` and `yii\validators\ExistValidator`, when used on multiple attributes, now only + generate an error on a single attribute. Previously, they would report a separate error on each attribute. + Old behavior can be achieved by setting `'skipOnError' => false`, but this might have undesired side effects with + additional validators on one of the target attributes. + See [issue #19407](https://github.com/yiisoft/yii2/issues/19407) Upgrade from Yii 2.0.44 ----------------------- diff --git a/framework/base/Configurable.php b/framework/base/Configurable.php index 9982d6f627c..b44a082841e 100644 --- a/framework/base/Configurable.php +++ b/framework/base/Configurable.php @@ -15,7 +15,7 @@ * like the following: * * ```php - * public function __constructor($param1, $param2, ..., $config = []) + * public function __construct($param1, $param2, ..., $config = []) * ``` * * That is, the last parameter of the constructor must accept a configuration array. diff --git a/framework/base/DynamicModel.php b/framework/base/DynamicModel.php index 027da6d1982..9ce466b8fa3 100644 --- a/framework/base/DynamicModel.php +++ b/framework/base/DynamicModel.php @@ -201,6 +201,7 @@ public function addRule($attributes, $validator, $options = []) } $validators->append($validator); + $this->defineAttributesByValidator($validator); return $this; } @@ -223,9 +224,11 @@ public static function validateData(array $data, $rules = []) foreach ($rules as $rule) { if ($rule instanceof Validator) { $validators->append($rule); + $model->defineAttributesByValidator($rule); } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type $validator = Validator::createValidator($rule[1], $model, (array)$rule[0], array_slice($rule, 2)); $validators->append($validator); + $model->defineAttributesByValidator($validator); } else { throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); } @@ -237,6 +240,19 @@ public static function validateData(array $data, $rules = []) return $model; } + /** + * Define the attributes that applies to the specified Validator. + * @param Validator $validator the validator whose attributes are to be defined. + */ + private function defineAttributesByValidator($validator) + { + foreach ($validator->getAttributeNames() as $attribute) { + if (!$this->hasAttribute($attribute)) { + $this->defineAttribute($attribute); + } + } + } + /** * {@inheritdoc} */ diff --git a/framework/behaviors/AttributeTypecastBehavior.php b/framework/behaviors/AttributeTypecastBehavior.php index fc3ce955949..a418b15de25 100644 --- a/framework/behaviors/AttributeTypecastBehavior.php +++ b/framework/behaviors/AttributeTypecastBehavior.php @@ -366,7 +366,10 @@ public function afterFind($event) $this->resetOldAttributes(); } - private function resetOldAttributes() + /** + * Resets the old values of the named attributes. + */ + protected function resetOldAttributes() { if ($this->attributeTypes === null) { return; @@ -375,7 +378,9 @@ private function resetOldAttributes() $attributes = array_keys($this->attributeTypes); foreach ($attributes as $attribute) { - $this->owner->setOldAttribute($attribute, $this->owner->{$attribute}); + if ($this->owner->canSetOldAttribute($attribute)) { + $this->owner->setOldAttribute($attribute, $this->owner->{$attribute}); + } } } } diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php index 5bf3b02dedd..d707ba41a27 100644 --- a/framework/caching/DbCache.php +++ b/framework/caching/DbCache.php @@ -317,7 +317,7 @@ protected function isVarbinaryDataField() */ protected function getDataFieldName() { - return $this->isVarbinaryDataField() ? 'convert(nvarchar(max),[data]) data' : 'data'; + return $this->isVarbinaryDataField() ? 'CONVERT(VARCHAR(MAX), [[data]]) data' : 'data'; } /** diff --git a/framework/classes.php b/framework/classes.php index 439f757ce0b..46741e3180d 100644 --- a/framework/classes.php +++ b/framework/classes.php @@ -345,9 +345,9 @@ 'yii\validators\RequiredValidator' => YII2_PATH . '/validators/RequiredValidator.php', 'yii\validators\SafeValidator' => YII2_PATH . '/validators/SafeValidator.php', 'yii\validators\StringValidator' => YII2_PATH . '/validators/StringValidator.php', + 'yii\validators\TrimValidator' => YII2_PATH . '/validators/TrimValidator.php', 'yii\validators\UniqueValidator' => YII2_PATH . '/validators/UniqueValidator.php', 'yii\validators\UrlValidator' => YII2_PATH . '/validators/UrlValidator.php', - 'yii\validators\TrimValidator' => YII2_PATH . '/validators/TrimValidator.php', 'yii\validators\ValidationAsset' => YII2_PATH . '/validators/ValidationAsset.php', 'yii\validators\Validator' => YII2_PATH . '/validators/Validator.php', 'yii\web\Application' => YII2_PATH . '/web/Application.php', diff --git a/framework/composer.json b/framework/composer.json index aaafa056878..5494cae7a24 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -68,9 +68,9 @@ "ext-ctype": "*", "lib-pcre": "*", "yiisoft/yii2-composer": "~2.0.4", - "ezyang/htmlpurifier": "~4.6", + "ezyang/htmlpurifier": "^4.6", "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", - "bower-asset/jquery": "3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", + "bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", "bower-asset/inputmask": "~3.2.2 | ~3.3.5", "bower-asset/punycode": "1.3.*", "bower-asset/yii2-pjax": "~2.0.1", diff --git a/framework/console/Controller.php b/framework/console/Controller.php index 614241d0f31..579b5977830 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -28,8 +28,8 @@ * where `` is a route to a controller action and the params will be populated as properties of a command. * See [[options()]] for details. * - * @property-read string $help The help information for this controller. - * @property-read string $helpSummary The one-line short summary describing this controller. + * @property-read string $help + * @property-read string $helpSummary * @property-read array $passedOptionValues The properties corresponding to the passed options. * @property-read array $passedOptions The names of the options passed during execution. * @@ -400,12 +400,19 @@ public function confirm($message, $default = false) * * @param string $prompt the prompt message * @param array $options Key-value array of options to choose from + * @param string|null $default value to use when the user doesn't provide an option. + * If the default is `null`, the user is required to select an option. * * @return string An option character the user chose + * @since 2.0.49 Added the $default argument */ - public function select($prompt, $options = []) + public function select($prompt, $options = [], $default = null) { - return Console::select($prompt, $options); + if ($this->interactive) { + return Console::select($prompt, $options, $default); + } + + return $default; } /** diff --git a/framework/console/widgets/Table.php b/framework/console/widgets/Table.php index 4862e88b6be..c0ece4d4d89 100644 --- a/framework/console/widgets/Table.php +++ b/framework/console/widgets/Table.php @@ -136,7 +136,11 @@ public function setRows(array $rows) { $this->rows = array_map(function($row) { return array_map(function($value) { - return empty($value) && !is_numeric($value) ? ' ' : $value; + return empty($value) && !is_numeric($value) + ? ' ' + : (is_array($value) + ? array_values($value) + : $value); }, array_values($row)); }, $rows); return $this; @@ -252,18 +256,32 @@ protected function renderRow(array $row, $spanLeft, $spanMiddle, $spanRight) if ($index !== 0) { $buffer .= $spanMiddle . ' '; } + + $arrayFromMultilineString = false; + if (is_string($cell)) { + $cellLines = explode(PHP_EOL, $cell); + if (count($cellLines) > 1) { + $cell = $cellLines; + $arrayFromMultilineString = true; + } + } + if (is_array($cell)) { if (empty($renderedChunkTexts[$index])) { $renderedChunkTexts[$index] = ''; $start = 0; - $prefix = $this->listPrefix; + $prefix = $arrayFromMultilineString ? '' : $this->listPrefix; if (!isset($arrayPointer[$index])) { $arrayPointer[$index] = 0; } } else { $start = mb_strwidth($renderedChunkTexts[$index], Yii::$app->charset); } - $chunk = Console::ansiColorizedSubstr($cell[$arrayPointer[$index]], $start, $cellSize - 4); + $chunk = Console::ansiColorizedSubstr( + $cell[$arrayPointer[$index]], + $start, + $cellSize - 2 - Console::ansiStrwidth($prefix) + ); $renderedChunkTexts[$index] .= Console::stripAnsiFormat($chunk); $fullChunkText = Console::stripAnsiFormat($cell[$arrayPointer[$index]]); if (isset($cell[$arrayPointer[$index] + 1]) && $renderedChunkTexts[$index] === $fullChunkText) { @@ -339,6 +357,9 @@ protected function calculateRowsSize() if (is_array($val)) { return max(array_map('yii\helpers\Console::ansiStrwidth', $val)) + Console::ansiStrwidth($this->listPrefix); } + if (is_string($val)) { + return max(array_map('yii\helpers\Console::ansiStrwidth', explode(PHP_EOL, $val))); + } return Console::ansiStrwidth($val); }, $column)) + 2; $this->columnWidths[] = $columnWidth; @@ -388,6 +409,9 @@ protected function calculateRowHeight($row) if (is_array($val)) { return array_map('yii\helpers\Console::ansiStrwidth', $val); } + if (is_string($val)) { + return array_map('yii\helpers\Console::ansiStrwidth', explode(PHP_EOL, $val)); + } return Console::ansiStrwidth($val); }, $row)); return max($rowsPerCell); diff --git a/framework/data/ActiveDataProvider.php b/framework/data/ActiveDataProvider.php index a8b6e158ecf..3a129aa7293 100644 --- a/framework/data/ActiveDataProvider.php +++ b/framework/data/ActiveDataProvider.php @@ -183,15 +183,11 @@ public function setSort($value) $sort->attributes[$attribute] = [ 'asc' => [$attribute => SORT_ASC], 'desc' => [$attribute => SORT_DESC], - 'label' => $model->getAttributeLabel($attribute), ]; } - } else { - foreach ($sort->attributes as $attribute => $config) { - if (!isset($config['label'])) { - $sort->attributes[$attribute]['label'] = $model->getAttributeLabel($attribute); - } - } + } + if ($sort->modelClass === null) { + $sort->modelClass = $modelClass; } } } diff --git a/framework/data/Sort.php b/framework/data/Sort.php index 1ed63f4ac87..0c0abc8b606 100644 --- a/framework/data/Sort.php +++ b/framework/data/Sort.php @@ -191,6 +191,12 @@ class Sort extends BaseObject * @since 2.0.33 */ public $sortFlags = SORT_REGULAR; + /** + * @var string|null the name of the [[\yii\base\Model]]-based class used by the [[link()]] method to retrieve + * attributes' labels. See [[link]] method for details. + * @since 2.0.49 + */ + public $modelClass; /** @@ -363,7 +369,8 @@ public function getAttributeOrder($attribute) * @param array $options additional HTML attributes for the hyperlink tag. * There is one special attribute `label` which will be used as the label of the hyperlink. * If this is not set, the label defined in [[attributes]] will be used. - * If no label is defined, [[\yii\helpers\Inflector::camel2words()]] will be called to get a label. + * If no label is defined, it will be retrieved from the instance of [[modelClass]] (if [[modelClass]] is not null) + * or generated from attribute name using [[\yii\helpers\Inflector::camel2words()]]. * Note that it will not be HTML-encoded. * @return string the generated hyperlink * @throws InvalidConfigException if the attribute is unknown @@ -388,6 +395,11 @@ public function link($attribute, $options = []) } else { if (isset($this->attributes[$attribute]['label'])) { $label = $this->attributes[$attribute]['label']; + } elseif ($this->modelClass !== null) { + $modelClass = $this->modelClass; + /** @var \yii\base\Model $model */ + $model = $modelClass::instance(); + $label = $model->getAttributeLabel($attribute); } else { $label = Inflector::camel2words($attribute); } diff --git a/framework/db/ActiveRecordInterface.php b/framework/db/ActiveRecordInterface.php index d845e9d32cb..441001a99ff 100644 --- a/framework/db/ActiveRecordInterface.php +++ b/framework/db/ActiveRecordInterface.php @@ -144,6 +144,7 @@ public static function isPrimaryKey($keys); * // Use where() to ignore the default condition * // SELECT FROM customer WHERE age>30 * $customers = Customer::find()->where('age>30')->all(); + * ``` * * @return ActiveQueryInterface the newly created [[ActiveQueryInterface]] instance. */ diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php index 67a55d7884b..61cf3774094 100644 --- a/framework/db/BaseActiveRecord.php +++ b/framework/db/BaseActiveRecord.php @@ -282,7 +282,7 @@ public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) */ public function __get($name) { - if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { + if (array_key_exists($name, $this->_attributes)) { return $this->_attributes[$name]; } @@ -290,7 +290,7 @@ public function __get($name) return null; } - if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) { + if (array_key_exists($name, $this->_related)) { return $this->_related[$name]; } $value = parent::__get($name); @@ -576,13 +576,24 @@ public function getOldAttribute($name) */ public function setOldAttribute($name, $value) { - if (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name)) { + if ($this->canSetOldAttribute($name)) { $this->_oldAttributes[$name] = $value; } else { throw new InvalidArgumentException(get_class($this) . ' has no attribute named "' . $name . '".'); } } + /** + * Returns if the old named attribute can be set. + * @param string $name the attribute name + * @return bool whether the old attribute can be set + * @see setOldAttribute() + */ + public function canSetOldAttribute($name) + { + return (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name)); + } + /** * Marks an attribute dirty. * This method may be called to force updating a record when calling [[update()]], @@ -639,7 +650,7 @@ public function getDirtyAttributes($names = null) } } else { foreach ($this->_attributes as $name => $value) { - if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $this->isAttributeDirty($name, $value))) { + if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $this->isValueDifferent($value, $this->_oldAttributes[$name]))) { $attributes[$name] = $value; } } @@ -1599,40 +1610,46 @@ public static function isPrimaryKey($keys) /** * Returns the text label for the specified attribute. - * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model. + * The attribute may be specified in a dot format to retrieve the label from related model or allow this model to override the label defined in related model. + * For example, if the attribute is specified as 'relatedModel1.relatedModel2.attr' the function will return the first label definition it can find + * in the following order: + * - the label for 'relatedModel1.relatedModel2.attr' defined in [[attributeLabels()]] of this model; + * - the label for 'relatedModel2.attr' defined in related model represented by relation 'relatedModel1' of this model; + * - the label for 'attr' defined in related model represented by relation 'relatedModel2' of relation 'relatedModel1'. + * If no label definition was found then the value of $this->generateAttributeLabel('relatedModel1.relatedModel2.attr') will be returned. * @param string $attribute the attribute name * @return string the attribute label - * @see generateAttributeLabel() * @see attributeLabels() + * @see generateAttributeLabel() */ public function getAttributeLabel($attribute) { - $labels = $this->attributeLabels(); - if (isset($labels[$attribute])) { - return $labels[$attribute]; - } elseif (strpos($attribute, '.')) { - $attributeParts = explode('.', $attribute); - $neededAttribute = array_pop($attributeParts); + $model = $this; + $modelAttribute = $attribute; + for (;;) { + $labels = $model->attributeLabels(); + if (isset($labels[$modelAttribute])) { + return $labels[$modelAttribute]; + } - $relatedModel = $this; - foreach ($attributeParts as $relationName) { - if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) { - $relatedModel = $relatedModel->$relationName; - } else { - try { - $relation = $relatedModel->getRelation($relationName); - } catch (InvalidParamException $e) { - return $this->generateAttributeLabel($attribute); - } - /* @var $modelClass ActiveRecordInterface */ - $modelClass = $relation->modelClass; - $relatedModel = $modelClass::instance(); - } + $parts = explode('.', $modelAttribute, 2); + if (count($parts) < 2) { + break; } - $labels = $relatedModel->attributeLabels(); - if (isset($labels[$neededAttribute])) { - return $labels[$neededAttribute]; + list ($relationName, $modelAttribute) = $parts; + + if ($model->isRelationPopulated($relationName) && $model->$relationName instanceof self) { + $model = $model->$relationName; + } else { + try { + $relation = $model->getRelation($relationName); + } catch (InvalidArgumentException $e) { + break; + } + /* @var $modelClass ActiveRecordInterface */ + $modelClass = $relation->modelClass; + $model = $modelClass::instance(); } } @@ -1756,18 +1773,18 @@ private function setRelationDependencies($name, $relation, $viaRelationName = nu } /** - * @param string $attribute - * @param mixed $value + * @param mixed $newValue + * @param mixed $oldValue * @return bool + * @since 2.0.48 */ - private function isAttributeDirty($attribute, $value) + private function isValueDifferent($newValue, $oldValue) { - $old_attribute = $this->oldAttributes[$attribute]; - if (is_array($value) && is_array($this->oldAttributes[$attribute])) { - $value = ArrayHelper::recursiveSort($value); - $old_attribute = ArrayHelper::recursiveSort($old_attribute); + if (is_array($newValue) && is_array($oldValue) && ArrayHelper::isAssociative($oldValue)) { + $newValue = ArrayHelper::recursiveSort($newValue); + $oldValue = ArrayHelper::recursiveSort($oldValue); } - return $value !== $old_attribute; + return $newValue !== $oldValue; } } diff --git a/framework/db/Command.php b/framework/db/Command.php index 2b8d77ff604..72eda72e4e1 100644 --- a/framework/db/Command.php +++ b/framework/db/Command.php @@ -258,7 +258,7 @@ public function prepare($forRead = null) $forRead = false; } if ($forRead || $forRead === null && $this->db->getSchema()->isReadQuery($sql)) { - $pdo = $this->db->getSlavePdo(); + $pdo = $this->db->getSlavePdo(true); } else { $pdo = $this->db->getMasterPdo(); } @@ -377,6 +377,13 @@ public function bindValues($values) $this->pendingParams[$name] = [$value->getValue(), $value->getType()]; $this->params[$name] = $value->getValue(); } else { + if (version_compare(PHP_VERSION, '8.1.0') >= 0) { + if ($value instanceof \BackedEnum) { + $value = $value->value; + } elseif ($value instanceof \UnitEnum) { + $value = $value->name; + } + } $type = $schema->getPdoType($value); $this->pendingParams[$name] = [$value, $type]; $this->params[$name] = $value; @@ -631,7 +638,8 @@ public function delete($table, $condition = '', $params = []) * * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), * where name stands for a column name which will be properly quoted by the method, and definition - * stands for the column type which can contain an abstract DB type. + * stands for the column type which must contain an abstract DB type. + * * The method [[QueryBuilder::getColumnType()]] will be called * to convert the abstract column types to physical ones. For example, `string` will be converted * as `varchar(255)`, and `string not null` becomes `varchar(255) not null`. @@ -639,6 +647,16 @@ public function delete($table, $condition = '', $params = []) * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly * inserted into the generated SQL. * + * Example usage: + * ```php + * Yii::$app->db->createCommand()->createTable('post', [ + * 'id' => 'pk', + * 'title' => 'string', + * 'text' => 'text', + * 'column_name double precision null default null', + * ]); + * ``` + * * @param string $table the name of the table to be created. The name will be properly quoted by the method. * @param array $columns the columns (name => definition) in the new table. * @param string|null $options additional SQL fragment that will be appended to the generated SQL. diff --git a/framework/db/Connection.php b/framework/db/Connection.php index 0693d242206..4df8f3af62f 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -1013,7 +1013,7 @@ public function getDriverName() if (($pos = strpos((string)$this->dsn, ':')) !== false) { $this->_driverName = strtolower(substr($this->dsn, 0, $pos)); } else { - $this->_driverName = strtolower($this->getSlavePdo()->getAttribute(PDO::ATTR_DRIVER_NAME)); + $this->_driverName = strtolower($this->getSlavePdo(true)->getAttribute(PDO::ATTR_DRIVER_NAME)); } } diff --git a/framework/db/Migration.php b/framework/db/Migration.php index be61749514e..c4bf99981af 100644 --- a/framework/db/Migration.php +++ b/framework/db/Migration.php @@ -305,13 +305,25 @@ public function delete($table, $condition = '', $params = []) * * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), * where name stands for a column name which will be properly quoted by the method, and definition - * stands for the column type which can contain an abstract DB type. + * stands for the column type which must contain an abstract DB type. * * The [[QueryBuilder::getColumnType()]] method will be invoked to convert any abstract type into a physical one. * * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly * put into the generated SQL. * + * Example usage: + * ```php + * class m200000_000000_create_table_fruits extends \yii\db\Migration + * { + * public function safeUp() + * { + * $this->createTable('{{%fruits}}', [ + * // ... + * 'column_name double precision null default null', + * ``` + * + * * @param string $table the name of the table to be created. The name will be properly quoted by the method. * @param array $columns the columns (name => definition) in the new table. * @param string|null $options additional SQL fragment that will be appended to the generated SQL. diff --git a/framework/db/Query.php b/framework/db/Query.php index f0b215859dc..1819c9c3107 100644 --- a/framework/db/Query.php +++ b/framework/db/Query.php @@ -1049,7 +1049,7 @@ public function rightJoin($table, $on = '', $params = []) /** * Sets the GROUP BY part of the query. - * @param string|array|ExpressionInterface $columns the columns to be grouped by. + * @param string|array|ExpressionInterface|null $columns the columns to be grouped by. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). * The method will automatically quote the column names unless a column contains some parenthesis * (which means the column contains a DB expression). @@ -1067,7 +1067,7 @@ public function groupBy($columns) { if ($columns instanceof ExpressionInterface) { $columns = [$columns]; - } elseif (!is_array($columns)) { + } elseif (!is_array($columns) && !is_null($columns)) { $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); } $this->groupBy = $columns; diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index b96ffe28eff..114116e67c5 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -692,7 +692,7 @@ public function delete($table, $condition, &$params) * * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), * where name stands for a column name which will be properly quoted by the method, and definition - * stands for the column type which can contain an abstract DB type. + * stands for the column type which must contain an abstract DB type. * The [[getColumnType()]] method will be invoked to convert any abstract type into a physical one. * * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly @@ -705,6 +705,7 @@ public function delete($table, $condition, &$params) * 'id' => 'pk', * 'name' => 'string', * 'age' => 'integer', + * 'column_name double precision null default null', # definition only example * ]); * ``` * diff --git a/framework/db/Schema.php b/framework/db/Schema.php index 026339b54e4..53c0af9882d 100644 --- a/framework/db/Schema.php +++ b/framework/db/Schema.php @@ -458,7 +458,7 @@ public function quoteValue($str) return $str; } - if (mb_stripos($this->db->dsn, 'odbc:') === false && ($value = $this->db->getSlavePdo()->quote($str)) !== false) { + if (mb_stripos((string)$this->db->dsn, 'odbc:') === false && ($value = $this->db->getSlavePdo(true)->quote($str)) !== false) { return $value; } @@ -695,7 +695,7 @@ public function isReadQuery($sql) public function getServerVersion() { if ($this->_serverVersion === null) { - $this->_serverVersion = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + $this->_serverVersion = $this->db->getSlavePdo(true)->getAttribute(\PDO::ATTR_SERVER_VERSION); } return $this->_serverVersion; } @@ -809,7 +809,7 @@ protected function setTableMetadata($name, $type, $data) */ protected function normalizePdoRowKeyCase(array $row, $multiple) { - if ($this->db->getSlavePdo()->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_UPPER) { + if ($this->db->getSlavePdo(true)->getAttribute(\PDO::ATTR_CASE) !== \PDO::CASE_UPPER) { return $row; } diff --git a/framework/db/conditions/LikeCondition.php b/framework/db/conditions/LikeCondition.php index f4684d3f54f..0fe2a8fa0eb 100644 --- a/framework/db/conditions/LikeCondition.php +++ b/framework/db/conditions/LikeCondition.php @@ -18,8 +18,8 @@ class LikeCondition extends SimpleCondition { /** - * @var array|null|false map of chars to their replacements, false if characters should not be escaped - * or either null or empty array if escaping is condition builder responsibility. + * @var array|null|false map of chars to their replacements, `false` if characters should not be escaped + * or either `null` or empty array if escaping is condition builder responsibility. * By default it's set to `null`. */ protected $escapingReplacements; @@ -40,9 +40,10 @@ public function __construct($column, $operator, $value) /** * This method allows to specify how to escape special characters in the value(s). * - * @param array an array of mappings from the special characters to their escaped counterparts. - * You may use `false` or an empty array to indicate the values are already escaped and no escape - * should be applied. Note that when using an escape mapping (or the third operand is not provided), + * @param array|null|false an array of mappings from the special characters to their escaped counterparts. + * You may use `false` to indicate the values are already escaped and no escape should be applied, + * or either `null` or empty array if escaping is condition builder responsibility. + * Note that when using an escape mapping (or the third operand is not provided), * the values will be automatically enclosed within a pair of percentage characters. */ public function setEscapingReplacements($escapingReplacements) @@ -51,7 +52,7 @@ public function setEscapingReplacements($escapingReplacements) } /** - * @return array|false + * @return array|null|false */ public function getEscapingReplacements() { diff --git a/framework/db/conditions/LikeConditionBuilder.php b/framework/db/conditions/LikeConditionBuilder.php index 049be5b92ec..accb849c2aa 100644 --- a/framework/db/conditions/LikeConditionBuilder.php +++ b/framework/db/conditions/LikeConditionBuilder.php @@ -78,7 +78,7 @@ public function build(ExpressionInterface $expression, array &$params = []) if ($value instanceof ExpressionInterface) { $phName = $this->queryBuilder->buildExpression($value, $params); } else { - $phName = $this->queryBuilder->bindParam(empty($escape) ? $value : ('%' . strtr($value, $escape) . '%'), $params); + $phName = $this->queryBuilder->bindParam(empty($escape) ? $value : ('%' . strtr((string)$value, $escape) . '%'), $params); } $parts[] = "{$column} {$operator} {$phName}{$escapeSql}"; } diff --git a/framework/db/cubrid/Schema.php b/framework/db/cubrid/Schema.php index d22d0a80b68..65b6c11b9d4 100644 --- a/framework/db/cubrid/Schema.php +++ b/framework/db/cubrid/Schema.php @@ -92,7 +92,7 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface */ protected function findTableNames($schema = '') { - $pdo = $this->db->getSlavePdo(); + $pdo = $this->db->getSlavePdo(true); $tables = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE); $tableNames = []; foreach ($tables as $table) { @@ -110,7 +110,7 @@ protected function findTableNames($schema = '') */ protected function loadTableSchema($name) { - $pdo = $this->db->getSlavePdo(); + $pdo = $this->db->getSlavePdo(true); $tableInfo = $pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name); @@ -159,7 +159,7 @@ protected function loadTableSchema($name) */ protected function loadTablePrimaryKey($tableName) { - $primaryKey = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $tableName); + $primaryKey = $this->db->getSlavePdo(true)->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $tableName); if (empty($primaryKey)) { return null; } @@ -183,7 +183,7 @@ protected function loadTableForeignKeys($tableName) 3 => 'SET NULL', ]; - $foreignKeys = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $tableName); + $foreignKeys = $this->db->getSlavePdo(true)->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $tableName); $foreignKeys = ArrayHelper::index($foreignKeys, null, 'FK_NAME'); ArrayHelper::multisort($foreignKeys, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC); $result = []; @@ -386,7 +386,7 @@ public function createColumnSchemaBuilder($type, $length = null) */ private function loadTableConstraints($tableName, $returnType) { - $constraints = $this->db->getSlavePdo()->cubrid_schema(\PDO::CUBRID_SCH_CONSTRAINT, $tableName); + $constraints = $this->db->getSlavePdo(true)->cubrid_schema(\PDO::CUBRID_SCH_CONSTRAINT, $tableName); $constraints = ArrayHelper::index($constraints, null, ['TYPE', 'NAME']); ArrayHelper::multisort($constraints, 'KEY_ORDER', SORT_ASC, SORT_NUMERIC); $result = [ diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index cc3595519cd..1225c466cc6 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -460,10 +460,9 @@ private function normalizeTableRowData($table, $columns, &$params) $columnSchemas = $tableSchema->columns; foreach ($columns as $name => $value) { // @see https://github.com/yiisoft/yii2/issues/12599 - if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value) || $value === null)) { - $phName = $this->bindParam($value, $params); + if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value))) { // @see https://github.com/yiisoft/yii2/issues/12599 - $columns[$name] = new Expression("CONVERT(VARBINARY(MAX), $phName)", $params); + $columns[$name] = new Expression('CONVERT(VARBINARY(MAX), ' . ('0x' . bin2hex($value)) . ')'); } } } diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index b1517ab4da7..fc10931c7a1 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -410,7 +410,7 @@ private function supportsFractionalSeconds() } $version = $cache ? $cache->get($key) : null; if (!$version) { - $version = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + $version = $this->db->getSlavePdo(true)->getAttribute(\PDO::ATTR_SERVER_VERSION); if ($cache) { $cache->set($key, $version, $this->db->schemaCacheDuration); } diff --git a/framework/db/mysql/Schema.php b/framework/db/mysql/Schema.php index 73c11f56724..fa2270eb77d 100644 --- a/framework/db/mysql/Schema.php +++ b/framework/db/mysql/Schema.php @@ -484,7 +484,7 @@ public function createColumnSchemaBuilder($type, $length = null) protected function isOldMysql() { if ($this->_oldMysql === null) { - $version = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + $version = $this->db->getSlavePdo(true)->getAttribute(\PDO::ATTR_SERVER_VERSION); $this->_oldMysql = version_compare($version, '5.1', '<='); } diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php index 0e615a579db..56411163e1e 100644 --- a/framework/helpers/BaseArrayHelper.php +++ b/framework/helpers/BaseArrayHelper.php @@ -327,7 +327,12 @@ public static function setValue(&$array, $path, $value) */ public static function remove(&$array, $key, $default = null) { - if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { + // ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2) + if (is_float($key)) { + $key = (int)$key; + } + + if (is_array($array) && array_key_exists($key, $array)) { $value = $array[$key]; unset($array[$key]); @@ -608,17 +613,20 @@ public static function map($array, $from, $to, $group = null) * Checks if the given array contains the specified key. * This method enhances the `array_key_exists()` function by supporting case-insensitive * key comparison. - * @param string $key the key to check + * @param string|int $key the key to check * @param array|ArrayAccess $array the array with keys to check * @param bool $caseSensitive whether the key comparison should be case-sensitive * @return bool whether the array contains the specified key */ public static function keyExists($key, $array, $caseSensitive = true) { + // ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2) + if (is_float($key)) { + $key = (int)$key; + } + if ($caseSensitive) { - // Function `isset` checks key faster but skips `null`, `array_key_exists` handles this case - // https://www.php.net/manual/en/function.array-key-exists.php#107786 - if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { + if (is_array($array) && array_key_exists($key, $array)) { return true; } // Cannot use `array_has_key` on Objects for PHP 7.4+, therefore we need to check using [[ArrayAccess::offsetExists()]] diff --git a/framework/helpers/BaseConsole.php b/framework/helpers/BaseConsole.php index abb838c9c47..febeed313b1 100644 --- a/framework/helpers/BaseConsole.php +++ b/framework/helpers/BaseConsole.php @@ -948,13 +948,17 @@ public static function confirm($message, $default = false) * @param string $prompt the prompt message * @param array $options Key-value array of options to choose from. Key is what is inputed and used, value is * what's displayed to end user by help command. + * @param string|null $default value to use when the user doesn't provide an option. + * If the default is `null`, the user is required to select an option. * * @return string An option character the user chose + * @since 2.0.49 Added the $default argument */ - public static function select($prompt, $options = []) + public static function select($prompt, $options = [], $default = null) { top: - static::stdout("$prompt [" . implode(',', array_keys($options)) . ',?]: '); + static::stdout("$prompt (" . implode(',', array_keys($options)) . ',?)' + . ($default !== null ? '[' . $default . ']' : '') . ': '); $input = static::stdin(); if ($input === '?') { foreach ($options as $key => $value) { @@ -962,6 +966,8 @@ public static function select($prompt, $options = []) } static::output(' ? - Show help'); goto top; + } elseif ($default !== null && $input === '') { + return $default; } elseif (!array_key_exists($input, $options)) { goto top; } diff --git a/framework/helpers/BaseFileHelper.php b/framework/helpers/BaseFileHelper.php index d409631520d..55445aec009 100644 --- a/framework/helpers/BaseFileHelper.php +++ b/framework/helpers/BaseFileHelper.php @@ -39,6 +39,11 @@ class BaseFileHelper * @since 2.0.14 */ public static $mimeAliasesFile = '@yii/helpers/mimeAliases.php'; + /** + * @var string the path (or alias) of a PHP file containing extensions per MIME type. + * @since 2.0.48 + */ + public static $mimeExtensionsFile = '@yii/helpers/mimeExtensions.php'; /** @@ -213,10 +218,49 @@ public static function getExtensionsByMimeType($mimeType, $magicFile = null) $mimeType = $aliases[$mimeType]; } + // Note: For backwards compatibility the "MimeTypes" file is used. $mimeTypes = static::loadMimeTypes($magicFile); return array_keys($mimeTypes, mb_strtolower($mimeType, 'UTF-8'), true); } + /** + * Determines the most common extension by given MIME type. + * This method will use a local map between MIME types and extension names. + * @param string $mimeType file MIME type. + * @param bool $preferShort return an extension with a maximum of 3 characters. + * @param string|null $magicFile the path (or alias) of the file that contains all available MIME type information. + * If this is not set, the file specified by [[mimeMagicFile]] will be used. + * @return string|null the extensions corresponding to the specified MIME type + * @since 2.0.48 + */ + public static function getExtensionByMimeType($mimeType, $preferShort = false, $magicFile = null) + { + $aliases = static::loadMimeAliases(static::$mimeAliasesFile); + if (isset($aliases[$mimeType])) { + $mimeType = $aliases[$mimeType]; + } + + $mimeExtensions = static::loadMimeExtensions($magicFile); + + if (!array_key_exists($mimeType, $mimeExtensions)) { + return null; + } + + $extensions = $mimeExtensions[$mimeType]; + if (is_array($extensions)) { + if ($preferShort) { + foreach ($extensions as $extension) { + if (mb_strlen($extension, 'UTF-8') <= 3) { + return $extension; + } + } + } + return $extensions[0]; + } else { + return $extensions; + } + } + private static $_mimeTypes = []; /** @@ -260,6 +304,28 @@ protected static function loadMimeAliases($aliasesFile) return self::$_mimeAliases[$aliasesFile]; } + private static $_mimeExtensions = []; + + /** + * Loads MIME extensions from the specified file. + * @param string|null $extensionsFile the path (or alias) of the file that contains MIME type aliases. + * If this is not set, the file specified by [[mimeAliasesFile]] will be used. + * @return array the mapping from file extensions to MIME types + * @since 2.0.48 + */ + protected static function loadMimeExtensions($extensionsFile) + { + if ($extensionsFile === null) { + $extensionsFile = static::$mimeExtensionsFile; + } + $extensionsFile = Yii::getAlias($extensionsFile); + if (!isset(self::$_mimeExtensions[$extensionsFile])) { + self::$_mimeExtensions[$extensionsFile] = require $extensionsFile; + } + + return self::$_mimeExtensions[$extensionsFile]; + } + /** * Copies a whole directory as another one. * The files and sub-directories will also be copied over. diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php index da134163105..60261f9828a 100644 --- a/framework/helpers/BaseStringHelper.php +++ b/framework/helpers/BaseStringHelper.php @@ -49,7 +49,7 @@ public static function byteSubstr($string, $start, $length = null) $length = static::byteLength($string); } - return mb_substr($string, $start, $length, '8bit'); + return mb_substr((string)$string, $start, $length, '8bit'); } /** @@ -67,6 +67,8 @@ public static function byteSubstr($string, $start, $length = null) */ public static function basename($path, $suffix = '') { + $path = (string)$path; + $len = mb_strlen($suffix); if ($len > 0 && mb_substr($path, -$len) === $suffix) { $path = mb_substr($path, 0, -$len); @@ -93,7 +95,7 @@ public static function basename($path, $suffix = '') public static function dirname($path) { $normalizedPath = rtrim( - str_replace('\\', '/', $path), + str_replace('\\', '/', (string)$path), '/' ); $separatorPosition = mb_strrpos($normalizedPath, '/'); @@ -122,6 +124,8 @@ public static function dirname($path) */ public static function truncate($string, $length, $suffix = '...', $encoding = null, $asHtml = false) { + $string = (string)$string; + if ($encoding === null) { $encoding = Yii::$app ? Yii::$app->charset : 'UTF-8'; } @@ -233,6 +237,9 @@ protected static function truncateHtml($string, $count, $suffix, $encoding = fal */ public static function startsWith($string, $with, $caseSensitive = true) { + $string = (string)$string; + $with = (string)$with; + if (!$bytes = static::byteLength($with)) { return true; } @@ -257,6 +264,9 @@ public static function startsWith($string, $with, $caseSensitive = true) */ public static function endsWith($string, $with, $caseSensitive = true) { + $string = (string)$string; + $with = (string)$with; + if (!$bytes = static::byteLength($with)) { return true; } diff --git a/framework/helpers/mimeExtensions.php b/framework/helpers/mimeExtensions.php new file mode 100644 index 00000000000..946d61cd0c5 --- /dev/null +++ b/framework/helpers/mimeExtensions.php @@ -0,0 +1,1238 @@ + 'ez', + 'application/applixware' => 'aw', + 'application/atom+xml' => 'atom', + 'application/atomcat+xml' => 'atomcat', + 'application/atomsvc+xml' => 'atomsvc', + 'application/ccxml+xml' => 'ccxml', + 'application/cdmi-capability' => 'cdmia', + 'application/cdmi-container' => 'cdmic', + 'application/cdmi-domain' => 'cdmid', + 'application/cdmi-object' => 'cdmio', + 'application/cdmi-queue' => 'cdmiq', + 'application/cu-seeme' => 'cu', + 'application/davmount+xml' => 'davmount', + 'application/docbook+xml' => 'dbk', + 'application/dssc+der' => 'dssc', + 'application/dssc+xml' => 'xdssc', + 'application/ecmascript' => 'ecma', + 'application/emma+xml' => 'emma', + 'application/epub+zip' => 'epub', + 'application/exi' => 'exi', + 'application/font-tdpfr' => 'pfr', + 'application/gml+xml' => 'gml', + 'application/gpx+xml' => 'gpx', + 'application/gxf' => 'gxf', + 'application/hyperstudio' => 'stk', + 'application/inkml+xml' => [ + 'ink', + 'inkml', + ], + 'application/ipfix' => 'ipfix', + 'application/java-archive' => 'jar', + 'application/java-serialized-object' => 'ser', + 'application/java-vm' => 'class', + 'application/json' => 'json', + 'application/jsonml+json' => 'jsonml', + 'application/lost+xml' => 'lostxml', + 'application/mac-binhex40' => 'hqx', + 'application/mac-compactpro' => 'cpt', + 'application/mads+xml' => 'mads', + 'application/marc' => 'mrc', + 'application/marcxml+xml' => 'mrcx', + 'application/mathematica' => [ + 'ma', + 'nb', + 'mb', + ], + 'application/mathml+xml' => 'mathml', + 'application/mbox' => 'mbox', + 'application/mediaservercontrol+xml' => 'mscml', + 'application/metalink+xml' => 'metalink', + 'application/metalink4+xml' => 'meta4', + 'application/mets+xml' => 'mets', + 'application/mods+xml' => 'mods', + 'application/mp21' => [ + 'm21', + 'mp21', + ], + 'application/mp4' => 'mp4s', + 'application/msword' => [ + 'doc', + 'dot', + ], + 'application/mxf' => 'mxf', + 'application/octet-stream' => [ + 'bin', + 'dms', + 'lrf', + 'mar', + 'so', + 'dist', + 'distz', + 'pkg', + 'bpk', + 'dump', + 'elc', + 'deploy', + ], + 'application/oda' => 'oda', + 'application/oebps-package+xml' => 'opf', + 'application/ogg' => 'ogx', + 'application/omdoc+xml' => 'omdoc', + 'application/onenote' => [ + 'onetoc', + 'onetoc2', + 'onetmp', + 'onepkg', + ], + 'application/oxps' => 'oxps', + 'application/patch-ops-error+xml' => 'xer', + 'application/pdf' => 'pdf', + 'application/pgp-encrypted' => 'pgp', + 'application/pgp-signature' => [ + 'asc', + 'sig', + ], + 'application/pics-rules' => 'prf', + 'application/pkcs10' => 'p10', + 'application/pkcs7-mime' => [ + 'p7m', + 'p7c', + ], + 'application/pkcs7-signature' => 'p7s', + 'application/pkcs8' => 'p8', + 'application/pkix-attr-cert' => 'ac', + 'application/pkix-cert' => 'cer', + 'application/pkix-crl' => 'crl', + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/pls+xml' => 'pls', + 'application/postscript' => [ + 'ai', + 'eps', + 'ps', + ], + 'application/prs.cww' => 'cww', + 'application/pskc+xml' => 'pskcxml', + 'application/rdf+xml' => 'rdf', + 'application/reginfo+xml' => 'rif', + 'application/relax-ng-compact-syntax' => 'rnc', + 'application/resource-lists+xml' => 'rl', + 'application/resource-lists-diff+xml' => 'rld', + 'application/rls-services+xml' => 'rs', + 'application/rpki-ghostbusters' => 'gbr', + 'application/rpki-manifest' => 'mft', + 'application/rpki-roa' => 'roa', + 'application/rsd+xml' => 'rsd', + 'application/rss+xml' => 'rss', + 'application/rtf' => 'rtf', + 'application/sbml+xml' => 'sbml', + 'application/scvp-cv-request' => 'scq', + 'application/scvp-cv-response' => 'scs', + 'application/scvp-vp-request' => 'spq', + 'application/scvp-vp-response' => 'spp', + 'application/sdp' => 'sdp', + 'application/set-payment-initiation' => 'setpay', + 'application/set-registration-initiation' => 'setreg', + 'application/shf+xml' => 'shf', + 'application/smil+xml' => [ + 'smi', + 'smil', + ], + 'application/sparql-query' => 'rq', + 'application/sparql-results+xml' => 'srx', + 'application/srgs' => 'gram', + 'application/srgs+xml' => 'grxml', + 'application/sru+xml' => 'sru', + 'application/ssdl+xml' => 'ssdl', + 'application/ssml+xml' => 'ssml', + 'application/tei+xml' => [ + 'tei', + 'teicorpus', + ], + 'application/thraud+xml' => 'tfi', + 'application/timestamped-data' => 'tsd', + 'application/vnd.3gpp.pic-bw-large' => 'plb', + 'application/vnd.3gpp.pic-bw-small' => 'psb', + 'application/vnd.3gpp.pic-bw-var' => 'pvb', + 'application/vnd.3gpp2.tcap' => 'tcap', + 'application/vnd.3m.post-it-notes' => 'pwn', + 'application/vnd.accpac.simply.aso' => 'aso', + 'application/vnd.accpac.simply.imp' => 'imp', + 'application/vnd.acucobol' => 'acu', + 'application/vnd.acucorp' => [ + 'atc', + 'acutc', + ], + 'application/vnd.adobe.air-application-installer-package+zip' => 'air', + 'application/vnd.adobe.formscentral.fcdt' => 'fcdt', + 'application/vnd.adobe.fxp' => [ + 'fxp', + 'fxpl', + ], + 'application/vnd.adobe.xdp+xml' => 'xdp', + 'application/vnd.adobe.xfdf' => 'xfdf', + 'application/vnd.ahead.space' => 'ahead', + 'application/vnd.airzip.filesecure.azf' => 'azf', + 'application/vnd.airzip.filesecure.azs' => 'azs', + 'application/vnd.amazon.ebook' => 'azw', + 'application/vnd.americandynamics.acc' => 'acc', + 'application/vnd.amiga.ami' => 'ami', + 'application/vnd.android.package-archive' => 'apk', + 'application/vnd.anser-web-certificate-issue-initiation' => 'cii', + 'application/vnd.anser-web-funds-transfer-initiation' => 'fti', + 'application/vnd.antix.game-component' => 'atx', + 'application/vnd.apple.installer+xml' => 'mpkg', + 'application/vnd.apple.mpegurl' => 'm3u8', + 'application/vnd.aristanetworks.swi' => 'swi', + 'application/vnd.astraea-software.iota' => 'iota', + 'application/vnd.audiograph' => 'aep', + 'application/vnd.blueice.multipass' => 'mpm', + 'application/vnd.bmi' => 'bmi', + 'application/vnd.businessobjects' => 'rep', + 'application/vnd.chemdraw+xml' => 'cdxml', + 'application/vnd.chipnuts.karaoke-mmd' => 'mmd', + 'application/vnd.cinderella' => 'cdy', + 'application/vnd.claymore' => 'cla', + 'application/vnd.cloanto.rp9' => 'rp9', + 'application/vnd.clonk.c4group' => [ + 'c4g', + 'c4d', + 'c4f', + 'c4p', + 'c4u', + ], + 'application/vnd.cluetrust.cartomobile-config' => 'c11amc', + 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz', + 'application/vnd.commonspace' => 'csp', + 'application/vnd.contact.cmsg' => 'cdbcmsg', + 'application/vnd.cosmocaller' => 'cmc', + 'application/vnd.crick.clicker' => 'clkx', + 'application/vnd.crick.clicker.keyboard' => 'clkk', + 'application/vnd.crick.clicker.palette' => 'clkp', + 'application/vnd.crick.clicker.template' => 'clkt', + 'application/vnd.crick.clicker.wordbank' => 'clkw', + 'application/vnd.criticaltools.wbs+xml' => 'wbs', + 'application/vnd.ctc-posml' => 'pml', + 'application/vnd.cups-ppd' => 'ppd', + 'application/vnd.curl.car' => 'car', + 'application/vnd.curl.pcurl' => 'pcurl', + 'application/vnd.dart' => 'dart', + 'application/vnd.data-vision.rdz' => 'rdz', + 'application/vnd.dece.data' => [ + 'uvf', + 'uvvf', + 'uvd', + 'uvvd', + ], + 'application/vnd.dece.ttml+xml' => [ + 'uvt', + 'uvvt', + ], + 'application/vnd.dece.unspecified' => [ + 'uvx', + 'uvvx', + ], + 'application/vnd.dece.zip' => [ + 'uvz', + 'uvvz', + ], + 'application/vnd.denovo.fcselayout-link' => 'fe_launch', + 'application/vnd.dna' => 'dna', + 'application/vnd.dolby.mlp' => 'mlp', + 'application/vnd.dpgraph' => 'dpg', + 'application/vnd.dreamfactory' => 'dfac', + 'application/vnd.ds-keypoint' => 'kpxx', + 'application/vnd.dvb.ait' => 'ait', + 'application/vnd.dvb.service' => 'svc', + 'application/vnd.dynageo' => 'geo', + 'application/vnd.ecowin.chart' => 'mag', + 'application/vnd.enliven' => 'nml', + 'application/vnd.epson.esf' => 'esf', + 'application/vnd.epson.msf' => 'msf', + 'application/vnd.epson.quickanime' => 'qam', + 'application/vnd.epson.salt' => 'slt', + 'application/vnd.epson.ssf' => 'ssf', + 'application/vnd.eszigno3+xml' => [ + 'es3', + 'et3', + ], + 'application/vnd.ezpix-album' => 'ez2', + 'application/vnd.ezpix-package' => 'ez3', + 'application/vnd.fdf' => 'fdf', + 'application/vnd.fdsn.mseed' => 'mseed', + 'application/vnd.fdsn.seed' => [ + 'seed', + 'dataless', + ], + 'application/vnd.flographit' => 'gph', + 'application/vnd.fluxtime.clip' => 'ftc', + 'application/vnd.framemaker' => [ + 'fm', + 'frame', + 'maker', + 'book', + ], + 'application/vnd.frogans.fnc' => 'fnc', + 'application/vnd.frogans.ltf' => 'ltf', + 'application/vnd.fsc.weblaunch' => 'fsc', + 'application/vnd.fujitsu.oasys' => 'oas', + 'application/vnd.fujitsu.oasys2' => 'oa2', + 'application/vnd.fujitsu.oasys3' => 'oa3', + 'application/vnd.fujitsu.oasysgp' => 'fg5', + 'application/vnd.fujitsu.oasysprs' => 'bh2', + 'application/vnd.fujixerox.ddd' => 'ddd', + 'application/vnd.fujixerox.docuworks' => 'xdw', + 'application/vnd.fujixerox.docuworks.binder' => 'xbd', + 'application/vnd.fuzzysheet' => 'fzs', + 'application/vnd.genomatix.tuxedo' => 'txd', + 'application/vnd.geogebra.file' => 'ggb', + 'application/vnd.geogebra.slides' => 'ggs', + 'application/vnd.geogebra.tool' => 'ggt', + 'application/vnd.geometry-explorer' => [ + 'gex', + 'gre', + ], + 'application/vnd.geonext' => 'gxt', + 'application/vnd.geoplan' => 'g2w', + 'application/vnd.geospace' => 'g3w', + 'application/vnd.gmx' => 'gmx', + 'application/vnd.google-earth.kml+xml' => 'kml', + 'application/vnd.google-earth.kmz' => 'kmz', + 'application/vnd.grafeq' => [ + 'gqf', + 'gqs', + ], + 'application/vnd.groove-account' => 'gac', + 'application/vnd.groove-help' => 'ghf', + 'application/vnd.groove-identity-message' => 'gim', + 'application/vnd.groove-injector' => 'grv', + 'application/vnd.groove-tool-message' => 'gtm', + 'application/vnd.groove-tool-template' => 'tpl', + 'application/vnd.groove-vcard' => 'vcg', + 'application/vnd.hal+xml' => 'hal', + 'application/vnd.handheld-entertainment+xml' => 'zmm', + 'application/vnd.hbci' => 'hbci', + 'application/vnd.hhe.lesson-player' => 'les', + 'application/vnd.hp-hpgl' => 'hpgl', + 'application/vnd.hp-hpid' => 'hpid', + 'application/vnd.hp-hps' => 'hps', + 'application/vnd.hp-jlyt' => 'jlt', + 'application/vnd.hp-pcl' => 'pcl', + 'application/vnd.hp-pclxl' => 'pclxl', + 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx', + 'application/vnd.ibm.minipay' => 'mpy', + 'application/vnd.ibm.modcap' => [ + 'afp', + 'listafp', + 'list3820', + ], + 'application/vnd.ibm.rights-management' => 'irm', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.iccprofile' => [ + 'icc', + 'icm', + ], + 'application/vnd.igloader' => 'igl', + 'application/vnd.immervision-ivp' => 'ivp', + 'application/vnd.immervision-ivu' => 'ivu', + 'application/vnd.insors.igm' => 'igm', + 'application/vnd.intercon.formnet' => [ + 'xpw', + 'xpx', + ], + 'application/vnd.intergeo' => 'i2g', + 'application/vnd.intu.qbo' => 'qbo', + 'application/vnd.intu.qfx' => 'qfx', + 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', + 'application/vnd.irepository.package+xml' => 'irp', + 'application/vnd.is-xpr' => 'xpr', + 'application/vnd.isac.fcs' => 'fcs', + 'application/vnd.jam' => 'jam', + 'application/vnd.jcp.javame.midlet-rms' => 'rms', + 'application/vnd.jisp' => 'jisp', + 'application/vnd.joost.joda-archive' => 'joda', + 'application/vnd.kahootz' => [ + 'ktz', + 'ktr', + ], + 'application/vnd.kde.karbon' => 'karbon', + 'application/vnd.kde.kchart' => 'chrt', + 'application/vnd.kde.kformula' => 'kfo', + 'application/vnd.kde.kivio' => 'flw', + 'application/vnd.kde.kontour' => 'kon', + 'application/vnd.kde.kpresenter' => [ + 'kpr', + 'kpt', + ], + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => [ + 'kwd', + 'kwt', + ], + 'application/vnd.kenameaapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => [ + 'kne', + 'knp', + ], + 'application/vnd.koan' => [ + 'skp', + 'skd', + 'skt', + 'skm', + ], + 'application/vnd.kodak-descriptor' => 'sse', + 'application/vnd.las.las+xml' => 'lasxml', + 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', + 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', + 'application/vnd.lotus-1-2-3' => '123', + 'application/vnd.lotus-approach' => 'apr', + 'application/vnd.lotus-freelance' => 'pre', + 'application/vnd.lotus-notes' => 'nsf', + 'application/vnd.lotus-organizer' => 'org', + 'application/vnd.lotus-screencam' => 'scm', + 'application/vnd.lotus-wordpro' => 'lwp', + 'application/vnd.macports.portpkg' => 'portpkg', + 'application/vnd.mcd' => 'mcd', + 'application/vnd.medcalcdata' => 'mc1', + 'application/vnd.mediastation.cdkey' => 'cdkey', + 'application/vnd.mfer' => 'mwf', + 'application/vnd.mfmp' => 'mfm', + 'application/vnd.micrografx.flo' => 'flo', + 'application/vnd.micrografx.igx' => 'igx', + 'application/vnd.mif' => 'mif', + 'application/vnd.mobius.daf' => 'daf', + 'application/vnd.mobius.dis' => 'dis', + 'application/vnd.mobius.mbk' => 'mbk', + 'application/vnd.mobius.mqy' => 'mqy', + 'application/vnd.mobius.msl' => 'msl', + 'application/vnd.mobius.plc' => 'plc', + 'application/vnd.mobius.txf' => 'txf', + 'application/vnd.mophun.application' => 'mpn', + 'application/vnd.mophun.certificate' => 'mpc', + 'application/vnd.mozilla.xul+xml' => 'xul', + 'application/vnd.ms-artgalry' => 'cil', + 'application/vnd.ms-cab-compressed' => 'cab', + 'application/vnd.ms-excel' => [ + 'xls', + 'xlm', + 'xla', + 'xlc', + 'xlt', + 'xlw', + ], + 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam', + 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb', + 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', + 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm', + 'application/vnd.ms-fontobject' => 'eot', + 'application/vnd.ms-htmlhelp' => 'chm', + 'application/vnd.ms-ims' => 'ims', + 'application/vnd.ms-lrm' => 'lrm', + 'application/vnd.ms-officetheme' => 'thmx', + 'application/vnd.ms-pki.seccat' => 'cat', + 'application/vnd.ms-pki.stl' => 'stl', + 'application/vnd.ms-powerpoint' => [ + 'ppt', + 'pps', + 'pot', + ], + 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam', + 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm', + 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm', + 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm', + 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm', + 'application/vnd.ms-project' => [ + 'mpp', + 'mpt', + ], + 'application/vnd.ms-word.document.macroenabled.12' => 'docm', + 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', + 'application/vnd.ms-works' => [ + 'wps', + 'wks', + 'wcm', + 'wdb', + ], + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.ms-xpsdocument' => 'xps', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.musician' => 'mus', + 'application/vnd.muvee.style' => 'msty', + 'application/vnd.mynfc' => 'taglet', + 'application/vnd.neurolanguage.nlu' => 'nlu', + 'application/vnd.nitf' => [ + 'ntf', + 'nitf', + ], + 'application/vnd.noblenet-directory' => 'nnd', + 'application/vnd.noblenet-sealer' => 'nns', + 'application/vnd.noblenet-web' => 'nnw', + 'application/vnd.nokia.n-gage.data' => 'ngdat', + 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage', + 'application/vnd.nokia.radio-preset' => 'rpst', + 'application/vnd.nokia.radio-presets' => 'rpss', + 'application/vnd.novadigm.edm' => 'edm', + 'application/vnd.novadigm.edx' => 'edx', + 'application/vnd.novadigm.ext' => 'ext', + 'application/vnd.oasis.opendocument.chart' => 'odc', + 'application/vnd.oasis.opendocument.chart-template' => 'otc', + 'application/vnd.oasis.opendocument.database' => 'odb', + 'application/vnd.oasis.opendocument.formula' => 'odf', + 'application/vnd.oasis.opendocument.formula-template' => 'odft', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + 'application/vnd.oasis.opendocument.graphics-template' => 'otg', + 'application/vnd.oasis.opendocument.image' => 'odi', + 'application/vnd.oasis.opendocument.image-template' => 'oti', + 'application/vnd.oasis.opendocument.presentation' => 'odp', + 'application/vnd.oasis.opendocument.presentation-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.text' => 'odt', + 'application/vnd.oasis.opendocument.text-master' => 'odm', + 'application/vnd.oasis.opendocument.text-template' => 'ott', + 'application/vnd.oasis.opendocument.text-web' => 'oth', + 'application/vnd.olpc-sugar' => 'xo', + 'application/vnd.oma.dd2+xml' => 'dd2', + 'application/vnd.openofficeorg.extension' => 'oxt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', + 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx', + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', + 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', + 'application/vnd.osgeo.mapguide.package' => 'mgp', + 'application/vnd.osgi.dp' => 'dp', + 'application/vnd.osgi.subsystem' => 'esa', + 'application/vnd.palm' => [ + 'pdb', + 'pqa', + 'oprc', + ], + 'application/vnd.pawaafile' => 'paw', + 'application/vnd.pg.format' => 'str', + 'application/vnd.pg.osasli' => 'ei6', + 'application/vnd.picsel' => 'efif', + 'application/vnd.pmi.widget' => 'wg', + 'application/vnd.pocketlearn' => 'plf', + 'application/vnd.powerbuilder6' => 'pbd', + 'application/vnd.previewsystems.box' => 'box', + 'application/vnd.proteus.magazine' => 'mgz', + 'application/vnd.publishare-delta-tree' => 'qps', + 'application/vnd.pvi.ptid1' => 'ptid', + 'application/vnd.quark.quarkxpress' => [ + 'qxd', + 'qxt', + 'qwd', + 'qwt', + 'qxl', + 'qxb', + ], + 'application/vnd.realvnc.bed' => 'bed', + 'application/vnd.recordare.musicxml' => 'mxl', + 'application/vnd.recordare.musicxml+xml' => 'musicxml', + 'application/vnd.rig.cryptonote' => 'cryptonote', + 'application/vnd.rim.cod' => 'cod', + 'application/vnd.rn-realmedia' => 'rm', + 'application/vnd.rn-realmedia-vbr' => 'rmvb', + 'application/vnd.route66.link66+xml' => 'link66', + 'application/vnd.sailingtracker.track' => 'st', + 'application/vnd.seemail' => 'see', + 'application/vnd.sema' => 'sema', + 'application/vnd.semd' => 'semd', + 'application/vnd.semf' => 'semf', + 'application/vnd.shana.informed.formdata' => 'ifm', + 'application/vnd.shana.informed.formtemplate' => 'itp', + 'application/vnd.shana.informed.interchange' => 'iif', + 'application/vnd.shana.informed.package' => 'ipk', + 'application/vnd.simtech-mindmapper' => [ + 'twd', + 'twds', + ], + 'application/vnd.smaf' => 'mmf', + 'application/vnd.smart.teacher' => 'teacher', + 'application/vnd.solent.sdkm+xml' => [ + 'sdkm', + 'sdkd', + ], + 'application/vnd.spotfire.dxp' => 'dxp', + 'application/vnd.spotfire.sfs' => 'sfs', + 'application/vnd.stardivision.calc' => 'sdc', + 'application/vnd.stardivision.draw' => 'sda', + 'application/vnd.stardivision.impress' => 'sdd', + 'application/vnd.stardivision.math' => 'smf', + 'application/vnd.stardivision.writer' => [ + 'sdw', + 'vor', + ], + 'application/vnd.stardivision.writer-global' => 'sgl', + 'application/vnd.stepmania.package' => 'smzip', + 'application/vnd.stepmania.stepchart' => 'sm', + 'application/vnd.sun.xml.calc' => 'sxc', + 'application/vnd.sun.xml.calc.template' => 'stc', + 'application/vnd.sun.xml.draw' => 'sxd', + 'application/vnd.sun.xml.draw.template' => 'std', + 'application/vnd.sun.xml.impress' => 'sxi', + 'application/vnd.sun.xml.impress.template' => 'sti', + 'application/vnd.sun.xml.math' => 'sxm', + 'application/vnd.sun.xml.writer' => 'sxw', + 'application/vnd.sun.xml.writer.global' => 'sxg', + 'application/vnd.sun.xml.writer.template' => 'stw', + 'application/vnd.sus-calendar' => [ + 'sus', + 'susp', + ], + 'application/vnd.svd' => 'svd', + 'application/vnd.symbian.install' => [ + 'sis', + 'sisx', + ], + 'application/vnd.syncml+xml' => 'xsm', + 'application/vnd.syncml.dm+wbxml' => 'bdm', + 'application/vnd.syncml.dm+xml' => 'xdm', + 'application/vnd.tao.intent-module-archive' => 'tao', + 'application/vnd.tcpdump.pcap' => [ + 'pcap', + 'cap', + 'dmp', + ], + 'application/vnd.tmobile-livetv' => 'tmo', + 'application/vnd.trid.tpt' => 'tpt', + 'application/vnd.triscape.mxs' => 'mxs', + 'application/vnd.trueapp' => 'tra', + 'application/vnd.ufdl' => [ + 'ufd', + 'ufdl', + ], + 'application/vnd.uiq.theme' => 'utz', + 'application/vnd.umajin' => 'umj', + 'application/vnd.unity' => 'unityweb', + 'application/vnd.uoml+xml' => 'uoml', + 'application/vnd.vcx' => 'vcx', + 'application/vnd.visio' => [ + 'vsd', + 'vst', + 'vss', + 'vsw', + ], + 'application/vnd.visionary' => 'vis', + 'application/vnd.vsf' => 'vsf', + 'application/vnd.wap.wbxml' => 'wbxml', + 'application/vnd.wap.wmlc' => 'wmlc', + 'application/vnd.wap.wmlscriptc' => 'wmlsc', + 'application/vnd.webturbo' => 'wtb', + 'application/vnd.wolfram.player' => 'nbp', + 'application/vnd.wordperfect' => 'wpd', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wt.stf' => 'stf', + 'application/vnd.xara' => 'xar', + 'application/vnd.xfdl' => 'xfdl', + 'application/vnd.yamaha.hv-dic' => 'hvd', + 'application/vnd.yamaha.hv-script' => 'hvs', + 'application/vnd.yamaha.hv-voice' => 'hvp', + 'application/vnd.yamaha.openscoreformat' => 'osf', + 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg', + 'application/vnd.yamaha.smaf-audio' => 'saf', + 'application/vnd.yamaha.smaf-phrase' => 'spf', + 'application/vnd.yellowriver-custom-menu' => 'cmp', + 'application/vnd.zul' => [ + 'zir', + 'zirz', + ], + 'application/vnd.zzazz.deck+xml' => 'zaz', + 'application/voicexml+xml' => 'vxml', + 'application/wasm' => 'wasm', + 'application/widget' => 'wgt', + 'application/winhlp' => 'hlp', + 'application/wsdl+xml' => 'wsdl', + 'application/wspolicy+xml' => 'wspolicy', + 'application/x-7z-compressed' => '7z', + 'application/x-abiword' => 'abw', + 'application/x-ace-compressed' => 'ace', + 'application/x-apple-diskimage' => 'dmg', + 'application/x-authorware-bin' => [ + 'aab', + 'x32', + 'u32', + 'vox', + ], + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bittorrent' => 'torrent', + 'application/x-blorb' => [ + 'blb', + 'blorb', + ], + 'application/x-bzip' => 'bz', + 'application/x-bzip2' => [ + 'bz2', + 'boz', + ], + 'application/x-cbr' => [ + 'cbr', + 'cba', + 'cbt', + 'cbz', + 'cb7', + ], + 'application/x-cdlink' => 'vcd', + 'application/x-cfs-compressed' => 'cfs', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-conference' => 'nsc', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => [ + 'deb', + 'udeb', + ], + 'application/x-dgc-compressed' => 'dgc', + 'application/x-director' => [ + 'dir', + 'dcr', + 'dxr', + 'cst', + 'cct', + 'cxt', + 'w3d', + 'fgd', + 'swa', + ], + 'application/x-doom' => 'wad', + 'application/x-dtbncx+xml' => 'ncx', + 'application/x-dtbook+xml' => 'dtb', + 'application/x-dtbresource+xml' => 'res', + 'application/x-dvi' => 'dvi', + 'application/x-envoy' => 'evy', + 'application/x-eva' => 'eva', + 'application/x-font-bdf' => 'bdf', + 'application/x-font-ghostscript' => 'gsf', + 'application/x-font-linux-psf' => 'psf', + 'application/x-font-pcf' => 'pcf', + 'application/x-font-snf' => 'snf', + 'application/x-font-type1' => [ + 'pfa', + 'pfb', + 'pfm', + 'afm', + ], + 'application/x-freearc' => 'arc', + 'application/x-futuresplash' => 'spl', + 'application/x-gca-compressed' => 'gca', + 'application/x-glulx' => 'ulx', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gramps-xml' => 'gramps', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-install-instructions' => 'install', + 'application/x-iso9660-image' => 'iso', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-lzh-compressed' => [ + 'lzh', + 'lha', + ], + 'application/x-mie' => 'mie', + 'application/x-mobipocket-ebook' => [ + 'prc', + 'mobi', + ], + 'application/x-ms-application' => 'application', + 'application/x-ms-shortcut' => 'lnk', + 'application/x-ms-wmd' => 'wmd', + 'application/x-ms-wmz' => 'wmz', + 'application/x-ms-xbap' => 'xbap', + 'application/x-msaccess' => 'mdb', + 'application/x-msbinder' => 'obd', + 'application/x-mscardfile' => 'crd', + 'application/x-msclip' => 'clp', + 'application/x-msdownload' => [ + 'exe', + 'dll', + 'com', + 'bat', + 'msi', + ], + 'application/x-msmediaview' => [ + 'mvb', + 'm13', + 'm14', + ], + 'application/x-msmetafile' => [ + 'wmf', + 'wmz', + 'emf', + 'emz', + ], + 'application/x-msmoney' => 'mny', + 'application/x-mspublisher' => 'pub', + 'application/x-msschedule' => 'scd', + 'application/x-msterminal' => 'trm', + 'application/x-mswrite' => 'wri', + 'application/x-netcdf' => [ + 'nc', + 'cdf', + ], + 'application/x-nzb' => 'nzb', + 'application/x-pkcs12' => [ + 'p12', + 'pfx', + ], + 'application/x-pkcs7-certificates' => [ + 'p7b', + 'spc', + ], + 'application/x-pkcs7-certreqresp' => 'p7r', + 'application/x-rar-compressed' => 'rar', + 'application/x-research-info-systems' => 'ris', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-sql' => 'sql', + 'application/x-stuffit' => 'sit', + 'application/x-stuffitx' => 'sitx', + 'application/x-subrip' => 'srt', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-t3vm-image' => 't3', + 'application/x-tads' => 'gam', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => [ + 'texinfo', + 'texi', + ], + 'application/x-tgif' => 'obj', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => [ + 'der', + 'crt', + ], + 'application/x-xfig' => 'fig', + 'application/x-xliff+xml' => 'xlf', + 'application/x-xpinstall' => 'xpi', + 'application/x-xz' => 'xz', + 'application/x-zmachine' => [ + 'z1', + 'z2', + 'z3', + 'z4', + 'z5', + 'z6', + 'z7', + 'z8', + ], + 'application/xaml+xml' => 'xaml', + 'application/xcap-diff+xml' => 'xdf', + 'application/xenc+xml' => 'xenc', + 'application/xhtml+xml' => [ + 'xhtml', + 'xht', + ], + 'application/xml' => [ + 'xml', + 'xsl', + ], + 'application/xml-dtd' => 'dtd', + 'application/xop+xml' => 'xop', + 'application/xproc+xml' => 'xpl', + 'application/xslt+xml' => 'xslt', + 'application/xspf+xml' => 'xspf', + 'application/xv+xml' => [ + 'mxml', + 'xhvml', + 'xvml', + 'xvm', + ], + 'application/yang' => 'yang', + 'application/yin+xml' => 'yin', + 'application/zip' => 'zip', + 'audio/adpcm' => 'adp', + 'audio/basic' => [ + 'au', + 'snd', + ], + 'audio/midi' => [ + 'mid', + 'midi', + 'kar', + 'rmi', + ], + 'audio/mp4' => [ + 'm4a', + 'mp4a', + ], + 'audio/mpeg' => [ + 'mpga', + 'mp2', + 'mp2a', + 'mp3', + 'm2a', + 'm3a', + ], + 'audio/ogg' => [ + 'oga', + 'ogg', + 'spx', + 'opus', + ], + 'audio/s3m' => 's3m', + 'audio/silk' => 'sil', + 'audio/vnd.dece.audio' => [ + 'uva', + 'uvva', + ], + 'audio/vnd.digital-winds' => 'eol', + 'audio/vnd.dra' => 'dra', + 'audio/vnd.dts' => 'dts', + 'audio/vnd.dts.hd' => 'dtshd', + 'audio/vnd.lucent.voice' => 'lvp', + 'audio/vnd.ms-playready.media.pya' => 'pya', + 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', + 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', + 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', + 'audio/vnd.rip' => 'rip', + 'audio/webm' => 'weba', + 'audio/x-aac' => 'aac', + 'audio/x-aiff' => [ + 'aif', + 'aiff', + 'aifc', + ], + 'audio/x-caf' => 'caf', + 'audio/x-flac' => 'flac', + 'audio/x-matroska' => 'mka', + 'audio/x-mpegurl' => 'm3u', + 'audio/x-ms-wax' => 'wax', + 'audio/x-ms-wma' => 'wma', + 'audio/x-pn-realaudio' => [ + 'ram', + 'ra', + ], + 'audio/x-pn-realaudio-plugin' => 'rmp', + 'audio/x-wav' => 'wav', + 'audio/xm' => 'xm', + 'chemical/x-cdx' => 'cdx', + 'chemical/x-cif' => 'cif', + 'chemical/x-cmdf' => 'cmdf', + 'chemical/x-cml' => 'cml', + 'chemical/x-csml' => 'csml', + 'chemical/x-xyz' => 'xyz', + 'font/collection' => 'ttc', + 'font/otf' => 'otf', + 'font/ttf' => 'ttf', + 'font/woff' => 'woff', + 'font/woff2' => 'woff2', + 'image/apng' => 'apng', + 'image/avif' => 'avif', + 'image/bmp' => 'bmp', + 'image/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => [ + 'jpeg', + 'jpg', + 'jpe', + 'jfif', + 'pjp', + 'pjpeg', + ], + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + 'image/sgi' => 'sgi', + 'image/svg+xml' => [ + 'svg', + 'svgz', + ], + 'image/tiff' => [ + 'tiff', + 'tif', + ], + 'image/vnd.adobe.photoshop' => 'psd', + 'image/vnd.dece.graphic' => [ + 'uvi', + 'uvvi', + 'uvg', + 'uvvg', + ], + 'image/vnd.djvu' => [ + 'djvu', + 'djv', + ], + 'image/vnd.dvb.subtitle' => 'sub', + 'image/vnd.dwg' => 'dwg', + 'image/vnd.dxf' => 'dxf', + 'image/vnd.fastbidsheet' => 'fbs', + 'image/vnd.fpx' => 'fpx', + 'image/vnd.fst' => 'fst', + 'image/vnd.fujixerox.edmics-mmr' => 'mmr', + 'image/vnd.fujixerox.edmics-rlc' => 'rlc', + 'image/vnd.ms-modi' => 'mdi', + 'image/vnd.ms-photo' => 'wdp', + 'image/vnd.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-3ds' => '3ds', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => [ + 'fh', + 'fhc', + 'fh4', + 'fh5', + 'fh7', + ], + 'image/x-icon' => 'ico', + 'image/x-mrsid-image' => 'sid', + 'image/x-pcx' => 'pcx', + 'image/x-pict' => [ + 'pic', + 'pct', + ], + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-tga' => 'tga', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/rfc822' => [ + 'eml', + 'mime', + ], + 'model/iges' => [ + 'igs', + 'iges', + ], + 'model/mesh' => [ + 'msh', + 'mesh', + 'silo', + ], + 'model/vnd.collada+xml' => 'dae', + 'model/vnd.dwf' => 'dwf', + 'model/vnd.gdl' => 'gdl', + 'model/vnd.gtw' => 'gtw', + 'model/vnd.mts' => 'mts', + 'model/vnd.vtu' => 'vtu', + 'model/vrml' => [ + 'wrl', + 'vrml', + ], + 'model/x3d+binary' => [ + 'x3db', + 'x3dbz', + ], + 'model/x3d+vrml' => [ + 'x3dv', + 'x3dvz', + ], + 'model/x3d+xml' => [ + 'x3d', + 'x3dz', + ], + 'text/cache-manifest' => 'appcache', + 'text/calendar' => [ + 'ics', + 'ifb', + ], + 'text/css' => 'css', + 'text/csv' => 'csv', + 'text/html' => [ + 'html', + 'htm', + ], + 'text/javascript' => [ + 'js', + 'mjs', + 'mjs', + ], + 'text/n3' => 'n3', + 'text/plain' => [ + 'txt', + 'text', + 'conf', + 'def', + 'list', + 'log', + 'in', + ], + 'text/prs.lines.tag' => 'dsc', + 'text/richtext' => 'rtx', + 'text/sgml' => [ + 'sgml', + 'sgm', + ], + 'text/tab-separated-values' => 'tsv', + 'text/troff' => [ + 't', + 'tr', + 'roff', + 'man', + 'me', + 'ms', + ], + 'text/turtle' => 'ttl', + 'text/uri-list' => [ + 'uri', + 'uris', + 'urls', + ], + 'text/vcard' => 'vcard', + 'text/vnd.curl' => 'curl', + 'text/vnd.curl.dcurl' => 'dcurl', + 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.curl.scurl' => 'scurl', + 'text/vnd.dvb.subtitle' => 'sub', + 'text/vnd.fly' => 'fly', + 'text/vnd.fmi.flexstor' => 'flx', + 'text/vnd.graphviz' => 'gv', + 'text/vnd.in3d.3dml' => '3dml', + 'text/vnd.in3d.spot' => 'spot', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/x-asm' => [ + 's', + 'asm', + ], + 'text/x-c' => [ + 'c', + 'cc', + 'cxx', + 'cpp', + 'h', + 'hh', + 'dic', + ], + 'text/x-fortran' => [ + 'f', + 'for', + 'f77', + 'f90', + ], + 'text/x-java-source' => 'java', + 'text/x-nfo' => 'nfo', + 'text/x-opml' => 'opml', + 'text/x-pascal' => [ + 'p', + 'pas', + ], + 'text/x-setext' => 'etx', + 'text/x-sfv' => 'sfv', + 'text/x-uuencode' => 'uu', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'video/3gpp' => '3gp', + 'video/3gpp2' => '3g2', + 'video/h261' => 'h261', + 'video/h263' => 'h263', + 'video/h264' => 'h264', + 'video/jpeg' => 'jpgv', + 'video/jpm' => [ + 'jpm', + 'jpgm', + ], + 'video/mj2' => [ + 'mj2', + 'mjp2', + ], + 'video/mp4' => [ + 'mp4', + 'mp4v', + 'mpg4', + ], + 'video/mpeg' => [ + 'mpeg', + 'mpg', + 'mpe', + 'm1v', + 'm2v', + ], + 'video/ogg' => 'ogv', + 'video/quicktime' => [ + 'qt', + 'mov', + ], + 'video/vnd.dece.hd' => [ + 'uvh', + 'uvvh', + ], + 'video/vnd.dece.mobile' => [ + 'uvm', + 'uvvm', + ], + 'video/vnd.dece.pd' => [ + 'uvp', + 'uvvp', + ], + 'video/vnd.dece.sd' => [ + 'uvs', + 'uvvs', + ], + 'video/vnd.dece.video' => [ + 'uvv', + 'uvvv', + ], + 'video/vnd.dvb.file' => 'dvb', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => [ + 'mxu', + 'm4u', + ], + 'video/vnd.ms-playready.media.pyv' => 'pyv', + 'video/vnd.uvvu.mp4' => [ + 'uvu', + 'uvvu', + ], + 'video/vnd.vivo' => 'viv', + 'video/webm' => 'webm', + 'video/x-f4v' => 'f4v', + 'video/x-fli' => 'fli', + 'video/x-flv' => 'flv', + 'video/x-m4v' => 'm4v', + 'video/x-matroska' => [ + 'mkv', + 'mk3d', + 'mks', + ], + 'video/x-mng' => 'mng', + 'video/x-ms-asf' => [ + 'asf', + 'asx', + ], + 'video/x-ms-vob' => 'vob', + 'video/x-ms-wm' => 'wm', + 'video/x-ms-wmv' => 'wmv', + 'video/x-ms-wmx' => 'wmx', + 'video/x-ms-wvx' => 'wvx', + 'video/x-msvideo' => 'avi', + 'video/x-sgi-movie' => 'movie', + 'video/x-smv' => 'smv', + 'x-conference/x-cooltalk' => 'ice', +]; diff --git a/framework/helpers/mimeTypes.php b/framework/helpers/mimeTypes.php index 8f95e4be857..e91f80f95f8 100644 --- a/framework/helpers/mimeTypes.php +++ b/framework/helpers/mimeTypes.php @@ -9,6 +9,7 @@ * This file has been placed in the public domain for unlimited redistribution. */ $mimeTypes = [ + 123 => 'application/vnd.lotus-1-2-3', '3dml' => 'text/vnd.in3d.3dml', '3ds' => 'image/x-3ds', '3g2' => 'video/3gpp2', @@ -37,6 +38,7 @@ 'ait' => 'application/vnd.dvb.ait', 'ami' => 'application/vnd.amiga.ami', 'apk' => 'application/vnd.android.package-archive', + 'apng' => 'image/apng', 'appcache' => 'text/cache-manifest', 'application' => 'application/x-ms-application', 'apr' => 'application/vnd.lotus-approach', @@ -53,6 +55,7 @@ 'atx' => 'application/vnd.antix.game-component', 'au' => 'audio/basic', 'avi' => 'video/x-msvideo', + 'avif' => 'image/avif', 'aw' => 'application/applixware', 'azf' => 'application/vnd.airzip.filesecure.azf', 'azs' => 'application/vnd.airzip.filesecure.azs', @@ -279,6 +282,7 @@ 'geo' => 'application/vnd.dynageo', 'gex' => 'application/vnd.geometry-explorer', 'ggb' => 'application/vnd.geogebra.file', + 'ggs' => 'application/vnd.geogebra.slides', 'ggt' => 'application/vnd.geogebra.tool', 'ghf' => 'application/vnd.groove-help', 'gif' => 'image/gif', @@ -318,12 +322,11 @@ 'htke' => 'application/vnd.kenameaapp', 'htm' => 'text/html', 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', 'hvp' => 'application/vnd.yamaha.hv-voice', 'hvs' => 'application/vnd.yamaha.hv-script', 'i2g' => 'application/vnd.intergeo', 'icc' => 'application/vnd.iccprofile', - 0 => 'application/vnd.lotus-1-2-3', - 'hvd' => 'application/vnd.yamaha.hv-dic', 'ice' => 'x-conference/x-cooltalk', 'icm' => 'application/vnd.iccprofile', 'ico' => 'image/x-icon', @@ -356,6 +359,7 @@ 'jam' => 'application/vnd.jam', 'jar' => 'application/java-archive', 'java' => 'text/x-java-source', + 'jfif' => 'image/jpeg', 'jisp' => 'application/vnd.jisp', 'jlt' => 'application/vnd.hp-jlyt', 'jnlp' => 'application/x-java-jnlp-file', @@ -598,6 +602,8 @@ 'pgn' => 'application/x-chess-pgn', 'pgp' => 'application/pgp-encrypted', 'pic' => 'image/x-pict', + 'pjp' => 'image/jpeg', + 'pjpeg' => 'image/jpeg', 'pkg' => 'application/octet-stream', 'pki' => 'application/pkixcmp', 'pkipath' => 'application/pkix-pkipath', @@ -882,6 +888,7 @@ 'vxml' => 'application/voicexml+xml', 'w3d' => 'application/x-director', 'wad' => 'application/x-doom', + 'wasm' => 'application/wasm', 'wav' => 'audio/x-wav', 'wax' => 'audio/x-ms-wax', 'wbmp' => 'image/vnd.wap.wbmp', @@ -996,8 +1003,9 @@ 'zmm' => 'application/vnd.handheld-entertainment+xml', ]; -if (PHP_VERSION_ID >= 80100) { +# fix for bundled libmagic bug, see also https://github.com/yiisoft/yii2/issues/19925 +if ((PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80122) || (PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80209)) { $mimeTypes = array_replace($mimeTypes, array('xz' => 'application/octet-stream')); } -return $mimeTypes; \ No newline at end of file +return $mimeTypes; diff --git a/framework/i18n/Formatter.php b/framework/i18n/Formatter.php index 3236a433beb..001b81bfa90 100644 --- a/framework/i18n/Formatter.php +++ b/framework/i18n/Formatter.php @@ -460,7 +460,7 @@ public function format($value, $format) } $method = 'as' . $format; if ($this->hasMethod($method)) { - return call_user_func_array([$this, $method], $params); + return call_user_func_array([$this, $method], array_values($params)); } throw new InvalidArgumentException("Unknown format type: $format"); diff --git a/framework/log/FileTarget.php b/framework/log/FileTarget.php index 05ad09430af..d816f73a8db 100644 --- a/framework/log/FileTarget.php +++ b/framework/log/FileTarget.php @@ -106,12 +106,18 @@ public function init() */ public function export() { + $text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n"; + $trimmedText = trim($text); + + if (empty($trimmedText)) { + return; // No messages to export, so we exit the function early + } + if (strpos($this->logFile, '://') === false || strncmp($this->logFile, 'file://', 7) === 0) { $logPath = dirname($this->logFile); FileHelper::createDirectory($logPath, $this->dirMode, true); } - $text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n"; if (($fp = @fopen($this->logFile, 'a')) === false) { throw new InvalidConfigException("Unable to append to log file: {$this->logFile}"); } diff --git a/framework/messages/zh-CN/yii.php b/framework/messages/zh/yii.php similarity index 100% rename from framework/messages/zh-CN/yii.php rename to framework/messages/zh/yii.php diff --git a/framework/rbac/DbManager.php b/framework/rbac/DbManager.php index d676cbf8cf5..2685ad5a1cd 100644 --- a/framework/rbac/DbManager.php +++ b/framework/rbac/DbManager.php @@ -882,6 +882,9 @@ public function assign($role, $userId) ])->execute(); unset($this->checkAccessAssignments[(string) $userId]); + + $this->invalidateCache(); + return $assignment; } @@ -895,9 +898,13 @@ public function revoke($role, $userId) } unset($this->checkAccessAssignments[(string) $userId]); - return $this->db->createCommand() + $result = $this->db->createCommand() ->delete($this->assignmentTable, ['user_id' => (string) $userId, 'item_name' => $role->name]) ->execute() > 0; + + $this->invalidateCache(); + + return $result; } /** @@ -910,9 +917,13 @@ public function revokeAll($userId) } unset($this->checkAccessAssignments[(string) $userId]); - return $this->db->createCommand() + $result = $this->db->createCommand() ->delete($this->assignmentTable, ['user_id' => (string) $userId]) ->execute() > 0; + + $this->invalidateCache(); + + return $result; } /** diff --git a/framework/rest/Serializer.php b/framework/rest/Serializer.php index a4d16b195f2..edc689e94e8 100644 --- a/framework/rest/Serializer.php +++ b/framework/rest/Serializer.php @@ -294,10 +294,9 @@ protected function serializeModelErrors($model) */ protected function serializeModels(array $models) { - list($fields, $expand) = $this->getRequestedFields(); foreach ($models as $i => $model) { if ($model instanceof Arrayable) { - $models[$i] = $model->toArray($fields, $expand); + $models[$i] = $this->serializeModel($model); } elseif (is_array($model)) { $models[$i] = ArrayHelper::toArray($model); } diff --git a/framework/web/Controller.php b/framework/web/Controller.php index 52de6c998e7..86be85819f0 100644 --- a/framework/web/Controller.php +++ b/framework/web/Controller.php @@ -17,10 +17,6 @@ * * For more details and usage information on Controller, see the [guide article on controllers](guide:structure-controllers). * - * @property Request $request The request object. - * @property Response $response The response object. - * @property View $view The view object that can be used to render views or view files. - * * @author Qiang Xue * @since 2.0 */ @@ -145,6 +141,7 @@ public function bindActionParams($action, $params) } elseif ( PHP_VERSION_ID >= 70000 && ($type = $param->getType()) !== null + && method_exists($type, 'isBuiltin') && $type->isBuiltin() && ($params[$name] !== null || !$type->allowsNull()) ) { diff --git a/framework/web/Cookie.php b/framework/web/Cookie.php index ac9a7233710..d249afa6cbd 100644 --- a/framework/web/Cookie.php +++ b/framework/web/Cookie.php @@ -57,8 +57,8 @@ class Cookie extends \yii\base\BaseObject */ public $domain = ''; /** - * @var int the timestamp at which the cookie expires. This is the server timestamp. - * Defaults to 0, meaning "until the browser is closed". + * @var int|string|\DateTimeInterface|null the timestamp or date at which the cookie expires. This is the server timestamp. + * Defaults to 0, meaning "until the browser is closed" (the same applies to `null`). */ public $expire = 0; /** diff --git a/framework/web/CookieCollection.php b/framework/web/CookieCollection.php index aa09b9ca455..597fc2f5fd7 100644 --- a/framework/web/CookieCollection.php +++ b/framework/web/CookieCollection.php @@ -18,7 +18,6 @@ * For more details and usage information on CookieCollection, see the [guide article on handling cookies](guide:runtime-sessions-cookies). * * @property-read int $count The number of cookies in the collection. - * @property-read ArrayIterator $iterator An iterator for traversing the cookies in the collection. * * @author Qiang Xue * @since 2.0 @@ -52,7 +51,7 @@ public function __construct($cookies = [], $config = []) * Returns an iterator for traversing the cookies in the collection. * This method is required by the SPL interface [[\IteratorAggregate]]. * It will be implicitly called when you use `foreach` to traverse the collection. - * @return ArrayIterator an iterator for traversing the cookies in the collection. + * @return ArrayIterator an iterator for traversing the cookies in the collection. */ #[\ReturnTypeWillChange] public function getIterator() @@ -114,7 +113,18 @@ public function getValue($name, $defaultValue = null) public function has($name) { return isset($this->_cookies[$name]) && $this->_cookies[$name]->value !== '' - && ($this->_cookies[$name]->expire === null || $this->_cookies[$name]->expire === 0 || $this->_cookies[$name]->expire >= time()); + && ($this->_cookies[$name]->expire === null + || $this->_cookies[$name]->expire === 0 + || ( + (is_string($this->_cookies[$name]->expire) && strtotime($this->_cookies[$name]->expire) >= time()) + || ( + interface_exists('\\DateTimeInterface') + && $this->_cookies[$name]->expire instanceof \DateTimeInterface + && $this->_cookies[$name]->expire->getTimestamp() >= time() + ) + || $this->_cookies[$name]->expire >= time() + ) + ); } /** @@ -175,7 +185,7 @@ public function removeAll() /** * Returns the collection as a PHP array. - * @return array the array representation of the collection. + * @return Cookie[] the array representation of the collection. * The array keys are cookie names, and the array values are the corresponding cookie objects. */ public function toArray() diff --git a/framework/web/HeaderCollection.php b/framework/web/HeaderCollection.php index 07d6502ef46..724e875bc69 100644 --- a/framework/web/HeaderCollection.php +++ b/framework/web/HeaderCollection.php @@ -13,9 +13,6 @@ /** * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers. * - * @property-read int $count The number of headers in the collection. - * @property-read \ArrayIterator $iterator An iterator for traversing the headers in the collection. - * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/web/Request.php b/framework/web/Request.php index d3a716c97f0..d87489ce389 100644 --- a/framework/web/Request.php +++ b/framework/web/Request.php @@ -1220,6 +1220,8 @@ protected function getUserIpFromIpHeaders() return null; } + private $_ip = null; + /** * Returns the user IP address. * The IP is determined using headers and / or `$_SERVER` variables. @@ -1227,8 +1229,14 @@ protected function getUserIpFromIpHeaders() */ public function getUserIP() { - $ip = $this->getUserIpFromIpHeaders(); - return $ip === null ? $this->getRemoteIP() : $ip; + if ($this->_ip === null) { + $this->_ip = $this->getUserIpFromIpHeaders(); + if ($this->_ip === null) { + $this->_ip = $this->getRemoteIP(); + } + } + + return $this->_ip; } /** @@ -1902,6 +1910,8 @@ protected function getSecureForwardedHeaderTrustedPart($token) return null; } + private $_secureForwardedHeaderTrustedParts; + /** * Gets only trusted `Forwarded` header parts * @@ -1911,6 +1921,10 @@ protected function getSecureForwardedHeaderTrustedPart($token) */ protected function getSecureForwardedHeaderTrustedParts() { + if ($this->_secureForwardedHeaderTrustedParts !== null) { + return $this->_secureForwardedHeaderTrustedParts; + } + $validator = $this->getIpValidator(); $trustedHosts = []; foreach ($this->trustedHosts as $trustedCidr => $trustedCidrOrHeaders) { @@ -1921,9 +1935,14 @@ protected function getSecureForwardedHeaderTrustedParts() } $validator->setRanges($trustedHosts); - return array_filter($this->getSecureForwardedHeaderParts(), function ($headerPart) use ($validator) { - return isset($headerPart['for']) ? !$validator->validate($headerPart['for']) : true; - }); + $this->_secureForwardedHeaderTrustedParts = array_filter( + $this->getSecureForwardedHeaderParts(), + function ($headerPart) use ($validator) { + return isset($headerPart['for']) ? !$validator->validate($headerPart['for']) : true; + } + ); + + return $this->_secureForwardedHeaderTrustedParts; } private $_secureForwardedHeaderParts; diff --git a/framework/web/Response.php b/framework/web/Response.php index bc06a4b04dd..1724438803e 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -10,6 +10,7 @@ use Yii; use yii\base\InvalidArgumentException; use yii\base\InvalidConfigException; +use yii\base\InvalidRouteException; use yii\helpers\FileHelper; use yii\helpers\Inflector; use yii\helpers\StringHelper; @@ -400,12 +401,21 @@ protected function sendCookies() } foreach ($this->getCookies() as $cookie) { $value = $cookie->value; - if ($cookie->expire != 1 && isset($validationKey)) { + $expire = $cookie->expire; + if (is_string($expire)) { + $expire = strtotime($expire); + } elseif (interface_exists('\\DateTimeInterface') && $expire instanceof \DateTimeInterface) { + $expire = $expire->getTimestamp(); + } + if ($expire === null || $expire === false) { + $expire = 0; + } + if ($expire != 1 && isset($validationKey)) { $value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey); } if (PHP_VERSION_ID >= 70300) { setcookie($cookie->name, $value, [ - 'expires' => $cookie->expire, + 'expires' => $expire, 'path' => $cookie->path, 'domain' => $cookie->domain, 'secure' => $cookie->secure, @@ -419,7 +429,7 @@ protected function sendCookies() if (!is_null($cookie->sameSite)) { $cookiePath .= '; samesite=' . $cookie->sameSite; } - setcookie($cookie->name, $value, $cookie->expire, $cookiePath, $cookie->domain, $cookie->secure, $cookie->httpOnly); + setcookie($cookie->name, $value, $expire, $cookiePath, $cookie->domain, $cookie->secure, $cookie->httpOnly); } } } @@ -886,12 +896,13 @@ public function redirect($url, $statusCode = 302, $checkAjax = true) } $request = Yii::$app->getRequest(); $normalizedUrl = Url::to($url); - if ( - $normalizedUrl !== null - && strncmp($normalizedUrl, '/', 1) === 0 - && strncmp($normalizedUrl, '//', 2) !== 0 - ) { - $normalizedUrl = $request->getHostInfo() . $normalizedUrl; + if ($normalizedUrl !== null) { + if (preg_match('/\n/', $normalizedUrl)) { + throw new InvalidRouteException('Route with new line character detected "' . $normalizedUrl . '".'); + } + if (strncmp($normalizedUrl, '/', 1) === 0 && strncmp($normalizedUrl, '//', 2) !== 0) { + $normalizedUrl = $request->getHostInfo() . $normalizedUrl; + } } if ($checkAjax && $request->getIsAjax()) { diff --git a/framework/web/Session.php b/framework/web/Session.php index 01997ff5f5f..40768924cf5 100644 --- a/framework/web/Session.php +++ b/framework/web/Session.php @@ -57,7 +57,6 @@ * @property bool $hasSessionId Whether the current request has sent the session ID. * @property string $id The current session ID. * @property-read bool $isActive Whether the session has started. - * @property-read SessionIterator $iterator An iterator for traversing the session variables. * @property string $name The current session name. * @property string $savePath The current session save path, defaults to '/tmp'. * @property int $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up. The @@ -406,7 +405,7 @@ public function getCookieParams() * 'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null, * ] * ``` - * See https://www.owasp.org/index.php/SameSite for more information about `sameSite`. + * See https://owasp.org/www-community/SameSite for more information about `sameSite`. * * @throws InvalidArgumentException if the parameters are incomplete. * @see https://www.php.net/manual/en/function.session-set-cookie-params.php diff --git a/framework/web/View.php b/framework/web/View.php index c621e5777e5..f2c19fc721f 100644 --- a/framework/web/View.php +++ b/framework/web/View.php @@ -133,6 +133,7 @@ class View extends \yii\base\View private $_assetManager; + /** * Whether [[endPage()]] has been called and all files have been registered * @var bool @@ -140,7 +141,6 @@ class View extends \yii\base\View */ protected $isPageEnded = false; - /** * Marks the position of an HTML head section. */ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 34e150ef8ed..2b9f7c6df55 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,7 +21,6 @@ framework/web/ResponseFormatterInterface.php framework/.phpstorm.meta.php framework/base - framework/db/mssql framework/bootstrap diff --git a/tests/TestCase.php b/tests/TestCase.php index d18e1145949..5a8287f1f55 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -123,6 +123,39 @@ protected function assertEqualsWithoutLE($expected, $actual, $message = '') $this->assertEquals($expected, $actual, $message); } + /** + * Asserting two strings equality ignoring unicode whitespaces. + * @param string $expected + * @param string $actual + * @param string $message + */ + protected function assertEqualsAnyWhitespace($expected, $actual, $message = ''){ + $expected = $this->sanitizeWhitespaces($expected); + $actual = $this->sanitizeWhitespaces($actual); + + $this->assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two variables have the same type and value and sanitizes value if it is a string. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ + protected function assertSameAnyWhitespace($expected, $actual, $message = ''){ + if (is_string($expected)) { + $expected = $this->sanitizeWhitespaces($expected); + } + if (is_string($actual)) { + $actual = $this->sanitizeWhitespaces($actual); + } + + $this->assertSame($expected, $actual, $message); + } + /** * Asserts that a haystack contains a needle ignoring line endings. * @@ -138,6 +171,17 @@ protected function assertContainsWithoutLE($needle, $haystack, $message = '') $this->assertContains($needle, $haystack, $message); } + /** + * Replaces unicode whitespaces with standard whitespace + * + * @see https://github.com/yiisoft/yii2/issues/19868 (ICU 72 changes) + * @param $string + * @return string + */ + protected function sanitizeWhitespaces($string){ + return preg_replace("/[\pZ\pC]/u", " ", $string); + } + /** * Invokes a inaccessible method. * @param $object diff --git a/tests/data/ar/NoAutoLabels.php b/tests/data/ar/NoAutoLabels.php new file mode 100644 index 00000000000..e33a3847d84 --- /dev/null +++ b/tests/data/ar/NoAutoLabels.php @@ -0,0 +1,27 @@ + 'Label for attr1', + ]; + } + + public function generateAttributeLabel($name) + { + throw new \yii\base\InvalidArgumentException('Label not defined!'); + } +} diff --git a/tests/framework/base/DynamicModelTest.php b/tests/framework/base/DynamicModelTest.php index 0c4a25110b3..d3dca504b6f 100644 --- a/tests/framework/base/DynamicModelTest.php +++ b/tests/framework/base/DynamicModelTest.php @@ -37,6 +37,20 @@ public function testValidateData() $this->assertTrue($model->hasErrors('age')); } + public function testValidateDataWithPostData() + { + $post = [ + 'name' => 'long name', + ]; + $model = DynamicModel::validateData($post, [ + [['email', 'name'], 'required'], + ['age', 'default', 'value' => 18], + ]); + $this->assertTrue($model->hasErrors()); + $this->assertTrue($model->hasErrors('email')); + $this->assertEquals(18, $model->age); + } + public function testAddRule() { $model = new DynamicModel(); diff --git a/tests/framework/caching/ApcCacheTest.php b/tests/framework/caching/ApcCacheTest.php index 809286bbd89..7f3311b7702 100644 --- a/tests/framework/caching/ApcCacheTest.php +++ b/tests/framework/caching/ApcCacheTest.php @@ -23,7 +23,7 @@ class ApcCacheTest extends CacheTestCase */ protected function getCacheInstance() { - if (!extension_loaded('apc')) { + if (!extension_loaded('apc') && !extension_loaded('apcu')) { $this->markTestSkipped('APC not installed. Skipping.'); } elseif ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) { $this->markTestSkipped('APC cli is not enabled. Skipping.'); @@ -33,7 +33,9 @@ protected function getCacheInstance() $this->markTestSkipped('APC is installed but not enabled. Skipping.'); } - if ($this->_cacheInstance === null) { + if ($this->_cacheInstance === null && PHP_VERSION_ID >= 70400) { + $this->_cacheInstance = new ApcCache(['useApcu' => true]); + } elseif ($this->_cacheInstance === null) { $this->_cacheInstance = new ApcCache(); } diff --git a/tests/framework/caching/MemCachedTest.php b/tests/framework/caching/MemCachedTest.php index 8b6b735b433..742d53d05ae 100644 --- a/tests/framework/caching/MemCachedTest.php +++ b/tests/framework/caching/MemCachedTest.php @@ -27,7 +27,9 @@ protected function getCacheInstance() $this->markTestSkipped('memcached not installed. Skipping.'); } - if (PHP_VERSION_ID >= 80100 && version_compare(phpversion('memcached'), '3.1.5', '<=')) { + if ( + PHP_VERSION_ID >= 80100 && version_compare(phpversion('memcached'), '3.1.5', '<=') + ) { $php_version = phpversion(); $memcached_version = phpversion('memcached'); $this->markTestSkipped("memcached version $memcached_version is not ready for PHP $php_version. Skipping."); diff --git a/tests/framework/console/widgets/TableTest.php b/tests/framework/console/widgets/TableTest.php index 95e7c60e462..796c95ad1d6 100644 --- a/tests/framework/console/widgets/TableTest.php +++ b/tests/framework/console/widgets/TableTest.php @@ -58,6 +58,121 @@ public function testTable($headers, $rows) ║ testcontent21 │ testcontent22 │ testcontent23 ║ ╚═══════════════╧═══════════════╧═══════════════╝ +EXPECTED; + + $tableContent = $table + ->setHeaders($headers) + ->setRows($rows) + ->setScreenWidth(200) + ->run(); + $this->assertEqualsWithoutLE($expected, $tableContent); + } + + public function getMultiLineTableData() + { + return [ + [ + ['test1', 'test2', 'test3' . PHP_EOL . 'multiline'], + [ + ['test' . PHP_EOL . 'content1', 'testcontent2', 'test' . PHP_EOL . 'content3'], + [ + 'testcontent21', + 'testcontent22' . PHP_EOL + . 'loooooooooooooooooooooooooooooooooooong' . PHP_EOL + . 'content', + 'testcontent23' . PHP_EOL + . 'loooooooooooooooooooooooooooooooooooong content' + ], + ] + ], + [ + ['key1' => 'test1', 'key2' => 'test2', 'key3' => 'test3' . PHP_EOL . 'multiline'], + [ + [ + 'key1' => 'test' . PHP_EOL . 'content1', + 'key2' => 'testcontent2', + 'key3' => 'test' . PHP_EOL . 'content3' + ], + [ + 'key1' => 'testcontent21', + 'key2' => 'testcontent22' . PHP_EOL + . 'loooooooooooooooooooooooooooooooooooong' . PHP_EOL + . 'content', + 'key3' => 'testcontent23' . PHP_EOL + . 'loooooooooooooooooooooooooooooooooooong content' + ], + ] + ] + ]; + } + + /** + * @dataProvider getMultiLineTableData + */ + public function testMultiLineTable($headers, $rows) + { + $table = new Table(); + + $expected = <<<'EXPECTED' +╔═════════════╤═════════════════════════════════════╤═════════════════════════════════════════════╗ +║ test1 │ test2 │ test3 ║ +║ │ │ multiline ║ +╟─────────────┼─────────────────────────────────────┼─────────────────────────────────────────────╢ +║ test │ testcontent2 │ test ║ +║ content1 │ │ content3 ║ +╟─────────────┼─────────────────────────────────────┼─────────────────────────────────────────────╢ +║ testcontent │ testcontent22 │ testcontent23 ║ +║ 21 │ loooooooooooooooooooooooooooooooooo │ loooooooooooooooooooooooooooooooooooong con ║ +║ │ oong │ tent ║ +║ │ content │ ║ +╚═════════════╧═════════════════════════════════════╧═════════════════════════════════════════════╝ + +EXPECTED; + + $tableContent = $table + ->setHeaders($headers) + ->setRows($rows) + ->setScreenWidth(100) + ->run(); + $this->assertEqualsWithoutLE($expected, $tableContent); + } + + public function getNumericTableData() + { + return [ + [ + [1, 2, 3], + [ + [1, 1.2, -1.3], + [-2, 2.2, 2.3], + ] + ], + [ + ['key1' => 1, 'key2' => 2, 'key3' => 3], + [ + ['key1' => 1, 'key2' => 1.2, 'key3' => -1.3], + ['key1' => -2, 'key2' => 2.2, 'key3' => 2.3], + ] + ] + ]; + } + + /** + * @dataProvider getNumericTableData + */ + public function testNumericTable($headers, $rows) + { + $table = new Table(); + + $expected = <<<'EXPECTED' +╔════╤═════╤══════╗ +║ 1 │ 2 │ 3 ║ +╟────┼─────┼──────╢ +║ 1 │ 1.2 │ -1.3 ║ +╟────┼─────┼──────╢ +║ -2 │ 2.2 │ 2.3 ║ +╚════╧═════╧══════╝ + EXPECTED; $tableContent = $table @@ -101,7 +216,8 @@ public function testLists() ╔═══════════════╤═══════════════╤══════════════╗ ║ test1 │ test2 │ test3 ║ ╟───────────────┼───────────────┼──────────────╢ -║ testcontent1 │ testcontent2 │ testcontent3 ║ +║ • col1 │ testcontent2 │ testcontent3 ║ +║ • col2 │ │ ║ ╟───────────────┼───────────────┼──────────────╢ ║ testcontent21 │ testcontent22 │ • col1 ║ ║ │ │ • col2 ║ @@ -111,7 +227,7 @@ public function testLists() $this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3']) ->setRows([ - ['testcontent1', 'testcontent2', 'testcontent3'], + [['key1' => 'col1', 'key2' => 'col2'], 'testcontent2', 'testcontent3'], ['testcontent21', 'testcontent22', ['col1', 'col2']], ])->setScreenWidth(200)->run() ); @@ -141,6 +257,35 @@ public function testListPrefix() ); } + public function testLongerListPrefix() + { + $table = new Table(); + + $expected = <<<'EXPECTED' +╔═════════════════════════════════╤═════════════════════════════════╤═════════════════════════════╗ +║ test1 │ test2 │ test3 ║ +╟─────────────────────────────────┼─────────────────────────────────┼─────────────────────────────╢ +║ testcontent1 │ testcontent2 │ testcontent3 ║ +╟─────────────────────────────────┼─────────────────────────────────┼─────────────────────────────╢ +║ testcontent21 with looooooooooo │ testcontent22 with looooooooooo │ -- col1 with looooooooooooo ║ +║ ooooooooooooong content │ ooooooooooooong content │ ooooooooooong content ║ +║ │ │ -- col2 with long content ║ +╚═════════════════════════════════╧═════════════════════════════════╧═════════════════════════════╝ + +EXPECTED; + + $this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3']) + ->setRows([ + ['testcontent1', 'testcontent2', 'testcontent3'], + [ + 'testcontent21 with loooooooooooooooooooooooong content', + 'testcontent22 with loooooooooooooooooooooooong content', + ['col1 with loooooooooooooooooooooooong content', 'col2 with long content'] + ], + ])->setScreenWidth(100)->setListPrefix('-- ')->run() + ); + } + public function testCustomChars() { $table = new Table(); diff --git a/tests/framework/db/ActiveRecordTest.php b/tests/framework/db/ActiveRecordTest.php index f1370f47282..b74803b94dd 100644 --- a/tests/framework/db/ActiveRecordTest.php +++ b/tests/framework/db/ActiveRecordTest.php @@ -2192,4 +2192,87 @@ public function testVirtualRelation() $this->assertNotNull($order->virtualCustomer); } + public function labelTestModelProvider() + { + $data = []; + + // Model 2 and 3 are represented by objects. + $model1 = new LabelTestModel1(); + $model2 = new LabelTestModel2(); + $model3 = new LabelTestModel3(); + $model2->populateRelation('model3', $model3); + $model1->populateRelation('model2', $model2); + $data[] = [$model1]; + + // Model 2 and 3 are represented by arrays instead of objects. + $model1 = new LabelTestModel1(); + $model2 = ['model3' => []]; + $model1->populateRelation('model2', $model2); + $data[] = [$model1]; + + return $data; + } + + /** + * @dataProvider labelTestModelProvider + * @param \yii\db\ActiveRecord $model + */ + public function testGetAttributeLabel($model) + { + $this->assertEquals('model3.attr1 from model2', $model->getAttributeLabel('model2.model3.attr1')); + $this->assertEquals('attr2 from model3', $model->getAttributeLabel('model2.model3.attr2')); + $this->assertEquals('model3.attr3 from model2', $model->getAttributeLabel('model2.model3.attr3')); + $attr = 'model2.doesNotExist.attr1'; + $this->assertEquals($model->generateAttributeLabel($attr), $model->getAttributeLabel($attr)); + } +} + +class LabelTestModel1 extends \yii\db\ActiveRecord +{ + public function attributes() + { + return []; + } + + public function getModel2() + { + return $this->hasOne(LabelTestModel2::className(), []); + } +} + +class LabelTestModel2 extends \yii\db\ActiveRecord +{ + public function attributes() + { + return []; + } + + public function getModel3() + { + return $this->hasOne(LabelTestModel3::className(), []); + } + + public function attributeLabels() + { + return [ + 'model3.attr1' => 'model3.attr1 from model2', // Override label defined in model3. + 'model3.attr3' => 'model3.attr3 from model2', // Define label not defined in model3. + ]; + } +} + +class LabelTestModel3 extends \yii\db\ActiveRecord +{ + public function attributes() + { + return ['attr1', 'attr2', 'attr3']; + } + + public function attributeLabels() + { + return [ + 'attr1' => 'attr1 from model3', + 'attr2' => 'attr2 from model3', + ]; + } } diff --git a/tests/framework/db/BaseActiveRecordTest.php b/tests/framework/db/BaseActiveRecordTest.php new file mode 100644 index 00000000000..0427421cd81 --- /dev/null +++ b/tests/framework/db/BaseActiveRecordTest.php @@ -0,0 +1,42 @@ +getConnection(); + } + + public function provideArrayValueWithChange() + { + return [ + 'not an associative array with data change' => [ + [1, 2, 3], + [1, 3, 2], + ], + + 'associative array with data change case 1' => [ + ['pineapple' => 2, 'apple' => 5, 'banana' => 1], + ['apple' => 5, 'pineapple' => 1, 'banana' => 3], + ], + 'associative array with data change case 2' => [ + ['pineapple' => 2, 'apple' => 5, 'banana' => 1], + ['pineapple' => 2, 'apple' => 3, 'banana' => 1], + ], + + 'filling an empty array' => [ + [], + ['pineapple' => 3, 'apple' => 1, 'banana' => 1], + ], + 'zeroing the array' => [ + ['pineapple' => 3, 'apple' => 1, 'banana' => 17], + [], + ], + ]; + } +} diff --git a/tests/framework/db/CommandTest.php b/tests/framework/db/CommandTest.php index 15ac5f4160f..041b788f3c1 100644 --- a/tests/framework/db/CommandTest.php +++ b/tests/framework/db/CommandTest.php @@ -1525,4 +1525,23 @@ public function testBindValuesSupportsDeprecatedPDOCastingFormat() $db->createCommand()->setSql("SELECT :p1")->bindValues([':p1' => [2, \PDO::PARAM_STR]]); $this->assertTrue(true); } + + public function testBindValuesSupportsEnums() + { + if (version_compare(PHP_VERSION, '8.1.0') >= 0) { + $db = $this->getConnection(); + $command = $db->createCommand(); + + $command->setSql('SELECT :p1')->bindValues([':p1' => enums\Status::ACTIVE]); + $this->assertSame('ACTIVE', $command->params[':p1']); + + $command->setSql('SELECT :p1')->bindValues([':p1' => enums\StatusTypeString::ACTIVE]); + $this->assertSame('active', $command->params[':p1']); + + $command->setSql('SELECT :p1')->bindValues([':p1' => enums\StatusTypeInt::ACTIVE]); + $this->assertSame(1, $command->params[':p1']); + } else { + $this->markTestSkipped('Enums are not supported in PHP < 8.1'); + } + } } diff --git a/tests/framework/db/ConnectionTest.php b/tests/framework/db/ConnectionTest.php index 39e6677610b..a155517ffdf 100644 --- a/tests/framework/db/ConnectionTest.php +++ b/tests/framework/db/ConnectionTest.php @@ -469,7 +469,7 @@ public function testClone() /** - * Test whether slave connection is recovered when call getSlavePdo() after close(). + * Test whether slave connection is recovered when call getSlavePdo(true) after close(). * * @see https://github.com/yiisoft/yii2/issues/14165 */ diff --git a/tests/framework/db/SchemaTest.php b/tests/framework/db/SchemaTest.php index b3200207e99..af02b2170b6 100644 --- a/tests/framework/db/SchemaTest.php +++ b/tests/framework/db/SchemaTest.php @@ -757,7 +757,7 @@ public function testTableSchemaConstraintsWithPdoUppercase($tableName, $type, $e } $connection = $this->getConnection(false); - $connection->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + $connection->getSlavePdo(true)->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); $constraints = $connection->getSchema()->{'getTable' . ucfirst($type)}($tableName, true); $this->assertMetadataEquals($expected, $constraints); } @@ -775,7 +775,7 @@ public function testTableSchemaConstraintsWithPdoLowercase($tableName, $type, $e } $connection = $this->getConnection(false); - $connection->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $connection->getSlavePdo(true)->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); $constraints = $connection->getSchema()->{'getTable' . ucfirst($type)}($tableName, true); $this->assertMetadataEquals($expected, $constraints); } diff --git a/tests/framework/db/cubrid/QueryBuilderTest.php b/tests/framework/db/cubrid/QueryBuilderTest.php index 6dc54e56375..366a457a5f9 100644 --- a/tests/framework/db/cubrid/QueryBuilderTest.php +++ b/tests/framework/db/cubrid/QueryBuilderTest.php @@ -57,7 +57,7 @@ public function testResetSequence() public function testCommentColumn() { - $version = $this->getQueryBuilder(false)->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + $version = $this->getQueryBuilder(false)->db->getSlavePdo(true)->getAttribute(\PDO::ATTR_SERVER_VERSION); if (version_compare($version, '10.0', '<')) { $this->markTestSkipped('Comments on columns are supported starting with CUBRID 10.0.'); return; diff --git a/tests/framework/db/enums/Status.php b/tests/framework/db/enums/Status.php new file mode 100644 index 00000000000..799a552319b --- /dev/null +++ b/tests/framework/db/enums/Status.php @@ -0,0 +1,9 @@ +getConnection(); - $testData = json_encode(['test' => 'string', 'test2' => 'integer']); - $params = []; - $qb = $db->getQueryBuilder(); - $sql = $qb->upsert('T_upsert_varbinary', ['id' => 1, 'blob_col' => $testData] , ['blob_col' => $testData], $params); + $testData = json_encode(['test' => 'string', 'test2' => 'integer'], JSON_THROW_ON_ERROR); - $result = $db->createCommand($sql, $params)->execute(); + $params = []; - $this->assertEquals(1, $result); + $sql = $qb->upsert('T_upsert_varbinary', ['id' => 1, 'blob_col' => $testData], ['blob_col' => $testData], $params); + $result = $db->createCommand($sql, $params)->execute(); - $query = (new Query()) - ->select(['convert(nvarchar(max),blob_col) as blob_col']) - ->from('T_upsert_varbinary') - ->where(['id' => 1]); + $this->assertSame(1, $result); + $query = (new Query())->select(['blob_col'])->from('T_upsert_varbinary')->where(['id' => 1]); $resultData = $query->createCommand($db)->queryOne(); - $this->assertEquals($testData, $resultData['blob_col']); + + $this->assertSame($testData, $resultData['blob_col']); } } diff --git a/tests/framework/db/mssql/QueryCacheTest.php b/tests/framework/db/mssql/QueryCacheTest.php new file mode 100644 index 00000000000..b35c11fde1c --- /dev/null +++ b/tests/framework/db/mssql/QueryCacheTest.php @@ -0,0 +1,55 @@ +getConnection(); + $db->enableQueryCache = true; + $db->queryCache = new FileCache(['cachePath' => '@yiiunit/runtime/cache']); + + $db->createCommand()->delete('type')->execute(); + $db->createCommand()->insert('type', [ + 'int_col' => $key = 1, + 'char_col' => '', + 'char_col2' => '6a3ce1a0bffe8eeb6fa986caf443e24c', + 'float_col' => 0.0, + 'blob_col' => 'a:1:{s:13:"template";s:1:"1";}', + 'bool_col' => true, + ])->execute(); + + $function = function($db) use ($key){ + return (new Query()) + ->select(['blob_col']) + ->from('type') + ->where(['int_col' => $key]) + ->createCommand($db) + ->queryScalar(); + }; + + // First run return + $result = $db->cache($function); + $this->assertSame('a:1:{s:13:"template";s:1:"1";}', $result); + + // After the request has been cached return + $result = $db->cache($function); + $this->assertSame('a:1:{s:13:"template";s:1:"1";}', $result); + } +} diff --git a/tests/framework/db/mssql/type/VarbinaryTest.php b/tests/framework/db/mssql/type/VarbinaryTest.php new file mode 100644 index 00000000000..b72a2b4c469 --- /dev/null +++ b/tests/framework/db/mssql/type/VarbinaryTest.php @@ -0,0 +1,44 @@ +getConnection(); + + $db->createCommand()->delete('type')->execute(); + $db->createCommand()->insert('type', [ + 'int_col' => $key = 1, + 'char_col' => '', + 'char_col2' => '6a3ce1a0bffe8eeb6fa986caf443e24c', + 'float_col' => 0.0, + 'blob_col' => 'a:1:{s:13:"template";s:1:"1";}', + 'bool_col' => true, + ])->execute(); + + $result = (new Query()) + ->select(['blob_col']) + ->from('type') + ->where(['int_col' => $key]) + ->createCommand($db) + ->queryScalar(); + + $this->assertSame('a:1:{s:13:"template";s:1:"1";}', $result); + } +} diff --git a/tests/framework/db/mysql/BaseActiveRecordTest.php b/tests/framework/db/mysql/BaseActiveRecordTest.php new file mode 100644 index 00000000000..394922e87f7 --- /dev/null +++ b/tests/framework/db/mysql/BaseActiveRecordTest.php @@ -0,0 +1,37 @@ +getConnection()->getSchema()->getServerVersion(), '5.7', '<')) { + $this->markTestSkipped('JSON columns are not supported in MySQL < 5.7'); + } + if (version_compare(PHP_VERSION, '5.6', '<')) { + $this->markTestSkipped('JSON columns are not supported in PDO for PHP < 5.6'); + } + + $createdStorage = new Storage(['data' => $actual]); + + $createdStorage->save(); + + $foundStorage = Storage::find()->limit(1)->one(); + + $this->assertNotNull($foundStorage); + + $foundStorage->data = $modified; + + $this->assertSame(['data' => $modified], $foundStorage->getDirtyAttributes()); + } +} diff --git a/tests/framework/db/mysql/QueryBuilderTest.php b/tests/framework/db/mysql/QueryBuilderTest.php index 21257bb2b2e..659bf1a9ee2 100644 --- a/tests/framework/db/mysql/QueryBuilderTest.php +++ b/tests/framework/db/mysql/QueryBuilderTest.php @@ -125,7 +125,7 @@ public function columnTimeTypes() /** * @link https://github.com/yiisoft/yii2/issues/14367 */ - $mysqlVersion = $this->getDb()->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + $mysqlVersion = $this->getDb()->getSlavePdo(true)->getAttribute(\PDO::ATTR_SERVER_VERSION); $supportsFractionalSeconds = version_compare($mysqlVersion,'5.6.4', '>='); if ($supportsFractionalSeconds) { $expectedValues = [ diff --git a/tests/framework/db/mysql/connection/DeadLockTest.php b/tests/framework/db/mysql/connection/DeadLockTest.php index 40f6d573f87..6e3da047eea 100644 --- a/tests/framework/db/mysql/connection/DeadLockTest.php +++ b/tests/framework/db/mysql/connection/DeadLockTest.php @@ -119,7 +119,12 @@ public function testDeadlockException() . ($logContent ? ". Shared children log:\n$logContent" : '') ); } - $this->assertEquals(1, $deadlockHitCount, "exactly one child must hit deadlock; shared children log:\n" . $logContent); + + if (version_compare($this->getConnection()->getSchema()->getServerVersion(), '8.0', '<')) { + $this->assertEquals(1, $deadlockHitCount, "exactly one child must hit deadlock; shared children log:\n" . $logContent); + } else { + $this->assertEquals(0, $deadlockHitCount, "exactly zero children must hit deadlock; shared children log:\n" . $logContent); + } } /** diff --git a/tests/framework/db/pgsql/BaseActiveRecordTest.php b/tests/framework/db/pgsql/BaseActiveRecordTest.php new file mode 100644 index 00000000000..a4993685249 --- /dev/null +++ b/tests/framework/db/pgsql/BaseActiveRecordTest.php @@ -0,0 +1,46 @@ + new JsonExpression($actual), + ]); + + $createdStorage->save(); + + $foundStorage = ArrayAndJsonType::find()->limit(1)->one(); + + $this->assertNotNull($foundStorage); + + $foundStorage->json_col = $modified; + + $this->assertSame(['json_col' => $modified], $foundStorage->getDirtyAttributes()); + } +} + +/** + * {@inheritdoc} + * @property array id + * @property array json_col + */ +class ArrayAndJsonType extends ActiveRecord +{ + public static function tableName() + { + return '{{%array_and_json_types}}'; + } +} diff --git a/tests/framework/db/sqlite/CommandTest.php b/tests/framework/db/sqlite/CommandTest.php index 23012f38fec..b7492c0d1d8 100644 --- a/tests/framework/db/sqlite/CommandTest.php +++ b/tests/framework/db/sqlite/CommandTest.php @@ -7,6 +7,8 @@ namespace yiiunit\framework\db\sqlite; +use yii\db\sqlite\Schema; + /** * @group db * @group sqlite @@ -109,4 +111,60 @@ public function batchInsertSqlProvider() return $parent; } + + public function testResetSequence() + { + $db = $this->getConnection(); + + if ($db->getTableSchema('reset_sequence', true) !== null) { + $db->createCommand()->dropTable('reset_sequence')->execute(); + } + + // create table reset_sequence + $db->createCommand()->createTable( + 'reset_sequence', + [ + 'id' => Schema::TYPE_PK, + 'description' => Schema::TYPE_TEXT, + ] + )->execute(); + + // ensure auto increment is working + $db->createCommand()->insert('reset_sequence', ['description' => 'test'])->execute(); + $this->assertEquals(1, $db->createCommand('SELECT MAX([[id]]) FROM {{reset_sequence}}')->queryScalar()); + + // remove all records + $db->createCommand()->delete('reset_sequence')->execute(); + $this->assertEquals(0, $db->createCommand('SELECT COUNT(*) FROM {{reset_sequence}}')->queryScalar()); + + // counter should be reset to 1 + $db->createCommand()->resetSequence('reset_sequence')->execute(); + $db->createCommand()->insert('reset_sequence', ['description' => 'test'])->execute(); + $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{reset_sequence}}')->queryScalar()); + $this->assertEquals(1, $db->createCommand('SELECT MAX([[id]]) FROM {{reset_sequence}}')->queryScalar()); + + // counter should be reset to 5, so next record gets ID 5 + $db->createCommand()->resetSequence('reset_sequence', 5)->execute(); + $db->createCommand()->insert('reset_sequence', ['description' => 'test'])->execute(); + $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{reset_sequence}}')->queryScalar()); + $this->assertEquals(5, $db->createCommand('SELECT MAX([[id]]) FROM {{reset_sequence}}')->queryScalar()); + } + + public function testResetSequenceExceptionTableNoExist() + { + $this->expectException('yii\base\InvalidArgumentException'); + $this->expectExceptionMessage('Table not found: no_exist_table'); + + $db = $this->getConnection(); + $db->createCommand()->resetSequence('no_exist_table', 5)->execute(); + } + + public function testResetSequenceExceptionSquenceNoExist() + { + $this->expectException('yii\base\InvalidArgumentException'); + $this->expectExceptionMessage("There is not sequence associated with table 'type'."); + + $db = $this->getConnection(); + $db->createCommand()->resetSequence('type', 5)->execute(); + } } diff --git a/tests/framework/filters/AccessRuleTest.php b/tests/framework/filters/AccessRuleTest.php index 4c2ded2d2dd..620766788d8 100644 --- a/tests/framework/filters/AccessRuleTest.php +++ b/tests/framework/filters/AccessRuleTest.php @@ -422,17 +422,18 @@ public function testMatchIP() { $action = $this->mockAction(); $user = false; - $request = $this->mockRequest(); $rule = new AccessRule(); // by default match all IPs + $request = $this->mockRequest(); $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); $rule->allow = false; $this->assertFalse($rule->allows($action, $user, $request)); // empty IPs = match all IPs + $request = $this->mockRequest(); $rule->ips = []; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -441,6 +442,7 @@ public function testMatchIP() // match, one IP $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['127.0.0.1']; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -449,6 +451,7 @@ public function testMatchIP() // no match, one IP $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['192.168.0.1']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -457,12 +460,14 @@ public function testMatchIP() // no partial match, one IP $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['127.0.0.10']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); $rule->allow = false; $this->assertNull($rule->allows($action, $user, $request)); $_SERVER['REMOTE_ADDR'] = '127.0.0.10'; + $request = $this->mockRequest(); $rule->ips = ['127.0.0.1']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -471,6 +476,7 @@ public function testMatchIP() // match, one IP IPv6 $_SERVER['REMOTE_ADDR'] = '::1'; + $request = $this->mockRequest(); $rule->ips = ['::1']; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -479,6 +485,7 @@ public function testMatchIP() // no match, one IP IPv6 $_SERVER['REMOTE_ADDR'] = '::1'; + $request = $this->mockRequest(); $rule->ips = ['dead::beaf::1']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -487,12 +494,14 @@ public function testMatchIP() // no partial match, one IP IPv6 $_SERVER['REMOTE_ADDR'] = '::1'; + $request = $this->mockRequest(); $rule->ips = ['::123']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); $rule->allow = false; $this->assertNull($rule->allows($action, $user, $request)); $_SERVER['REMOTE_ADDR'] = '::123'; + $request = $this->mockRequest(); $rule->ips = ['::1']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -501,6 +510,7 @@ public function testMatchIP() // undefined IP $_SERVER['REMOTE_ADDR'] = null; + $request = $this->mockRequest(); $rule->ips = ['192.168.*']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -512,12 +522,12 @@ public function testMatchIPWildcard() { $action = $this->mockAction(); $user = false; - $request = $this->mockRequest(); $rule = new AccessRule(); // no match $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['192.168.*']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -526,6 +536,7 @@ public function testMatchIPWildcard() // match $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['127.0.*']; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -534,6 +545,7 @@ public function testMatchIPWildcard() // match, IPv6 $_SERVER['REMOTE_ADDR'] = '2a01:4f8:120:7202::2'; + $request = $this->mockRequest(); $rule->ips = ['2a01:4f8:120:*']; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -542,6 +554,7 @@ public function testMatchIPWildcard() // no match, IPv6 $_SERVER['REMOTE_ADDR'] = '::1'; + $request = $this->mockRequest(); $rule->ips = ['2a01:4f8:120:*']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -553,12 +566,12 @@ public function testMatchIPMask() { $action = $this->mockAction(); $user = false; - $request = $this->mockRequest(); $rule = new AccessRule(); // no match $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['127.0.0.32/27']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); @@ -567,6 +580,7 @@ public function testMatchIPMask() // match $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $request = $this->mockRequest(); $rule->ips = ['127.0.0.1/27']; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -575,6 +589,7 @@ public function testMatchIPMask() // match, IPv6 $_SERVER['REMOTE_ADDR'] = '2a01:4f8:120:7202::2'; + $request = $this->mockRequest(); $rule->ips = ['2a01:4f8:120:7202::2/127']; $rule->allow = true; $this->assertTrue($rule->allows($action, $user, $request)); @@ -583,6 +598,7 @@ public function testMatchIPMask() // no match, IPv6 $_SERVER['REMOTE_ADDR'] = '2a01:4f8:120:7202::ffff'; + $request = $this->mockRequest(); $rule->ips = ['2a01:4f8:120:7202::2/123']; $rule->allow = true; $this->assertNull($rule->allows($action, $user, $request)); diff --git a/tests/framework/grid/GridViewTest.php b/tests/framework/grid/GridViewTest.php index 392aefbd0ba..b698011493b 100644 --- a/tests/framework/grid/GridViewTest.php +++ b/tests/framework/grid/GridViewTest.php @@ -7,10 +7,12 @@ namespace yiiunit\framework\grid; +use Yii; use yii\data\ArrayDataProvider; use yii\grid\DataColumn; use yii\grid\GridView; use yii\web\View; +use yiiunit\data\ar\NoAutoLabels; /** * @author Evgeniy Tkachenko @@ -150,4 +152,53 @@ public function testFooter() { $this->assertTrue(preg_match("/<\/tbody>/", $html) === 1); } + + public function testHeaderLabels() + { + // Ensure GridView does not call Model::generateAttributeLabel() to generate labels unless the labels are explicitly used. + + $this->mockApplication([ + 'components' => [ + 'db' => [ + 'class' => \yii\db\Connection::className(), + 'dsn' => 'sqlite::memory:', + ], + ], + ]); + + NoAutoLabels::$db = Yii::$app->getDb(); + Yii::$app->getDb()->createCommand()->createTable(NoAutoLabels::tableName(), ['attr1' => 'int', 'attr2' => 'int'])->execute(); + + $urlManager = new \yii\web\UrlManager([ + 'baseUrl' => '/', + 'scriptUrl' => '/index.php', + ]); + + $grid = new GridView([ + 'dataProvider' => new \yii\data\ActiveDataProvider([ + 'query' => NoAutoLabels::find(), + ]), + 'columns' => [ + 'attr1', + 'attr2:text:Label for attr2', + ], + ]); + + // NoAutoLabels::generateAttributeLabel() should not be called. + $grid->dataProvider->setSort([ + 'route' => '/', + 'urlManager' => $urlManager, + ]); + $grid->renderTableHeader(); + + // NoAutoLabels::generateAttributeLabel() should not be called. + $grid->dataProvider->setSort([ + 'route' => '/', + 'urlManager' => $urlManager, + 'attributes' => ['attr1', 'attr2'], + ]); + $grid->renderTableHeader(); + + // If NoAutoLabels::generateAttributeLabel() has not been called no exception will be thrown meaning this test passed successfully. + } } diff --git a/tests/framework/helpers/ArrayHelperTest.php b/tests/framework/helpers/ArrayHelperTest.php index 063eeafcaf6..0d706bc552f 100644 --- a/tests/framework/helpers/ArrayHelperTest.php +++ b/tests/framework/helpers/ArrayHelperTest.php @@ -135,6 +135,29 @@ public function testRemove() $this->assertEquals('defaultValue', $default); } + /** + * @return void + */ + public function testRemoveWithFloat() + { + if (version_compare(PHP_VERSION, '8.1.0', '>=')) { + $this->markTestSkipped('Using floats as array key is deprecated.'); + } + + $array = ['name' => 'b', 'age' => 3, 1.1 => null]; + + $name = ArrayHelper::remove($array, 'name'); + $this->assertEquals($name, 'b'); + $this->assertEquals($array, ['age' => 3, 1.1 => null]); + + $floatVal = ArrayHelper::remove($array, 1.1); + $this->assertNull($floatVal); + $this->assertEquals($array, ['age' => 3]); + + $default = ArrayHelper::remove($array, 'nonExisting', 'defaultValue'); + $this->assertEquals('defaultValue', $default); + } + public function testRemoveValueMultiple() { $array = [ @@ -506,14 +529,21 @@ public function testMergeEmpty() /** * @see https://github.com/yiisoft/yii2/pull/11549 */ - public function test() + public function testGetValueWithFloatKeys() { - $array = []; - $array[1.0] = 'some value'; + if (version_compare(PHP_VERSION, '8.1.0', '>=')) { + $this->markTestSkipped('Using floats as array key is deprecated.'); + } - $result = ArrayHelper::getValue($array, 1.0); + $array = []; + $array[1.1] = 'some value'; + $array[2.1] = null; + $result = ArrayHelper::getValue($array, 1.2); $this->assertEquals('some value', $result); + + $result = ArrayHelper::getValue($array, 2.2); + $this->assertNull($result); } public function testIndex() @@ -712,6 +742,7 @@ public function testKeyExists() 'a' => 1, 'B' => 2, ]; + $this->assertTrue(ArrayHelper::keyExists('a', $array)); $this->assertFalse(ArrayHelper::keyExists('b', $array)); $this->assertTrue(ArrayHelper::keyExists('B', $array)); @@ -723,6 +754,27 @@ public function testKeyExists() $this->assertFalse(ArrayHelper::keyExists('c', $array, false)); } + public function testKeyExistsWithFloat() + { + if (version_compare(PHP_VERSION, '8.1.0', '>=')) { + $this->markTestSkipped('Using floats as array key is deprecated.'); + } + + $array = [ + 1 => 3, + 2.2 => 4, // Note: Floats are cast to ints, which means that the fractional part will be truncated. + 3.3 => null, + ]; + + $this->assertTrue(ArrayHelper::keyExists(1, $array)); + $this->assertTrue(ArrayHelper::keyExists(1.1, $array)); + $this->assertTrue(ArrayHelper::keyExists(2, $array)); + $this->assertTrue(ArrayHelper::keyExists('2', $array)); + $this->assertTrue(ArrayHelper::keyExists(2.2, $array)); + $this->assertTrue(ArrayHelper::keyExists(3, $array)); + $this->assertTrue(ArrayHelper::keyExists(3.3, $array)); + } + public function testKeyExistsArrayAccess() { $array = new TraversableArrayAccessibleObject([ diff --git a/tests/framework/helpers/ConsoleTest.php b/tests/framework/helpers/ConsoleTest.php index b729e266910..cbd1b322ee5 100644 --- a/tests/framework/helpers/ConsoleTest.php +++ b/tests/framework/helpers/ConsoleTest.php @@ -216,27 +216,6 @@ public function testErrorSummary() $expectedHtml = "Error message. Here are some chars: < >\nError message. Here are even more chars: \"\""; $this->assertEqualsWithoutLE($expectedHtml, Console::errorSummary($model, $options)); } -} - -/** - * @property string name - * @property array types - * @property string description - */ -class TestConsoleModel extends DynamicModel -{ - public function rules() - { - return [ - ['name', 'required'], - ['name', 'string', 'max' => 100] - ]; - } - - public function init() - { - $this->defineAttribute('name'); - } /** * @covers \yii\helpers\BaseConsole::input() @@ -390,16 +369,16 @@ public function testConfirm() $this->truncateStreams(); foreach ([ - 'y' => true, - 'Y' => true, - 'yes' => true, - 'YeS' => true, - 'n' => false, - 'N' => false, - 'no' => false, - 'NO' => false, - 'WHAT?!' . PHP_EOL . 'yes' => true, - ] as $currInput => $currAssertion) { + 'y' => true, + 'Y' => true, + 'yes' => true, + 'YeS' => true, + 'n' => false, + 'N' => false, + 'no' => false, + 'NO' => false, + 'WHAT?!' . PHP_EOL . 'yes' => true, + ] as $currInput => $currAssertion) { $this->sendInput($currInput); $result = ConsoleStub::confirm('Are you sure?'); $this->assertEquals($currAssertion, $result, $currInput); @@ -420,31 +399,63 @@ public function testSelect() $this->sendInput('c'); $result = ConsoleStub::select('Usual behavior', $options); - $this->assertEquals('Usual behavior [c,d,m,?]: ', $this->readOutput()); + $this->assertEquals('Usual behavior (c,d,m,?): ', $this->readOutput()); $this->assertEquals('c', $result); $this->truncateStreams(); $this->sendInput('x', 'd'); $result = ConsoleStub::select('Wrong character', $options); - $this->assertEquals('Wrong character [c,d,m,?]: Wrong character [c,d,m,?]: ', $this->readOutput()); + $this->assertEquals('Wrong character (c,d,m,?): Wrong character (c,d,m,?): ', $this->readOutput()); $this->assertEquals('d', $result); $this->truncateStreams(); $this->sendInput('?', 'm'); $result = ConsoleStub::select('Using help', $options); $this->assertEquals( - 'Using help [c,d,m,?]: ' - . ' c - cat' - . PHP_EOL - . ' d - dog' - . PHP_EOL - . ' m - mouse' - . PHP_EOL - . ' ? - Show help' - . PHP_EOL - . 'Using help [c,d,m,?]: ', + 'Using help (c,d,m,?): ' + . ' c - cat' + . PHP_EOL + . ' d - dog' + . PHP_EOL + . ' m - mouse' + . PHP_EOL + . ' ? - Show help' + . PHP_EOL + . 'Using help (c,d,m,?): ', $this->readOutput() ); $this->truncateStreams(); + + $this->sendInput(''); + $result = ConsoleStub::select('Use Default', $options, 'm'); + $this->assertEquals('m', $result); + $this->truncateStreams(); + + $this->sendInput('', 'd'); + $result = ConsoleStub::select('Empty without Default', $options); + $this->assertEquals('Empty without Default (c,d,m,?): Empty without Default (c,d,m,?): ', $this->readOutput()); + $this->assertEquals('d', $result); + $this->truncateStreams(); + } +} + +/** + * @property string name + * @property array types + * @property string description + */ +class TestConsoleModel extends DynamicModel +{ + public function rules() + { + return [ + ['name', 'required'], + ['name', 'string', 'max' => 100] + ]; + } + + public function init() + { + $this->defineAttribute('name'); } } diff --git a/tests/framework/helpers/FileHelperTest.php b/tests/framework/helpers/FileHelperTest.php index b18a2946700..448e6148fd4 100644 --- a/tests/framework/helpers/FileHelperTest.php +++ b/tests/framework/helpers/FileHelperTest.php @@ -1270,4 +1270,60 @@ public function changeOwnershipInvalidArgumentsProvider() [true, null, 'test'], ]; } + + /** + * @dataProvider getExtensionsByMimeTypeProvider + * @param string $mimeType + * @param array $extensions + * @return void + */ + public function testGetExtensionsByMimeType($mimeType, $extensions) + { + $this->assertEquals($extensions, FileHelper::getExtensionsByMimeType($mimeType)); + } + + public function getExtensionsByMimeTypeProvider() + { + return [ + [ + 'application/json', + [ + 'json', + ], + ], + [ + 'image/jpeg', + [ // Note: For backwards compatibility the (alphabetic) order of `framework/helpers/mimeTypes.php` is expected. + 'jfif', + 'jpe', + 'jpeg', + 'jpg', + 'pjp', + 'pjpeg', + ], + ], + ]; + } + + /** + * @dataProvider getExtensionByMimeTypeProvider + * @param string $mimeType + * @param bool $preferShort + * @param array $extension + * @return void + */ + public function testGetExtensionByMimeType($mimeType, $preferShort, $extension) + { + $this->assertEquals($extension, FileHelper::getExtensionByMimeType($mimeType, $preferShort)); + } + + public function getExtensionByMimeTypeProvider() + { + return [ + ['application/json', true, 'json'], + ['application/json', false, 'json'], + ['image/jpeg', true, 'jpg'], + ['image/jpeg', false, 'jpeg'], + ]; + } } diff --git a/tests/framework/helpers/FormatConverterTest.php b/tests/framework/helpers/FormatConverterTest.php index 3dffe4e0823..dd8d470a1cd 100644 --- a/tests/framework/helpers/FormatConverterTest.php +++ b/tests/framework/helpers/FormatConverterTest.php @@ -39,8 +39,8 @@ protected function tearDown() public function testIntlIcuToPhpShortForm() { - $this->assertEquals('n/j/y', FormatConverter::convertDateIcuToPhp('short', 'date', 'en-US')); - $this->assertEquals('d.m.y', FormatConverter::convertDateIcuToPhp('short', 'date', 'de-DE')); + $this->assertEqualsAnyWhitespace('n/j/y', FormatConverter::convertDateIcuToPhp('short', 'date', 'en-US')); + $this->assertEqualsAnyWhitespace('d.m.y', FormatConverter::convertDateIcuToPhp('short', 'date', 'de-DE')); } public function testIntlIcuToPhpShortFormDefaultLang() @@ -53,13 +53,13 @@ public function testIntlIcuToPhpShortFormDefaultLang() public function testIntlIcuToPhpShortFormTime() { - $this->assertEquals('g:i A', FormatConverter::convertDateIcuToPhp('short', 'time', 'en-US')); - $this->assertEquals('H:i', FormatConverter::convertDateIcuToPhp('short', 'time', 'de-DE')); + $this->assertEqualsAnyWhitespace('g:i A', FormatConverter::convertDateIcuToPhp('short', 'time', 'en-US')); + $this->assertEqualsAnyWhitespace('H:i', FormatConverter::convertDateIcuToPhp('short', 'time', 'de-DE')); } public function testIntlIcuToPhpShortFormDateTime() { - $this->assertEquals('n/j/y, g:i A', FormatConverter::convertDateIcuToPhp('short', 'datetime', 'en-US')); + $this->assertEqualsAnyWhitespace('n/j/y, g:i A', FormatConverter::convertDateIcuToPhp('short', 'datetime', 'en-US')); $this->assertEquals( PHP_VERSION_ID < 50600 ? 'd.m.y H:i' : 'd.m.y, H:i', FormatConverter::convertDateIcuToPhp('short', 'datetime', 'de-DE') @@ -208,13 +208,13 @@ public function testIntlIcuToJuiShortFormDefaultLang() public function testIntlIcuToJuiShortFormTime() { - $this->assertEquals(': ', FormatConverter::convertDateIcuToJui('short', 'time', 'en-US')); - $this->assertEquals(':', FormatConverter::convertDateIcuToJui('short', 'time', 'de-DE')); + $this->assertEqualsAnyWhitespace(': ', FormatConverter::convertDateIcuToJui('short', 'time', 'en-US')); + $this->assertEqualsAnyWhitespace(':', FormatConverter::convertDateIcuToJui('short', 'time', 'de-DE')); } public function testIntlIcuToJuiShortFormDateTime() { - $this->assertEquals('m/d/y, : ', FormatConverter::convertDateIcuToJui('short', 'datetime', 'en-US')); + $this->assertEqualsAnyWhitespace('m/d/y, : ', FormatConverter::convertDateIcuToJui('short', 'datetime', 'en-US')); $this->assertEquals( PHP_VERSION_ID < 50600 ? 'dd.mm.y :' : 'dd.mm.y, :', FormatConverter::convertDateIcuToJui('short', 'datetime', 'de-DE') diff --git a/tests/framework/helpers/JsonTest.php b/tests/framework/helpers/JsonTest.php index 302f486a0d2..f8150c764d8 100644 --- a/tests/framework/helpers/JsonTest.php +++ b/tests/framework/helpers/JsonTest.php @@ -96,14 +96,18 @@ public function testEncode() $data->data = (object) null; $this->assertSame('{}', Json::encode($data)); - // Generator - $data = function () { - foreach (['a' => 1, 'b' => 2] as $name => $value) { - yield $name => $value; - } - }; - - $this->assertSame('{"a":1,"b":2}', Json::encode($data())); + // Generator (Only supported since PHP 5.5) + if (PHP_VERSION_ID >= 50500) { + $data = eval(<<<'PHP' + return function () { + foreach (['a' => 1, 'b' => 2] as $name => $value) { + yield $name => $value; + } + }; +PHP + ); + $this->assertSame('{"a":1,"b":2}', Json::encode($data())); + } } public function testHtmlEncode() diff --git a/tests/framework/helpers/MimeTest.php b/tests/framework/helpers/MimeTest.php deleted file mode 100644 index 70c5a362175..00000000000 --- a/tests/framework/helpers/MimeTest.php +++ /dev/null @@ -1,1037 +0,0 @@ -assertSame( - [ - 'text/rtf' => 'application/rtf', - 'text/xml' => 'application/xml', - 'image/svg' => 'image/svg+xml', - 'image/x-bmp' => 'image/bmp', - 'image/x-bitmap' => 'image/bmp', - 'image/x-xbitmap' => 'image/bmp', - 'image/x-win-bitmap' => 'image/bmp', - 'image/x-windows-bmp' => 'image/bmp', - 'image/ms-bmp' => 'image/bmp', - 'image/x-ms-bmp' => 'image/bmp', - 'application/bmp' => 'image/bmp', - 'application/x-bmp' => 'image/bmp', - 'application/x-win-bitmap' => 'image/bmp', - ], - require __DIR__ . '/../../../framework/helpers/mimeAliases.php' - ); - } - - public function testMimeTypes() - { - $coreMimeTypes = [ - '3dml' => 'text/vnd.in3d.3dml', - '3ds' => 'image/x-3ds', - '3g2' => 'video/3gpp2', - '3gp' => 'video/3gpp', - '7z' => 'application/x-7z-compressed', - 'aab' => 'application/x-authorware-bin', - 'aac' => 'audio/x-aac', - 'aam' => 'application/x-authorware-map', - 'aas' => 'application/x-authorware-seg', - 'abw' => 'application/x-abiword', - 'ac' => 'application/pkix-attr-cert', - 'acc' => 'application/vnd.americandynamics.acc', - 'ace' => 'application/x-ace-compressed', - 'acu' => 'application/vnd.acucobol', - 'acutc' => 'application/vnd.acucorp', - 'adp' => 'audio/adpcm', - 'aep' => 'application/vnd.audiograph', - 'afm' => 'application/x-font-type1', - 'afp' => 'application/vnd.ibm.modcap', - 'ahead' => 'application/vnd.ahead.space', - 'ai' => 'application/postscript', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'air' => 'application/vnd.adobe.air-application-installer-package+zip', - 'ait' => 'application/vnd.dvb.ait', - 'ami' => 'application/vnd.amiga.ami', - 'apk' => 'application/vnd.android.package-archive', - 'appcache' => 'text/cache-manifest', - 'application' => 'application/x-ms-application', - 'apr' => 'application/vnd.lotus-approach', - 'arc' => 'application/x-freearc', - 'asc' => 'application/pgp-signature', - 'asf' => 'video/x-ms-asf', - 'asm' => 'text/x-asm', - 'aso' => 'application/vnd.accpac.simply.aso', - 'asx' => 'video/x-ms-asf', - 'atc' => 'application/vnd.acucorp', - 'atom' => 'application/atom+xml', - 'atomcat' => 'application/atomcat+xml', - 'atomsvc' => 'application/atomsvc+xml', - 'atx' => 'application/vnd.antix.game-component', - 'au' => 'audio/basic', - 'avi' => 'video/x-msvideo', - 'aw' => 'application/applixware', - 'azf' => 'application/vnd.airzip.filesecure.azf', - 'azs' => 'application/vnd.airzip.filesecure.azs', - 'azw' => 'application/vnd.amazon.ebook', - 'bat' => 'application/x-msdownload', - 'bcpio' => 'application/x-bcpio', - 'bdf' => 'application/x-font-bdf', - 'bdm' => 'application/vnd.syncml.dm+wbxml', - 'bed' => 'application/vnd.realvnc.bed', - 'bh2' => 'application/vnd.fujitsu.oasysprs', - 'bin' => 'application/octet-stream', - 'blb' => 'application/x-blorb', - 'blorb' => 'application/x-blorb', - 'bmi' => 'application/vnd.bmi', - 'bmp' => 'image/bmp', - 'book' => 'application/vnd.framemaker', - 'box' => 'application/vnd.previewsystems.box', - 'boz' => 'application/x-bzip2', - 'bpk' => 'application/octet-stream', - 'btif' => 'image/prs.btif', - 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', - 'c' => 'text/x-c', - 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', - 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', - 'c4d' => 'application/vnd.clonk.c4group', - 'c4f' => 'application/vnd.clonk.c4group', - 'c4g' => 'application/vnd.clonk.c4group', - 'c4p' => 'application/vnd.clonk.c4group', - 'c4u' => 'application/vnd.clonk.c4group', - 'cab' => 'application/vnd.ms-cab-compressed', - 'caf' => 'audio/x-caf', - 'cap' => 'application/vnd.tcpdump.pcap', - 'car' => 'application/vnd.curl.car', - 'cat' => 'application/vnd.ms-pki.seccat', - 'cb7' => 'application/x-cbr', - 'cba' => 'application/x-cbr', - 'cbr' => 'application/x-cbr', - 'cbt' => 'application/x-cbr', - 'cbz' => 'application/x-cbr', - 'cc' => 'text/x-c', - 'cct' => 'application/x-director', - 'ccxml' => 'application/ccxml+xml', - 'cdbcmsg' => 'application/vnd.contact.cmsg', - 'cdf' => 'application/x-netcdf', - 'cdkey' => 'application/vnd.mediastation.cdkey', - 'cdmia' => 'application/cdmi-capability', - 'cdmic' => 'application/cdmi-container', - 'cdmid' => 'application/cdmi-domain', - 'cdmio' => 'application/cdmi-object', - 'cdmiq' => 'application/cdmi-queue', - 'cdx' => 'chemical/x-cdx', - 'cdxml' => 'application/vnd.chemdraw+xml', - 'cdy' => 'application/vnd.cinderella', - 'cer' => 'application/pkix-cert', - 'cfs' => 'application/x-cfs-compressed', - 'cgm' => 'image/cgm', - 'chat' => 'application/x-chat', - 'chm' => 'application/vnd.ms-htmlhelp', - 'chrt' => 'application/vnd.kde.kchart', - 'cif' => 'chemical/x-cif', - 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', - 'cil' => 'application/vnd.ms-artgalry', - 'cla' => 'application/vnd.claymore', - 'class' => 'application/java-vm', - 'clkk' => 'application/vnd.crick.clicker.keyboard', - 'clkp' => 'application/vnd.crick.clicker.palette', - 'clkt' => 'application/vnd.crick.clicker.template', - 'clkw' => 'application/vnd.crick.clicker.wordbank', - 'clkx' => 'application/vnd.crick.clicker', - 'clp' => 'application/x-msclip', - 'cmc' => 'application/vnd.cosmocaller', - 'cmdf' => 'chemical/x-cmdf', - 'cml' => 'chemical/x-cml', - 'cmp' => 'application/vnd.yellowriver-custom-menu', - 'cmx' => 'image/x-cmx', - 'cod' => 'application/vnd.rim.cod', - 'com' => 'application/x-msdownload', - 'conf' => 'text/plain', - 'cpio' => 'application/x-cpio', - 'cpp' => 'text/x-c', - 'cpt' => 'application/mac-compactpro', - 'crd' => 'application/x-mscardfile', - 'crl' => 'application/pkix-crl', - 'crt' => 'application/x-x509-ca-cert', - 'cryptonote' => 'application/vnd.rig.cryptonote', - 'csh' => 'application/x-csh', - 'csml' => 'chemical/x-csml', - 'csp' => 'application/vnd.commonspace', - 'css' => 'text/css', - 'cst' => 'application/x-director', - 'csv' => 'text/csv', - 'cu' => 'application/cu-seeme', - 'curl' => 'text/vnd.curl', - 'cww' => 'application/prs.cww', - 'cxt' => 'application/x-director', - 'cxx' => 'text/x-c', - 'dae' => 'model/vnd.collada+xml', - 'daf' => 'application/vnd.mobius.daf', - 'dart' => 'application/vnd.dart', - 'dataless' => 'application/vnd.fdsn.seed', - 'davmount' => 'application/davmount+xml', - 'dbk' => 'application/docbook+xml', - 'dcr' => 'application/x-director', - 'dcurl' => 'text/vnd.curl.dcurl', - 'dd2' => 'application/vnd.oma.dd2+xml', - 'ddd' => 'application/vnd.fujixerox.ddd', - 'deb' => 'application/x-debian-package', - 'def' => 'text/plain', - 'deploy' => 'application/octet-stream', - 'der' => 'application/x-x509-ca-cert', - 'dfac' => 'application/vnd.dreamfactory', - 'dgc' => 'application/x-dgc-compressed', - 'dic' => 'text/x-c', - 'dir' => 'application/x-director', - 'dis' => 'application/vnd.mobius.dis', - 'dist' => 'application/octet-stream', - 'distz' => 'application/octet-stream', - 'djv' => 'image/vnd.djvu', - 'djvu' => 'image/vnd.djvu', - 'dll' => 'application/x-msdownload', - 'dmg' => 'application/x-apple-diskimage', - 'dmp' => 'application/vnd.tcpdump.pcap', - 'dms' => 'application/octet-stream', - 'dna' => 'application/vnd.dna', - 'doc' => 'application/msword', - 'docm' => 'application/vnd.ms-word.document.macroenabled.12', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dot' => 'application/msword', - 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'dp' => 'application/vnd.osgi.dp', - 'dpg' => 'application/vnd.dpgraph', - 'dra' => 'audio/vnd.dra', - 'dsc' => 'text/prs.lines.tag', - 'dssc' => 'application/dssc+der', - 'dtb' => 'application/x-dtbook+xml', - 'dtd' => 'application/xml-dtd', - 'dts' => 'audio/vnd.dts', - 'dtshd' => 'audio/vnd.dts.hd', - 'dump' => 'application/octet-stream', - 'dvb' => 'video/vnd.dvb.file', - 'dvi' => 'application/x-dvi', - 'dwf' => 'model/vnd.dwf', - 'dwg' => 'image/vnd.dwg', - 'dxf' => 'image/vnd.dxf', - 'dxp' => 'application/vnd.spotfire.dxp', - 'dxr' => 'application/x-director', - 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', - 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', - 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', - 'ecma' => 'application/ecmascript', - 'edm' => 'application/vnd.novadigm.edm', - 'edx' => 'application/vnd.novadigm.edx', - 'efif' => 'application/vnd.picsel', - 'ei6' => 'application/vnd.pg.osasli', - 'elc' => 'application/octet-stream', - 'emf' => 'application/x-msmetafile', - 'eml' => 'message/rfc822', - 'emma' => 'application/emma+xml', - 'emz' => 'application/x-msmetafile', - 'eol' => 'audio/vnd.digital-winds', - 'eot' => 'application/vnd.ms-fontobject', - 'eps' => 'application/postscript', - 'epub' => 'application/epub+zip', - 'es3' => 'application/vnd.eszigno3+xml', - 'esa' => 'application/vnd.osgi.subsystem', - 'esf' => 'application/vnd.epson.esf', - 'et3' => 'application/vnd.eszigno3+xml', - 'etx' => 'text/x-setext', - 'eva' => 'application/x-eva', - 'evy' => 'application/x-envoy', - 'exe' => 'application/x-msdownload', - 'exi' => 'application/exi', - 'ext' => 'application/vnd.novadigm.ext', - 'ez' => 'application/andrew-inset', - 'ez2' => 'application/vnd.ezpix-album', - 'ez3' => 'application/vnd.ezpix-package', - 'f' => 'text/x-fortran', - 'f4v' => 'video/x-f4v', - 'f77' => 'text/x-fortran', - 'f90' => 'text/x-fortran', - 'fbs' => 'image/vnd.fastbidsheet', - 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', - 'fcs' => 'application/vnd.isac.fcs', - 'fdf' => 'application/vnd.fdf', - 'fe_launch' => 'application/vnd.denovo.fcselayout-link', - 'fg5' => 'application/vnd.fujitsu.oasysgp', - 'fgd' => 'application/x-director', - 'fh' => 'image/x-freehand', - 'fh4' => 'image/x-freehand', - 'fh5' => 'image/x-freehand', - 'fh7' => 'image/x-freehand', - 'fhc' => 'image/x-freehand', - 'fig' => 'application/x-xfig', - 'flac' => 'audio/x-flac', - 'fli' => 'video/x-fli', - 'flo' => 'application/vnd.micrografx.flo', - 'flv' => 'video/x-flv', - 'flw' => 'application/vnd.kde.kivio', - 'flx' => 'text/vnd.fmi.flexstor', - 'fly' => 'text/vnd.fly', - 'fm' => 'application/vnd.framemaker', - 'fnc' => 'application/vnd.frogans.fnc', - 'for' => 'text/x-fortran', - 'fpx' => 'image/vnd.fpx', - 'frame' => 'application/vnd.framemaker', - 'fsc' => 'application/vnd.fsc.weblaunch', - 'fst' => 'image/vnd.fst', - 'ftc' => 'application/vnd.fluxtime.clip', - 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', - 'fvt' => 'video/vnd.fvt', - 'fxp' => 'application/vnd.adobe.fxp', - 'fxpl' => 'application/vnd.adobe.fxp', - 'fzs' => 'application/vnd.fuzzysheet', - 'g2w' => 'application/vnd.geoplan', - 'g3' => 'image/g3fax', - 'g3w' => 'application/vnd.geospace', - 'gac' => 'application/vnd.groove-account', - 'gam' => 'application/x-tads', - 'gbr' => 'application/rpki-ghostbusters', - 'gca' => 'application/x-gca-compressed', - 'gdl' => 'model/vnd.gdl', - 'geo' => 'application/vnd.dynageo', - 'gex' => 'application/vnd.geometry-explorer', - 'ggb' => 'application/vnd.geogebra.file', - 'ggt' => 'application/vnd.geogebra.tool', - 'ghf' => 'application/vnd.groove-help', - 'gif' => 'image/gif', - 'gim' => 'application/vnd.groove-identity-message', - 'gml' => 'application/gml+xml', - 'gmx' => 'application/vnd.gmx', - 'gnumeric' => 'application/x-gnumeric', - 'gph' => 'application/vnd.flographit', - 'gpx' => 'application/gpx+xml', - 'gqf' => 'application/vnd.grafeq', - 'gqs' => 'application/vnd.grafeq', - 'gram' => 'application/srgs', - 'gramps' => 'application/x-gramps-xml', - 'gre' => 'application/vnd.geometry-explorer', - 'grv' => 'application/vnd.groove-injector', - 'grxml' => 'application/srgs+xml', - 'gsf' => 'application/x-font-ghostscript', - 'gtar' => 'application/x-gtar', - 'gtm' => 'application/vnd.groove-tool-message', - 'gtw' => 'model/vnd.gtw', - 'gv' => 'text/vnd.graphviz', - 'gxf' => 'application/gxf', - 'gxt' => 'application/vnd.geonext', - 'h' => 'text/x-c', - 'h261' => 'video/h261', - 'h263' => 'video/h263', - 'h264' => 'video/h264', - 'hal' => 'application/vnd.hal+xml', - 'hbci' => 'application/vnd.hbci', - 'hdf' => 'application/x-hdf', - 'hh' => 'text/x-c', - 'hlp' => 'application/winhlp', - 'hpgl' => 'application/vnd.hp-hpgl', - 'hpid' => 'application/vnd.hp-hpid', - 'hps' => 'application/vnd.hp-hps', - 'hqx' => 'application/mac-binhex40', - 'htke' => 'application/vnd.kenameaapp', - 'htm' => 'text/html', - 'html' => 'text/html', - 'hvp' => 'application/vnd.yamaha.hv-voice', - 'hvs' => 'application/vnd.yamaha.hv-script', - 'i2g' => 'application/vnd.intergeo', - 'icc' => 'application/vnd.iccprofile', - 0 => 'application/vnd.lotus-1-2-3', - 'hvd' => 'application/vnd.yamaha.hv-dic', - 'ice' => 'x-conference/x-cooltalk', - 'icm' => 'application/vnd.iccprofile', - 'ico' => 'image/x-icon', - 'ics' => 'text/calendar', - 'ief' => 'image/ief', - 'ifb' => 'text/calendar', - 'ifm' => 'application/vnd.shana.informed.formdata', - 'iges' => 'model/iges', - 'igl' => 'application/vnd.igloader', - 'igm' => 'application/vnd.insors.igm', - 'igs' => 'model/iges', - 'igx' => 'application/vnd.micrografx.igx', - 'iif' => 'application/vnd.shana.informed.interchange', - 'imp' => 'application/vnd.accpac.simply.imp', - 'ims' => 'application/vnd.ms-ims', - 'in' => 'text/plain', - 'ink' => 'application/inkml+xml', - 'inkml' => 'application/inkml+xml', - 'install' => 'application/x-install-instructions', - 'iota' => 'application/vnd.astraea-software.iota', - 'ipfix' => 'application/ipfix', - 'ipk' => 'application/vnd.shana.informed.package', - 'irm' => 'application/vnd.ibm.rights-management', - 'irp' => 'application/vnd.irepository.package+xml', - 'iso' => 'application/x-iso9660-image', - 'itp' => 'application/vnd.shana.informed.formtemplate', - 'ivp' => 'application/vnd.immervision-ivp', - 'ivu' => 'application/vnd.immervision-ivu', - 'jad' => 'text/vnd.sun.j2me.app-descriptor', - 'jam' => 'application/vnd.jam', - 'jar' => 'application/java-archive', - 'java' => 'text/x-java-source', - 'jisp' => 'application/vnd.jisp', - 'jlt' => 'application/vnd.hp-jlyt', - 'jnlp' => 'application/x-java-jnlp-file', - 'joda' => 'application/vnd.joost.joda-archive', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpgm' => 'video/jpm', - 'jpgv' => 'video/jpeg', - 'jpm' => 'video/jpm', - 'js' => 'text/javascript', - 'json' => 'application/json', - 'jsonml' => 'application/jsonml+json', - 'kar' => 'audio/midi', - 'karbon' => 'application/vnd.kde.karbon', - 'kfo' => 'application/vnd.kde.kformula', - 'kia' => 'application/vnd.kidspiration', - 'kml' => 'application/vnd.google-earth.kml+xml', - 'kmz' => 'application/vnd.google-earth.kmz', - 'kne' => 'application/vnd.kinar', - 'knp' => 'application/vnd.kinar', - 'kon' => 'application/vnd.kde.kontour', - 'kpr' => 'application/vnd.kde.kpresenter', - 'kpt' => 'application/vnd.kde.kpresenter', - 'kpxx' => 'application/vnd.ds-keypoint', - 'ksp' => 'application/vnd.kde.kspread', - 'ktr' => 'application/vnd.kahootz', - 'ktx' => 'image/ktx', - 'ktz' => 'application/vnd.kahootz', - 'kwd' => 'application/vnd.kde.kword', - 'kwt' => 'application/vnd.kde.kword', - 'lasxml' => 'application/vnd.las.las+xml', - 'latex' => 'application/x-latex', - 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', - 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', - 'les' => 'application/vnd.hhe.lesson-player', - 'lha' => 'application/x-lzh-compressed', - 'link66' => 'application/vnd.route66.link66+xml', - 'list' => 'text/plain', - 'list3820' => 'application/vnd.ibm.modcap', - 'listafp' => 'application/vnd.ibm.modcap', - 'lnk' => 'application/x-ms-shortcut', - 'log' => 'text/plain', - 'lostxml' => 'application/lost+xml', - 'lrf' => 'application/octet-stream', - 'lrm' => 'application/vnd.ms-lrm', - 'ltf' => 'application/vnd.frogans.ltf', - 'lvp' => 'audio/vnd.lucent.voice', - 'lwp' => 'application/vnd.lotus-wordpro', - 'lzh' => 'application/x-lzh-compressed', - 'm13' => 'application/x-msmediaview', - 'm14' => 'application/x-msmediaview', - 'm1v' => 'video/mpeg', - 'm21' => 'application/mp21', - 'm2a' => 'audio/mpeg', - 'm2v' => 'video/mpeg', - 'm3a' => 'audio/mpeg', - 'm3u' => 'audio/x-mpegurl', - 'm3u8' => 'application/vnd.apple.mpegurl', - 'm4a' => 'audio/mp4', - 'm4u' => 'video/vnd.mpegurl', - 'm4v' => 'video/x-m4v', - 'ma' => 'application/mathematica', - 'mads' => 'application/mads+xml', - 'mag' => 'application/vnd.ecowin.chart', - 'maker' => 'application/vnd.framemaker', - 'man' => 'text/troff', - 'mar' => 'application/octet-stream', - 'mathml' => 'application/mathml+xml', - 'mb' => 'application/mathematica', - 'mbk' => 'application/vnd.mobius.mbk', - 'mbox' => 'application/mbox', - 'mc1' => 'application/vnd.medcalcdata', - 'mcd' => 'application/vnd.mcd', - 'mcurl' => 'text/vnd.curl.mcurl', - 'mdb' => 'application/x-msaccess', - 'mdi' => 'image/vnd.ms-modi', - 'me' => 'text/troff', - 'mesh' => 'model/mesh', - 'meta4' => 'application/metalink4+xml', - 'metalink' => 'application/metalink+xml', - 'mets' => 'application/mets+xml', - 'mfm' => 'application/vnd.mfmp', - 'mft' => 'application/rpki-manifest', - 'mgp' => 'application/vnd.osgeo.mapguide.package', - 'mgz' => 'application/vnd.proteus.magazine', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mie' => 'application/x-mie', - 'mif' => 'application/vnd.mif', - 'mime' => 'message/rfc822', - 'mj2' => 'video/mj2', - 'mjp2' => 'video/mj2', - 'mjs' => 'text/javascript', - 'mk3d' => 'video/x-matroska', - 'mka' => 'audio/x-matroska', - 'mks' => 'video/x-matroska', - 'mkv' => 'video/x-matroska', - 'mlp' => 'application/vnd.dolby.mlp', - 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', - 'mmf' => 'application/vnd.smaf', - 'mmr' => 'image/vnd.fujixerox.edmics-mmr', - 'mng' => 'video/x-mng', - 'mny' => 'application/x-msmoney', - 'mobi' => 'application/x-mobipocket-ebook', - 'mods' => 'application/mods+xml', - 'mov' => 'video/quicktime', - 'movie' => 'video/x-sgi-movie', - 'mp2' => 'audio/mpeg', - 'mp21' => 'application/mp21', - 'mp2a' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mp4a' => 'audio/mp4', - 'mp4s' => 'application/mp4', - 'mp4v' => 'video/mp4', - 'mpc' => 'application/vnd.mophun.certificate', - 'mpe' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpg4' => 'video/mp4', - 'mpga' => 'audio/mpeg', - 'mpkg' => 'application/vnd.apple.installer+xml', - 'mpm' => 'application/vnd.blueice.multipass', - 'mpn' => 'application/vnd.mophun.application', - 'mpp' => 'application/vnd.ms-project', - 'mpt' => 'application/vnd.ms-project', - 'mpy' => 'application/vnd.ibm.minipay', - 'mqy' => 'application/vnd.mobius.mqy', - 'mrc' => 'application/marc', - 'mrcx' => 'application/marcxml+xml', - 'ms' => 'text/troff', - 'mscml' => 'application/mediaservercontrol+xml', - 'mseed' => 'application/vnd.fdsn.mseed', - 'mseq' => 'application/vnd.mseq', - 'msf' => 'application/vnd.epson.msf', - 'msh' => 'model/mesh', - 'msi' => 'application/x-msdownload', - 'msl' => 'application/vnd.mobius.msl', - 'msty' => 'application/vnd.muvee.style', - 'mts' => 'model/vnd.mts', - 'mus' => 'application/vnd.musician', - 'musicxml' => 'application/vnd.recordare.musicxml+xml', - 'mvb' => 'application/x-msmediaview', - 'mwf' => 'application/vnd.mfer', - 'mxf' => 'application/mxf', - 'mxl' => 'application/vnd.recordare.musicxml', - 'mxml' => 'application/xv+xml', - 'mxs' => 'application/vnd.triscape.mxs', - 'mxu' => 'video/vnd.mpegurl', - 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', - 'n3' => 'text/n3', - 'nb' => 'application/mathematica', - 'nbp' => 'application/vnd.wolfram.player', - 'nc' => 'application/x-netcdf', - 'ncx' => 'application/x-dtbncx+xml', - 'nfo' => 'text/x-nfo', - 'ngdat' => 'application/vnd.nokia.n-gage.data', - 'nitf' => 'application/vnd.nitf', - 'nlu' => 'application/vnd.neurolanguage.nlu', - 'nml' => 'application/vnd.enliven', - 'nnd' => 'application/vnd.noblenet-directory', - 'nns' => 'application/vnd.noblenet-sealer', - 'nnw' => 'application/vnd.noblenet-web', - 'npx' => 'image/vnd.net-fpx', - 'nsc' => 'application/x-conference', - 'nsf' => 'application/vnd.lotus-notes', - 'ntf' => 'application/vnd.nitf', - 'nzb' => 'application/x-nzb', - 'oa2' => 'application/vnd.fujitsu.oasys2', - 'oa3' => 'application/vnd.fujitsu.oasys3', - 'oas' => 'application/vnd.fujitsu.oasys', - 'obd' => 'application/x-msbinder', - 'obj' => 'application/x-tgif', - 'oda' => 'application/oda', - 'odb' => 'application/vnd.oasis.opendocument.database', - 'odc' => 'application/vnd.oasis.opendocument.chart', - 'odf' => 'application/vnd.oasis.opendocument.formula', - 'odft' => 'application/vnd.oasis.opendocument.formula-template', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odi' => 'application/vnd.oasis.opendocument.image', - 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', - 'omdoc' => 'application/omdoc+xml', - 'onepkg' => 'application/onenote', - 'onetmp' => 'application/onenote', - 'onetoc' => 'application/onenote', - 'onetoc2' => 'application/onenote', - 'opf' => 'application/oebps-package+xml', - 'opml' => 'text/x-opml', - 'oprc' => 'application/vnd.palm', - 'opus' => 'audio/ogg', - 'org' => 'application/vnd.lotus-organizer', - 'osf' => 'application/vnd.yamaha.openscoreformat', - 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', - 'otc' => 'application/vnd.oasis.opendocument.chart-template', - 'otf' => 'font/otf', - 'otg' => 'application/vnd.oasis.opendocument.graphics-template', - 'oth' => 'application/vnd.oasis.opendocument.text-web', - 'oti' => 'application/vnd.oasis.opendocument.image-template', - 'otp' => 'application/vnd.oasis.opendocument.presentation-template', - 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', - 'ott' => 'application/vnd.oasis.opendocument.text-template', - 'oxps' => 'application/oxps', - 'oxt' => 'application/vnd.openofficeorg.extension', - 'p' => 'text/x-pascal', - 'p10' => 'application/pkcs10', - 'p12' => 'application/x-pkcs12', - 'p7b' => 'application/x-pkcs7-certificates', - 'p7c' => 'application/pkcs7-mime', - 'p7m' => 'application/pkcs7-mime', - 'p7r' => 'application/x-pkcs7-certreqresp', - 'p7s' => 'application/pkcs7-signature', - 'p8' => 'application/pkcs8', - 'pas' => 'text/x-pascal', - 'paw' => 'application/vnd.pawaafile', - 'pbd' => 'application/vnd.powerbuilder6', - 'pbm' => 'image/x-portable-bitmap', - 'pcap' => 'application/vnd.tcpdump.pcap', - 'pcf' => 'application/x-font-pcf', - 'pcl' => 'application/vnd.hp-pcl', - 'pclxl' => 'application/vnd.hp-pclxl', - 'pct' => 'image/x-pict', - 'pcurl' => 'application/vnd.curl.pcurl', - 'pcx' => 'image/x-pcx', - 'pdb' => 'application/vnd.palm', - 'pdf' => 'application/pdf', - 'pfa' => 'application/x-font-type1', - 'pfb' => 'application/x-font-type1', - 'pfm' => 'application/x-font-type1', - 'pfr' => 'application/font-tdpfr', - 'pfx' => 'application/x-pkcs12', - 'pgm' => 'image/x-portable-graymap', - 'pgn' => 'application/x-chess-pgn', - 'pgp' => 'application/pgp-encrypted', - 'pic' => 'image/x-pict', - 'pkg' => 'application/octet-stream', - 'pki' => 'application/pkixcmp', - 'pkipath' => 'application/pkix-pkipath', - 'plb' => 'application/vnd.3gpp.pic-bw-large', - 'plc' => 'application/vnd.mobius.plc', - 'plf' => 'application/vnd.pocketlearn', - 'pls' => 'application/pls+xml', - 'pml' => 'application/vnd.ctc-posml', - 'png' => 'image/png', - 'pnm' => 'image/x-portable-anymap', - 'portpkg' => 'application/vnd.macports.portpkg', - 'pot' => 'application/vnd.ms-powerpoint', - 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', - 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', - 'ppd' => 'application/vnd.cups-ppd', - 'ppm' => 'image/x-portable-pixmap', - 'pps' => 'application/vnd.ms-powerpoint', - 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', - 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'pqa' => 'application/vnd.palm', - 'prc' => 'application/x-mobipocket-ebook', - 'pre' => 'application/vnd.lotus-freelance', - 'prf' => 'application/pics-rules', - 'ps' => 'application/postscript', - 'psb' => 'application/vnd.3gpp.pic-bw-small', - 'psd' => 'image/vnd.adobe.photoshop', - 'psf' => 'application/x-font-linux-psf', - 'pskcxml' => 'application/pskc+xml', - 'ptid' => 'application/vnd.pvi.ptid1', - 'pub' => 'application/x-mspublisher', - 'pvb' => 'application/vnd.3gpp.pic-bw-var', - 'pwn' => 'application/vnd.3m.post-it-notes', - 'pya' => 'audio/vnd.ms-playready.media.pya', - 'pyv' => 'video/vnd.ms-playready.media.pyv', - 'qam' => 'application/vnd.epson.quickanime', - 'qbo' => 'application/vnd.intu.qbo', - 'qfx' => 'application/vnd.intu.qfx', - 'qps' => 'application/vnd.publishare-delta-tree', - 'qt' => 'video/quicktime', - 'qwd' => 'application/vnd.quark.quarkxpress', - 'qwt' => 'application/vnd.quark.quarkxpress', - 'qxb' => 'application/vnd.quark.quarkxpress', - 'qxd' => 'application/vnd.quark.quarkxpress', - 'qxl' => 'application/vnd.quark.quarkxpress', - 'qxt' => 'application/vnd.quark.quarkxpress', - 'ra' => 'audio/x-pn-realaudio', - 'ram' => 'audio/x-pn-realaudio', - 'rar' => 'application/x-rar-compressed', - 'ras' => 'image/x-cmu-raster', - 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', - 'rdf' => 'application/rdf+xml', - 'rdz' => 'application/vnd.data-vision.rdz', - 'rep' => 'application/vnd.businessobjects', - 'res' => 'application/x-dtbresource+xml', - 'rgb' => 'image/x-rgb', - 'rif' => 'application/reginfo+xml', - 'rip' => 'audio/vnd.rip', - 'ris' => 'application/x-research-info-systems', - 'rl' => 'application/resource-lists+xml', - 'rlc' => 'image/vnd.fujixerox.edmics-rlc', - 'rld' => 'application/resource-lists-diff+xml', - 'rm' => 'application/vnd.rn-realmedia', - 'rmi' => 'audio/midi', - 'rmp' => 'audio/x-pn-realaudio-plugin', - 'rms' => 'application/vnd.jcp.javame.midlet-rms', - 'rmvb' => 'application/vnd.rn-realmedia-vbr', - 'rnc' => 'application/relax-ng-compact-syntax', - 'roa' => 'application/rpki-roa', - 'roff' => 'text/troff', - 'rp9' => 'application/vnd.cloanto.rp9', - 'rpss' => 'application/vnd.nokia.radio-presets', - 'rpst' => 'application/vnd.nokia.radio-preset', - 'rq' => 'application/sparql-query', - 'rs' => 'application/rls-services+xml', - 'rsd' => 'application/rsd+xml', - 'rss' => 'application/rss+xml', - 'rtf' => 'application/rtf', - 'rtx' => 'text/richtext', - 's' => 'text/x-asm', - 's3m' => 'audio/s3m', - 'saf' => 'application/vnd.yamaha.smaf-audio', - 'sbml' => 'application/sbml+xml', - 'sc' => 'application/vnd.ibm.secure-container', - 'scd' => 'application/x-msschedule', - 'scm' => 'application/vnd.lotus-screencam', - 'scq' => 'application/scvp-cv-request', - 'scs' => 'application/scvp-cv-response', - 'scurl' => 'text/vnd.curl.scurl', - 'sda' => 'application/vnd.stardivision.draw', - 'sdc' => 'application/vnd.stardivision.calc', - 'sdd' => 'application/vnd.stardivision.impress', - 'sdkd' => 'application/vnd.solent.sdkm+xml', - 'sdkm' => 'application/vnd.solent.sdkm+xml', - 'sdp' => 'application/sdp', - 'sdw' => 'application/vnd.stardivision.writer', - 'see' => 'application/vnd.seemail', - 'seed' => 'application/vnd.fdsn.seed', - 'sema' => 'application/vnd.sema', - 'semd' => 'application/vnd.semd', - 'semf' => 'application/vnd.semf', - 'ser' => 'application/java-serialized-object', - 'setpay' => 'application/set-payment-initiation', - 'setreg' => 'application/set-registration-initiation', - 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', - 'sfs' => 'application/vnd.spotfire.sfs', - 'sfv' => 'text/x-sfv', - 'sgi' => 'image/sgi', - 'sgl' => 'application/vnd.stardivision.writer-global', - 'sgm' => 'text/sgml', - 'sgml' => 'text/sgml', - 'sh' => 'application/x-sh', - 'shar' => 'application/x-shar', - 'shf' => 'application/shf+xml', - 'sid' => 'image/x-mrsid-image', - 'sig' => 'application/pgp-signature', - 'sil' => 'audio/silk', - 'silo' => 'model/mesh', - 'sis' => 'application/vnd.symbian.install', - 'sisx' => 'application/vnd.symbian.install', - 'sit' => 'application/x-stuffit', - 'sitx' => 'application/x-stuffitx', - 'skd' => 'application/vnd.koan', - 'skm' => 'application/vnd.koan', - 'skp' => 'application/vnd.koan', - 'skt' => 'application/vnd.koan', - 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', - 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', - 'slt' => 'application/vnd.epson.salt', - 'sm' => 'application/vnd.stepmania.stepchart', - 'smf' => 'application/vnd.stardivision.math', - 'smi' => 'application/smil+xml', - 'smil' => 'application/smil+xml', - 'smv' => 'video/x-smv', - 'smzip' => 'application/vnd.stepmania.package', - 'snd' => 'audio/basic', - 'snf' => 'application/x-font-snf', - 'so' => 'application/octet-stream', - 'spc' => 'application/x-pkcs7-certificates', - 'spf' => 'application/vnd.yamaha.smaf-phrase', - 'spl' => 'application/x-futuresplash', - 'spot' => 'text/vnd.in3d.spot', - 'spp' => 'application/scvp-vp-response', - 'spq' => 'application/scvp-vp-request', - 'spx' => 'audio/ogg', - 'sql' => 'application/x-sql', - 'src' => 'application/x-wais-source', - 'srt' => 'application/x-subrip', - 'sru' => 'application/sru+xml', - 'srx' => 'application/sparql-results+xml', - 'ssdl' => 'application/ssdl+xml', - 'sse' => 'application/vnd.kodak-descriptor', - 'ssf' => 'application/vnd.epson.ssf', - 'ssml' => 'application/ssml+xml', - 'st' => 'application/vnd.sailingtracker.track', - 'stc' => 'application/vnd.sun.xml.calc.template', - 'std' => 'application/vnd.sun.xml.draw.template', - 'stf' => 'application/vnd.wt.stf', - 'sti' => 'application/vnd.sun.xml.impress.template', - 'stk' => 'application/hyperstudio', - 'stl' => 'application/vnd.ms-pki.stl', - 'str' => 'application/vnd.pg.format', - 'stw' => 'application/vnd.sun.xml.writer.template', - 'sub' => 'text/vnd.dvb.subtitle', - 'sus' => 'application/vnd.sus-calendar', - 'susp' => 'application/vnd.sus-calendar', - 'sv4cpio' => 'application/x-sv4cpio', - 'sv4crc' => 'application/x-sv4crc', - 'svc' => 'application/vnd.dvb.service', - 'svd' => 'application/vnd.svd', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'swa' => 'application/x-director', - 'swf' => 'application/x-shockwave-flash', - 'swi' => 'application/vnd.aristanetworks.swi', - 'sxc' => 'application/vnd.sun.xml.calc', - 'sxd' => 'application/vnd.sun.xml.draw', - 'sxg' => 'application/vnd.sun.xml.writer.global', - 'sxi' => 'application/vnd.sun.xml.impress', - 'sxm' => 'application/vnd.sun.xml.math', - 'sxw' => 'application/vnd.sun.xml.writer', - 't' => 'text/troff', - 't3' => 'application/x-t3vm-image', - 'taglet' => 'application/vnd.mynfc', - 'tao' => 'application/vnd.tao.intent-module-archive', - 'tar' => 'application/x-tar', - 'tcap' => 'application/vnd.3gpp2.tcap', - 'tcl' => 'application/x-tcl', - 'teacher' => 'application/vnd.smart.teacher', - 'tei' => 'application/tei+xml', - 'teicorpus' => 'application/tei+xml', - 'tex' => 'application/x-tex', - 'texi' => 'application/x-texinfo', - 'texinfo' => 'application/x-texinfo', - 'text' => 'text/plain', - 'tfi' => 'application/thraud+xml', - 'tfm' => 'application/x-tex-tfm', - 'tga' => 'image/x-tga', - 'thmx' => 'application/vnd.ms-officetheme', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'tmo' => 'application/vnd.tmobile-livetv', - 'torrent' => 'application/x-bittorrent', - 'tpl' => 'application/vnd.groove-tool-template', - 'tpt' => 'application/vnd.trid.tpt', - 'tr' => 'text/troff', - 'tra' => 'application/vnd.trueapp', - 'trm' => 'application/x-msterminal', - 'tsd' => 'application/timestamped-data', - 'tsv' => 'text/tab-separated-values', - 'ttc' => 'font/collection', - 'ttf' => 'font/ttf', - 'ttl' => 'text/turtle', - 'twd' => 'application/vnd.simtech-mindmapper', - 'twds' => 'application/vnd.simtech-mindmapper', - 'txd' => 'application/vnd.genomatix.tuxedo', - 'txf' => 'application/vnd.mobius.txf', - 'txt' => 'text/plain', - 'u32' => 'application/x-authorware-bin', - 'udeb' => 'application/x-debian-package', - 'ufd' => 'application/vnd.ufdl', - 'ufdl' => 'application/vnd.ufdl', - 'ulx' => 'application/x-glulx', - 'umj' => 'application/vnd.umajin', - 'unityweb' => 'application/vnd.unity', - 'uoml' => 'application/vnd.uoml+xml', - 'uri' => 'text/uri-list', - 'uris' => 'text/uri-list', - 'urls' => 'text/uri-list', - 'ustar' => 'application/x-ustar', - 'utz' => 'application/vnd.uiq.theme', - 'uu' => 'text/x-uuencode', - 'uva' => 'audio/vnd.dece.audio', - 'uvd' => 'application/vnd.dece.data', - 'uvf' => 'application/vnd.dece.data', - 'uvg' => 'image/vnd.dece.graphic', - 'uvh' => 'video/vnd.dece.hd', - 'uvi' => 'image/vnd.dece.graphic', - 'uvm' => 'video/vnd.dece.mobile', - 'uvp' => 'video/vnd.dece.pd', - 'uvs' => 'video/vnd.dece.sd', - 'uvt' => 'application/vnd.dece.ttml+xml', - 'uvu' => 'video/vnd.uvvu.mp4', - 'uvv' => 'video/vnd.dece.video', - 'uvva' => 'audio/vnd.dece.audio', - 'uvvd' => 'application/vnd.dece.data', - 'uvvf' => 'application/vnd.dece.data', - 'uvvg' => 'image/vnd.dece.graphic', - 'uvvh' => 'video/vnd.dece.hd', - 'uvvi' => 'image/vnd.dece.graphic', - 'uvvm' => 'video/vnd.dece.mobile', - 'uvvp' => 'video/vnd.dece.pd', - 'uvvs' => 'video/vnd.dece.sd', - 'uvvt' => 'application/vnd.dece.ttml+xml', - 'uvvu' => 'video/vnd.uvvu.mp4', - 'uvvv' => 'video/vnd.dece.video', - 'uvvx' => 'application/vnd.dece.unspecified', - 'uvvz' => 'application/vnd.dece.zip', - 'uvx' => 'application/vnd.dece.unspecified', - 'uvz' => 'application/vnd.dece.zip', - 'vcard' => 'text/vcard', - 'vcd' => 'application/x-cdlink', - 'vcf' => 'text/x-vcard', - 'vcg' => 'application/vnd.groove-vcard', - 'vcs' => 'text/x-vcalendar', - 'vcx' => 'application/vnd.vcx', - 'vis' => 'application/vnd.visionary', - 'viv' => 'video/vnd.vivo', - 'vob' => 'video/x-ms-vob', - 'vor' => 'application/vnd.stardivision.writer', - 'vox' => 'application/x-authorware-bin', - 'vrml' => 'model/vrml', - 'vsd' => 'application/vnd.visio', - 'vsf' => 'application/vnd.vsf', - 'vss' => 'application/vnd.visio', - 'vst' => 'application/vnd.visio', - 'vsw' => 'application/vnd.visio', - 'vtu' => 'model/vnd.vtu', - 'vxml' => 'application/voicexml+xml', - 'w3d' => 'application/x-director', - 'wad' => 'application/x-doom', - 'wav' => 'audio/x-wav', - 'wax' => 'audio/x-ms-wax', - 'wbmp' => 'image/vnd.wap.wbmp', - 'wbs' => 'application/vnd.criticaltools.wbs+xml', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wcm' => 'application/vnd.ms-works', - 'wdb' => 'application/vnd.ms-works', - 'wdp' => 'image/vnd.ms-photo', - 'weba' => 'audio/webm', - 'webm' => 'video/webm', - 'webp' => 'image/webp', - 'wg' => 'application/vnd.pmi.widget', - 'wgt' => 'application/widget', - 'wks' => 'application/vnd.ms-works', - 'wm' => 'video/x-ms-wm', - 'wma' => 'audio/x-ms-wma', - 'wmd' => 'application/x-ms-wmd', - 'wmf' => 'application/x-msmetafile', - 'wml' => 'text/vnd.wap.wml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'wmls' => 'text/vnd.wap.wmlscript', - 'wmlsc' => 'application/vnd.wap.wmlscriptc', - 'wmv' => 'video/x-ms-wmv', - 'wmx' => 'video/x-ms-wmx', - 'wmz' => 'application/x-msmetafile', - 'woff' => 'font/woff', - 'woff2' => 'font/woff2', - 'wpd' => 'application/vnd.wordperfect', - 'wpl' => 'application/vnd.ms-wpl', - 'wps' => 'application/vnd.ms-works', - 'wqd' => 'application/vnd.wqd', - 'wri' => 'application/x-mswrite', - 'wrl' => 'model/vrml', - 'wsdl' => 'application/wsdl+xml', - 'wspolicy' => 'application/wspolicy+xml', - 'wtb' => 'application/vnd.webturbo', - 'wvx' => 'video/x-ms-wvx', - 'x32' => 'application/x-authorware-bin', - 'x3d' => 'model/x3d+xml', - 'x3db' => 'model/x3d+binary', - 'x3dbz' => 'model/x3d+binary', - 'x3dv' => 'model/x3d+vrml', - 'x3dvz' => 'model/x3d+vrml', - 'x3dz' => 'model/x3d+xml', - 'xaml' => 'application/xaml+xml', - 'xap' => 'application/x-silverlight-app', - 'xar' => 'application/vnd.xara', - 'xbap' => 'application/x-ms-xbap', - 'xbd' => 'application/vnd.fujixerox.docuworks.binder', - 'xbm' => 'image/x-xbitmap', - 'xdf' => 'application/xcap-diff+xml', - 'xdm' => 'application/vnd.syncml.dm+xml', - 'xdp' => 'application/vnd.adobe.xdp+xml', - 'xdssc' => 'application/dssc+xml', - 'xdw' => 'application/vnd.fujixerox.docuworks', - 'xenc' => 'application/xenc+xml', - 'xer' => 'application/patch-ops-error+xml', - 'xfdf' => 'application/vnd.adobe.xfdf', - 'xfdl' => 'application/vnd.xfdl', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'xhvml' => 'application/xv+xml', - 'xif' => 'image/vnd.xiff', - 'xla' => 'application/vnd.ms-excel', - 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', - 'xlc' => 'application/vnd.ms-excel', - 'xlf' => 'application/x-xliff+xml', - 'xlm' => 'application/vnd.ms-excel', - 'xls' => 'application/vnd.ms-excel', - 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', - 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xlt' => 'application/vnd.ms-excel', - 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', - 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'xlw' => 'application/vnd.ms-excel', - 'xm' => 'audio/xm', - 'xml' => 'application/xml', - 'xo' => 'application/vnd.olpc-sugar', - 'xop' => 'application/xop+xml', - 'xpi' => 'application/x-xpinstall', - 'xpl' => 'application/xproc+xml', - 'xpm' => 'image/x-xpixmap', - 'xpr' => 'application/vnd.is-xpr', - 'xps' => 'application/vnd.ms-xpsdocument', - 'xpw' => 'application/vnd.intercon.formnet', - 'xpx' => 'application/vnd.intercon.formnet', - 'xsl' => 'application/xml', - 'xslt' => 'application/xslt+xml', - 'xsm' => 'application/vnd.syncml+xml', - 'xspf' => 'application/xspf+xml', - 'xul' => 'application/vnd.mozilla.xul+xml', - 'xvm' => 'application/xv+xml', - 'xvml' => 'application/xv+xml', - 'xwd' => 'image/x-xwindowdump', - 'xyz' => 'chemical/x-xyz', - 'xz' => 'application/x-xz', - 'yang' => 'application/yang', - 'yin' => 'application/yin+xml', - 'z1' => 'application/x-zmachine', - 'z2' => 'application/x-zmachine', - 'z3' => 'application/x-zmachine', - 'z4' => 'application/x-zmachine', - 'z5' => 'application/x-zmachine', - 'z6' => 'application/x-zmachine', - 'z7' => 'application/x-zmachine', - 'z8' => 'application/x-zmachine', - 'zaz' => 'application/vnd.zzazz.deck+xml', - 'zip' => 'application/zip', - 'zir' => 'application/vnd.zul', - 'zirz' => 'application/vnd.zul', - 'zmm' => 'application/vnd.handheld-entertainment+xml', - ]; - - if (PHP_VERSION_ID >= 80100) { - $coreMimeTypes = array_replace($coreMimeTypes, array('xz' => 'application/octet-stream')); - } - - $this->assertSame($coreMimeTypes, - require __DIR__ . '/../../../framework/helpers/mimeTypes.php' - ); - } -} diff --git a/tests/framework/i18n/FormatterDateTest.php b/tests/framework/i18n/FormatterDateTest.php index 43fc1311cb9..ef488433572 100644 --- a/tests/framework/i18n/FormatterDateTest.php +++ b/tests/framework/i18n/FormatterDateTest.php @@ -143,23 +143,23 @@ public function testIntlAsTime() public function testAsTime() { $value = time(); - $this->assertSame(date('g:i:s A', $value), $this->formatter->asTime($value)); + $this->assertSameAnyWhitespace(date('g:i:s A', $value), $this->formatter->asTime($value)); $this->assertSame(date('h:i:s A', $value), $this->formatter->asTime($value, 'php:h:i:s A')); $value = new DateTime(); - $this->assertSame(date('g:i:s A', $value->getTimestamp()), $this->formatter->asTime($value)); + $this->assertSameAnyWhitespace(date('g:i:s A', $value->getTimestamp()), $this->formatter->asTime($value)); $this->assertSame(date('h:i:s A', $value->getTimestamp()), $this->formatter->asTime($value, 'php:h:i:s A')); if (version_compare(PHP_VERSION, '5.5.0', '>=')) { $value = new \DateTimeImmutable(); - $this->assertSame(date('g:i:s A', $value->getTimestamp()), $this->formatter->asTime($value)); + $this->assertSameAnyWhitespace(date('g:i:s A', $value->getTimestamp()), $this->formatter->asTime($value)); $this->assertSame(date('h:i:s A', $value->getTimestamp()), $this->formatter->asTime($value, 'php:h:i:s A')); } // empty input - $this->assertSame('12:00:00 AM', $this->formatter->asTime('')); - $this->assertSame('12:00:00 AM', $this->formatter->asTime(0)); - $this->assertSame('12:00:00 AM', $this->formatter->asTime(false)); + $this->assertSameAnyWhitespace('12:00:00 AM', $this->formatter->asTime('')); + $this->assertSameAnyWhitespace('12:00:00 AM', $this->formatter->asTime(0)); + $this->assertSameAnyWhitespace('12:00:00 AM', $this->formatter->asTime(false)); // null display $this->assertSame($this->formatter->nullDisplay, $this->formatter->asTime(null)); } @@ -178,23 +178,35 @@ public function testIntlAsDatetime() public function testAsDatetime() { $value = time(); - $this->assertRegExp(date('~M j, Y,? g:i:s A~', $value), $this->formatter->asDatetime($value)); + $this->assertRegExp( + $this->sanitizeWhitespaces(date('~M j, Y,? g:i:s A~', $value)), + $this->sanitizeWhitespaces($this->formatter->asDatetime($value)) + ); $this->assertSame(date('Y/m/d h:i:s A', $value), $this->formatter->asDatetime($value, 'php:Y/m/d h:i:s A')); $value = new DateTime(); - $this->assertRegExp(date('~M j, Y,? g:i:s A~', $value->getTimestamp()), $this->formatter->asDatetime($value)); + $this->assertRegExp( + $this->sanitizeWhitespaces(date('~M j, Y,? g:i:s A~', $value->getTimestamp())), + $this->sanitizeWhitespaces($this->formatter->asDatetime($value)) + ); $this->assertSame(date('Y/m/d h:i:s A', $value->getTimestamp()), $this->formatter->asDatetime($value, 'php:Y/m/d h:i:s A')); // empty time $value = new DateTime(); $date = $value->format('Y-m-d'); $value = new DateTime($date); - $this->assertRegExp(date('~M j, Y,? g:i:s A~', $value->getTimestamp()), $this->formatter->asDatetime($date)); + $this->assertRegExp( + $this->sanitizeWhitespaces(date('~M j, Y,? g:i:s A~', $value->getTimestamp())), + $this->sanitizeWhitespaces($this->formatter->asDatetime($date)) + ); $this->assertSame(date('Y/m/d h:i:s A', $value->getTimestamp()), $this->formatter->asDatetime($date, 'php:Y/m/d h:i:s A')); if (PHP_VERSION_ID >= 50500) { $value = new \DateTimeImmutable(); - $this->assertRegExp(date('~M j, Y,? g:i:s A~', $value->getTimestamp()), $this->formatter->asDatetime($value)); + $this->assertRegExp( + $this->sanitizeWhitespaces(date('~M j, Y,? g:i:s A~', $value->getTimestamp())), + $this->sanitizeWhitespaces($this->formatter->asDatetime($value)) + ); $this->assertSame(date('Y/m/d h:i:s A', $value->getTimestamp()), $this->formatter->asDatetime($value, 'php:Y/m/d h:i:s A')); } @@ -205,9 +217,18 @@ public function testAsDatetime() } // empty input - $this->assertRegExp('~Jan 1, 1970,? 12:00:00 AM~', $this->formatter->asDatetime('')); - $this->assertRegExp('~Jan 1, 1970,? 12:00:00 AM~', $this->formatter->asDatetime(0)); - $this->assertRegExp('~Jan 1, 1970,? 12:00:00 AM~', $this->formatter->asDatetime(false)); + $this->assertRegExp( + $this->sanitizeWhitespaces('~Jan 1, 1970,? 12:00:00 AM~'), + $this->sanitizeWhitespaces($this->formatter->asDatetime('')) + ); + $this->assertRegExp( + $this->sanitizeWhitespaces('~Jan 1, 1970,? 12:00:00 AM~'), + $this->sanitizeWhitespaces($this->formatter->asDatetime(0)) + ); + $this->assertRegExp( + $this->sanitizeWhitespaces('~Jan 1, 1970,? 12:00:00 AM~'), + $this->sanitizeWhitespaces($this->formatter->asDatetime(false)) + ); // null display $this->assertSame($this->formatter->nullDisplay, $this->formatter->asDatetime(null)); } diff --git a/tests/framework/log/FileTargetTest.php b/tests/framework/log/FileTargetTest.php index 217a94af0f0..4f6eaf4ae75 100644 --- a/tests/framework/log/FileTargetTest.php +++ b/tests/framework/log/FileTargetTest.php @@ -12,6 +12,7 @@ use yii\log\Dispatcher; use yii\log\FileTarget; use yii\log\Logger; +use yiiunit\framework\log\mocks\CustomLogger; use yiiunit\TestCase; /** @@ -110,4 +111,53 @@ public function testRotate() $this->assertFileNotExists($logFile . '.3'); $this->assertFileNotExists($logFile . '.4'); } + + public function testLogEmptyStrings() + { + $logFile = Yii::getAlias('@yiiunit/runtime/log/filetargettest.log'); + $this->clearLogFile($logFile); + + $logger = new CustomLogger(); + $logger->logFile = $logFile; + $logger->messages = array_fill(0, 1, 'xxx'); + $logger->export(); + + $test = file($logFile); + $this->assertEquals("xxx\n", $test[0]); + + $this->clearLogFile($logFile); + + $logger = new CustomLogger(); + $logger->logFile = $logFile; + $logger->messages = array_fill(0, 3, 'xxx'); + $logger->export(); + + $test = file($logFile); + $this->assertEquals("xxx\n", $test[0]); + $this->assertEquals("xxx\n", $test[1]); + $this->assertEquals("xxx\n", $test[2]); + + $this->clearLogFile($logFile); + + $logger->messages = array_fill(0, 1, 'yyy'); + $logger->export(); + + $this->assertFileNotExists($logFile); + + $logger->messages = array_fill(0, 10, ''); + $logger->export(); + + $this->assertFileNotExists($logFile); + + $logger->messages = array_fill(0, 10, null); + $logger->export(); + + $this->assertFileNotExists($logFile); + } + + private function clearLogFile($logFile) + { + FileHelper::removeDirectory(dirname($logFile)); + mkdir(dirname($logFile), 0777, true); + } } diff --git a/tests/framework/log/mocks/CustomLogger.php b/tests/framework/log/mocks/CustomLogger.php new file mode 100644 index 00000000000..5dae134b7ed --- /dev/null +++ b/tests/framework/log/mocks/CustomLogger.php @@ -0,0 +1,22 @@ +auth->createRole('Admin'); $this->auth->add($admin); - $this->auth->assign($admin, 1); $manager = $this->auth->createRole('Manager'); $this->auth->add($manager); + + $adminUserRoles = $this->auth->getRolesByUser(1); + $this->assertArrayHasKey('myDefaultRole', $adminUserRoles); + $this->assertArrayNotHasKey('Admin', $adminUserRoles); + $this->auth->assign($admin, 1); + + $managerUserRoles = $this->auth->getRolesByUser(2); + $this->assertArrayHasKey('myDefaultRole', $managerUserRoles); + $this->assertArrayNotHasKey('Manager', $managerUserRoles); $this->auth->assign($manager, 2); $adminUserRoles = $this->auth->getRolesByUser(1); + $this->assertArrayHasKey('myDefaultRole', $adminUserRoles); $this->assertArrayHasKey('Admin', $adminUserRoles); $this->assertEquals($admin->name, $adminUserRoles['Admin']->name); $managerUserRoles = $this->auth->getRolesByUser(2); + $this->assertArrayHasKey('myDefaultRole', $managerUserRoles); $this->assertArrayHasKey('Manager', $managerUserRoles); $this->assertEquals($manager->name, $managerUserRoles['Manager']->name); } @@ -350,7 +360,7 @@ public function testCheckAccessCache() } $this->assertSingleQueryToAssignmentsTable($logTarget); - // verify cache is flushed on unassign (createPost is now false again) + // verify cache is flushed on revoke (createPost is now false again) $this->auth->revoke($this->auth->getRole('admin'), 'reader A'); foreach (['readPost' => true, 'createPost' => false] as $permission => $result) { $this->assertEquals($result, $this->auth->checkAccess('reader A', $permission), "Checking $permission"); @@ -379,8 +389,11 @@ public function testCheckAccessCache() private function assertSingleQueryToAssignmentsTable($logTarget) { - $this->assertCount(1, $logTarget->messages, 'Only one query should have been performed, but there are the following logs: ' . print_r($logTarget->messages, true)); - $this->assertContains('auth_assignment', $logTarget->messages[0][0], 'Log message should be a query to auth_assignment table'); + $messages = array_filter($logTarget->messages, function ($message) { + return strpos($message[0], 'auth_assignment') !== false; + }); + $this->assertCount(1, $messages, 'Only one query should have been performed, but there are the following logs: ' . print_r($logTarget->messages, true)); + $this->assertContains('auth_assignment', $messages[0][0], 'Log message should be a query to auth_assignment table'); $logTarget->messages = []; } } diff --git a/tests/framework/validators/FileValidatorTest.php b/tests/framework/validators/FileValidatorTest.php index f6af4ec6816..157269e5d41 100644 --- a/tests/framework/validators/FileValidatorTest.php +++ b/tests/framework/validators/FileValidatorTest.php @@ -546,7 +546,8 @@ public function validMimeTypes() ['test.tar.xz', 'application/x-xz', 'tar.xz'], ]); - if (PHP_VERSION_ID >= 80100) { + # fix for bundled libmagic bug, see also https://github.com/yiisoft/yii2/issues/19925 + if ((PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80122) || (PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80209)) { $v81_zx = ['test.tar.xz', 'application/octet-stream', 'tar.xz']; array_pop($validMimeTypes); $validMimeTypes[] = $v81_zx; diff --git a/tests/framework/web/ControllerTest.php b/tests/framework/web/ControllerTest.php index 21680f08cc6..1ce51eeb298 100644 --- a/tests/framework/web/ControllerTest.php +++ b/tests/framework/web/ControllerTest.php @@ -10,7 +10,6 @@ use RuntimeException; use Yii; use yii\base\InlineAction; -use yii\web\HttpException; use yii\web\NotFoundHttpException; use yii\web\Response; use yii\web\ServerErrorHttpException; @@ -332,4 +331,34 @@ public function testRedirect() $this->assertEquals($this->controller->redirect(['//controller/index', 'id_1' => 3, 'id_2' => 4])->headers->get('location'), '/index.php?r=controller%2Findex&id_1=3&id_2=4'); $this->assertEquals($this->controller->redirect(['//controller/index', 'slug' => 'äöüß!"§$%&/()'])->headers->get('location'), '/index.php?r=controller%2Findex&slug=%C3%A4%C3%B6%C3%BC%C3%9F%21%22%C2%A7%24%25%26%2F%28%29'); } + + public function testUnionBindingActionParams() + { + if (PHP_VERSION_ID < 80000) { + $this->markTestSkipped('Can not be tested on PHP < 8.0'); + return; + } + + // Use the PHP80 controller for this test + $this->controller = new FakePhp80Controller('fake', new \yii\web\Application([ + 'id' => 'app', + 'basePath' => __DIR__, + 'components' => [ + 'request' => [ + 'cookieValidationKey' => 'wefJDF8sfdsfSDefwqdxj9oq', + 'scriptFile' => __DIR__ . '/index.php', + 'scriptUrl' => '/index.php', + ], + ], + ])); + + $this->mockWebApplication(['controller' => $this->controller]); + + $injectionAction = new InlineAction('injection', $this->controller, 'actionInjection'); + $params = ['arg' => 'test', 'second' => 1]; + + $args = $this->controller->bindActionParams($injectionAction, $params); + $this->assertSame('test', $args[0]); + $this->assertSame(1, $args[1]); + } } diff --git a/tests/framework/web/FakePhp80Controller.php b/tests/framework/web/FakePhp80Controller.php new file mode 100644 index 00000000000..8f8fc20fe7b --- /dev/null +++ b/tests/framework/web/FakePhp80Controller.php @@ -0,0 +1,20 @@ +expectException('yii\base\InvalidRouteException'); + + $this->response->redirect(urldecode('http://test-domain.com/gql.json;%0aa.html')); + } + /** * @dataProvider dataProviderAjaxRedirectInternetExplorer11 */ @@ -370,21 +380,116 @@ public function testSendFileWithInvalidCharactersInFileName() ); } - public function testSameSiteCookie() + /** + * @dataProvider cookiesTestProvider + */ + public function testCookies($cookieConfig, $expected) { $response = new Response(); - $response->cookies->add(new Cookie([ - 'name' => 'test', - 'value' => 'testValue', - 'sameSite' => Cookie::SAME_SITE_STRICT, - ])); + $response->cookies->add(new Cookie(array_merge( + [ + 'name' => 'test', + 'value' => 'testValue', + ], + $cookieConfig + ))); ob_start(); $response->send(); $content = ob_get_clean(); - // Only way to test is that it doesn't create any errors - $this->assertEquals('', $content); + $cookies = $this->parseHeaderCookies(); + if ($cookies === false) { + // Unable to resolve cookies, only way to test is that it doesn't create any errors + $this->assertEquals('', $content); + } else { + $testCookie = $cookies['test']; + $actual = array_intersect_key($testCookie, $expected); + ksort($actual); + ksort($expected); + $this->assertEquals($expected, $actual); + } + } + + public function cookiesTestProvider() + { + $expireInt = time() + 3600; + $expireString = date('D, d-M-Y H:i:s', $expireInt) . ' GMT'; + + $testCases = [ + 'same-site' => [ + ['sameSite' => Cookie::SAME_SITE_STRICT], + ['samesite' => Cookie::SAME_SITE_STRICT], + ], + 'expire-as-int' => [ + ['expire' => $expireInt], + ['expires' => $expireString], + ], + 'expire-as-string' => [ + ['expire' => $expireString], + ['expires' => $expireString], + ], + ]; + + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { + $testCases = array_merge($testCases, [ + 'expire-as-date_time' => [ + ['expire' => new \DateTime('@' . $expireInt)], + ['expires' => $expireString], + ], + 'expire-as-date_time_immutable' => [ + ['expire' => new \DateTimeImmutable('@' . $expireInt)], + ['expires' => $expireString], + ], + ]); + } + + return $testCases; + } + + /** + * Tries to parse cookies set in the response headers. + * When running PHP on the CLI headers are not available (the `headers_list()` function always returns an + * empty array). If possible use xDebug: http://xdebug.org/docs/all_functions#xdebug_get_headers + * @param $name + * @return array|false + */ + protected function parseHeaderCookies() { + + if (!function_exists('xdebug_get_headers')) { + return false; + } + + $cookies = []; + foreach(xdebug_get_headers() as $header) { + if (strpos($header, 'Set-Cookie: ') !== 0) { + continue; + } + + $name = null; + $params = []; + $pairs = explode(';', substr($header, 12)); + foreach ($pairs as $index => $pair) { + $pair = trim($pair); + if (strpos($pair, '=') === false) { + $params[strtolower($pair)] = true; + } else { + list($paramName, $paramValue) = explode('=', $pair, 2); + if ($index === 0) { + $name = $paramName; + $params['value'] = urldecode($paramValue); + } else { + $params[strtolower($paramName)] = urldecode($paramValue); + } + } + } + if ($name === null) { + throw new \Exception('Could not determine cookie name for header "' . $header . '".'); + } + $cookies[$name] = $params; + } + + return $cookies; } /** diff --git a/tests/framework/widgets/LinkSorterTest.php b/tests/framework/widgets/LinkSorterTest.php index 13d4311da73..49da1fce71e 100644 --- a/tests/framework/widgets/LinkSorterTest.php +++ b/tests/framework/widgets/LinkSorterTest.php @@ -8,7 +8,6 @@ namespace yiiunit\framework\widgets; use yii\data\ActiveDataProvider; -use yii\widgets\Breadcrumbs; use yii\widgets\LinkSorter; use yii\widgets\ListView; use yiiunit\data\ar\ActiveRecord; @@ -28,7 +27,6 @@ protected function setUp() parent::setUp(); ActiveRecord::$db = $this->getConnection(); $this->mockWebApplication(); - $this->breadcrumbs = new Breadcrumbs(); } public function testLabelsSimple()