diff --git a/.ecrc b/.ecrc index 7d1afee..7ece0b3 100644 --- a/.ecrc +++ b/.ecrc @@ -6,6 +6,7 @@ "NoColor": false, "Exclude": [ ".Build", + ".cache", ".git", ".DS_Store", ".xsd" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 37d8218..ded3d47 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,106 +5,91 @@ on: [push, pull_request] jobs: lint: name: Source code linting - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 + strategy: + max-parallel: 2 + matrix: + php-versions: ['8.2'] + typo3-versions: ['13.2'] steps: - - - uses: actions/checkout@v2 + - name: "Checkout" + uses: actions/checkout@v4 - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -s composerValidate + - name: Validate composer.json + run: Build/Scripts/runTests.sh -s composer validate - - - name: Cache composer dependencies - uses: actions/cache@v1 + - name: Cache dependencies installed with composer + uses: actions/cache@v4 with: - path: ~/.composer/cache - key: composer + key: "php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}" + path: .cache/composer + restore-keys: "php${{ matrix.php-version }}-composer-\n" - - - name: Install composer dependencies - run: Build/Scripts/runTests.sh -s composerInstall + - name: Install composer dependencies + run: Build/Scripts/runTests.sh -s composer install - - - name: PHP Linting + - name: PHP Linting run: Build/Scripts/runTests.sh -s lintPhp - - - name: Editorconfig Linting + - name: Editorconfig Linting run: Build/Scripts/runTests.sh -s lintEditorconfig unittest: - runs-on: ubuntu-latest - + runs-on: ubuntu-24.04 strategy: max-parallel: 4 matrix: - php-versions: [7.4, '8.0', 8.1, 8.2] - typo3-versions: [11, 12] - exclude: - - - php-versions: 7.4 - typo3-versions: 12 - - name: Unit (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) + php-versions: ['8.2'] + typo3-versions: ['13.2'] + + name: Unit Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) steps: - - - uses: actions/checkout@v2 + - name: "Checkout" + uses: actions/checkout@v4 - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerValidate + - name: Validate composer.json + run: Build/Scripts/runTests.sh -s composer validate - - - name: Cache composer dependencies - uses: actions/cache@v1 + - name: Cache dependencies installed with composer + uses: actions/cache@v4 with: - path: ~/.composer/cache - key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} - - - - name: Install composer dependencies - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerInstall - - - - name: Automated Unit Testing - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s unit - - - functionaltest: - runs-on: ubuntu-latest - - strategy: - max-parallel: 4 - matrix: - php-versions: [ 7.4, '8.0', 8.1, 8.2 ] - typo3-versions: [ 11, 12 ] - exclude: - - - php-versions: 7.4 - typo3-versions: 12 - - name: Functional (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) - steps: - - - uses: actions/checkout@v2 - - - - name: Validate composer.json - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerValidate - - - - name: Cache composer dependencies - uses: actions/cache@v1 - with: - path: ~/.composer/cache - key: php-${{ matrix.php-versions }}-typo3-${{ matrix.typo3-versions }} - - - - name: Install composer dependencies - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerInstall - - - - name: Automated Functional Testing - run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s functional + key: "php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}" + path: .cache/composer + restore-keys: "php${{ matrix.php-version }}-composer-\n" + + - name: Install composer dependencies + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerUpdateMin + + - name: Automated Unit Testing + run: Build/Scripts/runTests.sh -s unit + + + functional: + runs-on: ubuntu-24.04 + strategy: + max-parallel: 2 + matrix: + php-versions: ['8.2'] + typo3-versions: ['13.2'] + + name: Functional Testing (PHP ${{ matrix.php-versions }}, TYPO3 ${{ matrix.typo3-versions }}) + steps: + - name: "Checkout" + uses: actions/checkout@v4 + + - name: Validate composer.json + run: Build/Scripts/runTests.sh -s composer validate + + - name: Cache dependencies installed with composer + uses: actions/cache@v4 + with: + key: "php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}" + path: .cache/composer + restore-keys: "php${{ matrix.php-version }}-composer-\n" + + - name: Install composer dependencies + run: Build/Scripts/runTests.sh -p ${{ matrix.php-versions }} -t ${{ matrix.typo3-versions }} -s composerUpdateMin + + - name: Automated Unit Testing + run: Build/Scripts/runTests.sh -s functional diff --git a/.gitignore b/.gitignore index 68f37cd..22c4fa3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .Build -composer.lock /var -/Build/Testing/.phpunit.result.cache -/Build/testing-docker/.env +.cache +/Build/phpunit/.phpunit.cache/ diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 1f32ada..b807c91 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -1,87 +1,223 @@ #!/usr/bin/env bash -# -# TYPO3 core test runner based on docker and docker-compose. -# - -# Function to write a .env file in Build/testing-docker/local -# This is read by docker-compose and vars defined here are -# used in Build/testing-docker/local/docker-compose.yml -setUpDockerComposeDotEnv() { - # Delete possibly existing local .env file if exists - [ -e .env ] && rm .env - # Set up a new .env file for docker-compose - echo "COMPOSE_PROJECT_NAME=local" >> .env - # To prevent access rights of files created by the testing, the docker image later - # runs with the same user that is currently executing the script. docker-compose can't - # use $UID directly itself since it is a shell variable and not an env variable, so - # we have to set it explicitly here. - echo "HOST_UID=`id -u`" >> .env - # Your local home directory for composer and npm caching - echo "HOST_HOME=${HOME}" >> .env - # Your local user - echo "ROOT_DIR"=${ROOT_DIR} >> .env - echo "HOST_USER=${USER}" >> .env - echo "TEST_FILE=${TEST_FILE}" >> .env - echo "PHP_XDEBUG_ON=${PHP_XDEBUG_ON}" >> .env - echo "PHP_XDEBUG_PORT=${PHP_XDEBUG_PORT}" >> .env - echo "PHP_VERSION=${PHP_VERSION}" >> .env - echo "TYPO3_VERSION=${TYPO3_VERSION}" >> .env - echo "DOCKER_PHP_IMAGE=${DOCKER_PHP_IMAGE}" >> .env - echo "EXTRA_TEST_OPTIONS=${EXTRA_TEST_OPTIONS}" >> .env - echo "SCRIPT_VERBOSE=${SCRIPT_VERBOSE}" >> .env +trap 'cleanUp;exit 2' SIGINT + +waitFor() { + local HOST=${1} + local PORT=${2} + local TESTCOMMAND=" + COUNT=0; + while ! nc -z ${HOST} ${PORT}; do + if [ \"\${COUNT}\" -gt 20 ]; then + echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; + exit 1; + fi; + sleep 1; + COUNT=\$((COUNT + 1)); + done; + " + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" + # shellcheck disable=SC2181 # Disabled because we don‘t want to move the long line between the brackets + if [[ $? -gt 0 ]]; then + kill -SIGINT -$$ + fi } -# Load help text into $HELP -read -r -d '' HELP </dev/null + done + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null +} -Usage: $0 [options] [file] +handleDbmsOptions() { + # -a, -d, -i depend on each other. Validate input combinations and set defaults. + case ${DBMS} in + mariadb) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.3" + if ! [[ ${DBMS_VERSION} =~ ^(10.3|10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + mysql) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" + if ! [[ ${DBMS_VERSION} =~ ^(5.5|5.6|5.7|8.0)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" + if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + if [ -n "${DBMS_VERSION}" ]; then + echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + *) + echo "Invalid option -d ${DBMS}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + ;; + esac +} -No arguments: Run all unit tests with PHP 7.4 +cleanCacheFiles() { + echo -n "Clean caches ... " + rm -rf \ + .Build/.cache \ + .php-cs-fixer.cache + echo "done" +} + +cleanTestFiles() { + # test related + echo -n "Clean test related files ... " + rm -rf \ + .Build/public/typo3temp/var/tests/ + echo "done" +} + +loadHelp() { + # Load help text into $HELP + read -r -d '' HELP < Specifies which test suite to run - - composerInstall: "composer install" - - composerInstallMax: "composer update", with no platform.php config. - - composerInstallMin: "composer update --prefer-lowest", with platform.php set to PHP version x.x.0. - - composerValidate: "composer validate" + - clean: clean up build, cache and testing related files and folders + - cleanCache: clean up cache related files and folders + - cleanTests: clean up test related files and folders + - composer: "composer" with all remaining arguments dispatched. + - composerNormalize: Normalizes the composer.json. + - composerUnused: Finds unused Composer packages. + - composerUpdateMax: "composer update", with no platform.php config. + - composerUpdateMin: "composer update --prefer-lowest", with platform.php set to PHP version x.x.0. + - functional: PHP functional tests - lintPhp: PHP linting - - lintEditorconfig: Editorconfig linting + - lintEditorconfig: Editor config linting - unit (default): PHP unit tests - - functional: functional tests + - unitRandom: PHP unit tests in random order, add -o to use specific seed + - update: Updates existing typo3/core-testing-*:latest container images and removes dangling local volumes. + + -a + Only with -s functional|functionalDeprecated + Specifies to use another driver, following combinations are available: + - mysql + - mysqli (default) + - pdo_mysql + - mariadb + - mysqli (default) + - pdo_mysql + + -b + Container environment: + - docker (default) + - podman - -d - Only with -s functional + -d + Only with -s functional|functionalDeprecated Specifies on which DBMS tests are performed - - mariadb (default): use mariadb - - mssql: use mssql microsoft sql server + - mariadb: use mariadb + - mysql: use MySQL - postgres: use postgres - - sqlite: use sqlite + - sqlite: (default): use sqlite - -p <7.4|8.0|8.1|8.2> + -i version + Specify a specific database version + With "-d mariadb": + - 10.3 short-term, maintained until 2023-05-25 (default) + - 10.4 short-term, maintained until 2024-06-18 + - 10.5 short-term, maintained until 2025-06-24 + - 10.6 long-term, maintained until 2026-06 + - 10.7 short-term, no longer maintained + - 10.8 short-term, maintained until 2023-05 + - 10.9 short-term, maintained until 2023-08 + - 10.10 short-term, maintained until 2023-11 + - 10.11 long-term, maintained until 2028-02 + - 11.0 development series + - 11.1 short-term development series + With "-d mysql": + - 5.5 unmaintained since 2018-12 + - 5.6 unmaintained since 2021-02 + - 5.7 maintained until 2023-10 + - 8.0 maintained until 2026-04 (default) + With "-d postgres": + - 10 unmaintained since 2022-11-10 (default) + - 11 unmaintained since 2023-11-09 + - 12 maintained until 2024-11-14 + - 13 maintained until 2025-11-13 + - 14 maintained until 2026-11-12 + - 15 maintained until 2027-11-11 + - 16 maintained until 2028-11-09 + + -t <13.2> + Only with -s composerUpdateMin|composerUpdateMax + Specifies the TYPO3 CORE Version to be used + - 13.2: (default) use TYPO3 v13 with typo3/cms-composer-installers ^5 + + -p <8.2|8.3|8.4> Specifies the PHP minor version to be used - - 7.4 (default): use PHP 7.4 - - 8.0: use PHP 8.0 - - 8.1: use PHP 8.1 - 8.2: use PHP 8.2 - - -t <11|12> - Specifies the TYPO3 version to be used - - 11 (default): use TYPO3 11 - - 12: use TYPO3 12 + - 8.3: (default) use PHP 8.3 + - 8.4: use PHP 8.4 -e "" - Only with -s functional|unit - Additional options to send to phpunit tests. - For phpunit, options starting with "--" must be added after options starting with "-". + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom + Additional options to send to phpunit (unit & functional tests). For phpunit, + options starting with "--" must be added after options starting with "-". Example -e "-v --filter canRetrieveValueWithGP" to enable verbose output AND filter tests named "canRetrieveValueWithGP" -x - Only with -s unit + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom Send information to host instance for test or system under test break points. This is especially useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port can be selected with -y @@ -90,100 +226,150 @@ Options: Send xdebug information to a different port than default 9003 if an IDE like PhpStorm is not listening on default port. - -u - Update existing typo3gmbh/phpXY:latest docker images. Maintenance call to docker pull latest - versions of the main php images. The images are updated once in a while and only the youngest - ones are supported by core testing. Use this if weird test errors occur. Also removes obsolete - image versions of typo3gmbh/phpXY. + -o + Only with -s unitRandom + Set specific random seed to replay a random run in this order again. The phpunit randomizer + outputs the used seed at the end (in gitlab core testing logs, too). Use that number to + replay the unit tests in that order. - -v - Enable verbose script output. Shows variables and docker commands. + -u + Update existing typo3/core-testing-*:latest container images and remove dangling local volumes. + New images are published once in a while and only the latest ones are supported by core testing. + Use this if weird test errors occur. Also removes obsolete image versions of typo3/core-testing-*. -h Show this help. Examples: - # Run unit tests using PHP 7.4 + # Run all core unit tests using PHP 8.3 ./Build/Scripts/runTests.sh + ./Build/Scripts/runTests.sh -s unit + + # Run all core units tests and enable xdebug (have a PhpStorm listening on port 9003!) + ./Build/Scripts/runTests.sh -x + + # Run unit tests in phpunit verbose mode with xdebug on PHP 8.1 and filter for test canRetrieveValueWithGP + ./Build/Scripts/runTests.sh -x -p 8.1 -e "-v --filter canRetrieveValueWithGP" + + # Run functional tests in phpunit with a filtered test method name in a specified file + # example will currently execute two tests, both of which start with the search term + ./Build/Scripts/runTests.sh -s functional -e "--filter deleteContent" typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php + + # Run functional tests on postgres with xdebug, php 8.1 and execute a restricted set of tests + ./Build/Scripts/runTests.sh -x -p 8.1 -s functional -d postgres typo3/sysext/core/Tests/Functional/Authentication - # Run unit tests using PHP 7.3 - ./Build/Scripts/runTests.sh -p 7.3 + # Run functional tests on postgres 11 + ./Build/Scripts/runTests.sh -s functional -d postgres -i 11 EOF +} -# Test if docker-compose exists, else exit out with error -if ! type "docker-compose" > /dev/null; then - echo "This script relies on docker and docker-compose. Please install" >&2 +# Test if at least one of the supported container binaries exists, else exit out with error +if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then + echo "This script relies on docker or podman. Please install at least one of them" >&2 exit 1 fi # Go to the directory this script is located, so everything else is relative -# to this dir, no matter from where this script is called. -THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +# to this dir, no matter from where this script is called, then go up two dirs. +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" cd "$THIS_SCRIPT_DIR" || exit 1 - -# Go to directory that contains the local docker-compose.yml file -cd ../testing-docker || exit 1 +cd ../../ || exit 1 +ROOT_DIR="${PWD}" # Option defaults -if ! command -v realpath &> /dev/null; then - echo "Consider installing realpath for properly resolving symlinks" >&2 - ROOT_DIR="${PWD}/../../" -else - ROOT_DIR=`realpath ${PWD}/../../` -fi TEST_SUITE="unit" -DBMS="mariadb" -PHP_VERSION="7.4" -TYPO3_VERSION="11" +CORE_VERSION="13.3" +DBMS="sqlite" +DBMS_VERSION="" +PHP_VERSION="8.3" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9003 EXTRA_TEST_OPTIONS="" -SCRIPT_VERBOSE=0 +PHPUNIT_RANDOM="" +DATABASE_DRIVER="" +CONTAINER_BIN="" +COMPOSER_ROOT_VERSION="3.0.x-dev" +NODE_VERSION=22 +HELP_TEXT_NPM_CI="Now running \'npm ci --silent\'." +HELP_TEXT_NPM_FAILURE="npm clean-install has failed. Please run \'${0} -s npm ci\' to explore." +CONTAINER_INTERACTIVE="-it --init" +HOST_UID=$(id -u) +HOST_PID=$(id -g) +USERSET="" +SUFFIX="$RANDOM" +NETWORK="friendsoftypo3-tea-${SUFFIX}" +CI_PARAMS="${CI_PARAMS:-}" +CONTAINER_HOST="host.docker.internal" +# shellcheck disable=SC2034 # This variable will be needed when we try to clean up the root folder +PHPSTAN_CONFIG_FILE="phpstan.neon" +IS_CORE_CI=0 -# Option parsing +# Option parsing updates above default vars # Reset in case getopts has been used previously in the shell OPTIND=1 # Array for invalid options -INVALID_OPTIONS=(); +INVALID_OPTIONS=() # Simple option parsing based on getopts (! not getopt) -while getopts ":s:d:p:e:t:xy:huv" OPT; do +while getopts "a:b:s:d:i:p:e:t:xy:o:nhu" OPT; do case ${OPT} in s) TEST_SUITE=${OPTARG} ;; + a) + DATABASE_DRIVER=${OPTARG} + ;; + b) + if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then + INVALID_OPTIONS+=("-b ${OPTARG}") + fi + CONTAINER_BIN=${OPTARG} + ;; d) DBMS=${OPTARG} ;; + i) + DBMS_VERSION=${OPTARG} + ;; p) PHP_VERSION=${OPTARG} + if ! [[ ${PHP_VERSION} =~ ^(7.4|8.0|8.1|8.2|8.3|8.4)$ ]]; then + INVALID_OPTIONS+=("-p ${OPTARG}") + fi ;; e) EXTRA_TEST_OPTIONS=${OPTARG} ;; + t) + CORE_VERSION=${OPTARG} + if ! [[ ${CORE_VERSION} =~ ^(13.2)$ ]]; then + INVALID_OPTIONS+=("-t ${OPTARG}") + fi + ;; x) PHP_XDEBUG_ON=1 ;; y) PHP_XDEBUG_PORT=${OPTARG} ;; + o) + PHPUNIT_RANDOM="--random-order-seed=${OPTARG}" + ;; + n) + CGLCHECK_DRY_RUN=1 + ;; h) + loadHelp echo "${HELP}" exit 0 ;; u) TEST_SUITE=update ;; - v) - SCRIPT_VERBOSE=1 - ;; - t) - TYPO3_VERSION=${OPTARG} - ;; \?) - INVALID_OPTIONS+=(${OPTARG}) + INVALID_OPTIONS+=("-${OPTARG}") ;; :) - INVALID_OPTIONS+=(${OPTARG}) + INVALID_OPTIONS+=("-${OPTARG}") ;; esac done @@ -192,113 +378,234 @@ done if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then echo "Invalid option(s):" >&2 for I in "${INVALID_OPTIONS[@]}"; do - echo "-"${I} >&2 + echo ${I} >&2 done echo >&2 - echo "${HELP}" >&2 + echo "call \".Build/Scripts/runTests.sh -h\" to display help and valid options" exit 1 fi -# Move "7.4" to "php74", the latter is the docker container name -DOCKER_PHP_IMAGE=`echo "php${PHP_VERSION}" | sed -e 's/\.//'` + + +# ENV var "CI" is set by gitlab-ci. Use it to force some CI details. +if [ "${CI}" == "true" ]; then + IS_CORE_CI=1 + CONTAINER_INTERACTIVE="" +fi + +# determine default container binary to use: 1. podman 2. docker +if [[ -z "${CONTAINER_BIN}" ]]; then + if type "podman" >/dev/null 2>&1; then + CONTAINER_BIN="podman" + elif type "docker" >/dev/null 2>&1; then + CONTAINER_BIN="docker" + fi +fi + +if [ "$(uname)" != "Darwin" ] && [ "${CONTAINER_BIN}" == "docker" ]; then + # Run docker jobs as current user to prevent permission issues. Not needed with podman. + USERSET="--user $HOST_UID" +fi + +if ! type ${CONTAINER_BIN} >/dev/null 2>&1; then + echo "Selected container environment \"${CONTAINER_BIN}\" not found. Please install \"${CONTAINER_BIN}\" or use -b option to select one." >&2 + exit 1 +fi + +# Create .cache dir: composer need this. +mkdir -p .cache +mkdir -p .Build/public/typo3temp/var/tests + +IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest" +IMAGE_NODE="docker.io/node:${NODE_VERSION}-alpine" +IMAGE_ALPINE="docker.io/alpine:3.8" +IMAGE_SHELLCHECK="docker.io/koalaman/shellcheck:v0.10.0" +IMAGE_DOCS="ghcr.io/typo3-documentation/render-guides:latest" +IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}" +IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}" +IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine" # Set $1 to first mass argument, this is the optional test file or test directory to execute shift $((OPTIND - 1)) -if [ -n "${1}" ]; then - TEST_FILE="Web/typo3conf/ext/media_components/${1}" +TEST_FILE=${1} + +${CONTAINER_BIN} network create ${NETWORK} >/dev/null + +if [ "${CONTAINER_BIN}" == "docker" ]; then + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" +else + # podman + CONTAINER_HOST="host.containers.internal" + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" fi -if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x +if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE="-e XDEBUG_MODE=off" + XDEBUG_CONFIG=" " +else + XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" + XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=host.docker.internal" fi # Suite execution case ${TEST_SUITE} in - composerInstall) - setUpDockerComposeDotEnv - docker-compose run composer_install + clean) + cleanCacheFiles + cleanRenderedDocumentationFiles + cleanTestFiles + ;; + cleanCache) + cleanCacheFiles + ;; + cleanTests) + cleanTestFiles + ;; + composer) + COMMAND_PARTS=(composer "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND_PARTS[@]}" + SUITE_EXIT_CODE=$? + ;; + composerUpdateMax) + COMMAND="composer config --unset platform.php; composer require --no-ansi --no-interaction --no-progress --no-install typo3/cms-core:"^${CORE_VERSION}"; composer update --no-progress --no-interaction; composer dumpautoload; composer show" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-max-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; - composerInstallMax) - setUpDockerComposeDotEnv - docker-compose run composer_install_max + composerUpdateMin) + COMMAND="composer config platform.php ${PHP_VERSION}.0; composer require --no-ansi --no-interaction --no-progress --no-install typo3/cms-core:"^${CORE_VERSION}"; composer update --prefer-lowest --no-progress --no-interaction; composer dumpautoload; composer show" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-min-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; - composerInstallMin) - setUpDockerComposeDotEnv - docker-compose run composer_install_min + composerNormalize) + COMMAND="composer ci:composer:normalize" + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND="composer ci:composer:normalize" + else + COMMAND="composer fix:composer:normalize" + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-normalize-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_DOCS} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; - composerValidate) - setUpDockerComposeDotEnv - docker-compose run composer_validate + composerUnused) + COMMAND="composer ci:composer:unused" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-unused-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; functional) - setUpDockerComposeDotEnv + [ -z "${TEST_FILE}" ] && TEST_FILE="Tests/Functional" + COMMAND=".Build/bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} ${TEST_FILE}" case ${DBMS} in mariadb) - docker-compose run functional_mariadb10 + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; - mssql) - docker-compose run functional_mssql2019latest + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; postgres) - docker-compose run functional_postgres10 + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-func-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; sqlite) - # sqlite has a tmpfs as .Build/Web/typo3temp/var/tests/functional-sqlite-dbs/ - # Since docker is executed as root (yay!), the path to this dir is owned by - # root if docker creates it. Thank you, docker. We create the path beforehand - # to avoid permission issues. - mkdir -p ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/ - docker-compose run functional_sqlite + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} SUITE_EXIT_CODE=$? ;; - *) - echo "Invalid -d option argument ${DBMS}" >&2 - echo >&2 - echo "${HELP}" >&2 - exit 1 esac - docker-compose down ;; lintPhp) - setUpDockerComposeDotEnv - docker-compose run lint_php + COMMAND="composer lint:php" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; lintEditorconfig) - setUpDockerComposeDotEnv - docker-compose run lint_editorconfig - SUITE_EXIT_CODE=$? - docker-compose down - ;; + COMMAND="composer lint:editorconfig" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; unit) - setUpDockerComposeDotEnv - docker-compose run unit + [ -z "${TEST_FILE}" ] && TEST_FILE="Tests/Unit" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} .Build/bin/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE} + SUITE_EXIT_CODE=$? + ;; + unitRandom) + [ -z "${TEST_FILE}" ] && TEST_FILE="Tests/Unit" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-random-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} .Build/bin/phpunit -c Build/phpunit/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} ${TEST_FILE} SUITE_EXIT_CODE=$? - docker-compose down ;; update) # pull typo3/core-testing-*:latest versions of those ones that exist locally - docker images typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} docker pull {} + echo "> pull ghcr.io/typo3/core-testing-*:latest versions of those ones that exist locally" + ${CONTAINER_BIN} images ghcr.io/typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} ${CONTAINER_BIN} pull {} + echo "" # remove "dangling" typo3/core-testing-* images (those tagged as ) - docker images typo3/core-testing-* --filter "dangling=true" --format "{{.ID}}" | xargs -I {} docker rmi {} + echo "> remove \"dangling\" ghcr.io/typo3/core-testing-* images (those tagged as )" + ${CONTAINER_BIN} images --filter "reference=ghcr.io/typo3/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi {} + echo "" ;; *) + loadHelp echo "Invalid -s option argument ${TEST_SUITE}" >&2 echo >&2 echo "${HELP}" >&2 + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null + else + ${CONTAINER_BIN} network rm -f ${NETWORK} >/dev/null + fi exit 1 + ;; esac +cleanUp + +# Print summary +echo "" >&2 +echo "###########################################################################" >&2 +echo "Result of ${TEST_SUITE}" >&2 +if [[ ${IS_CORE_CI} -eq 1 ]]; then + echo "Environment: CI" >&2 +else + echo "Environment: local" >&2 +fi +if [[ ${TEST_SUITE} =~ ^(npm|lintCss|lintJs)$ ]]; then + echo "NODE: ${NODE_VERSION}" >&2 +else + echo "PHP: ${PHP_VERSION}" >&2 + echo "TYPO3: ${CORE_VERSION}" >&2 +fi +echo "CONTAINER_BIN: ${CONTAINER_BIN}" +if [[ ${TEST_SUITE} =~ ^functional$ ]]; then + case "${DBMS}" in + mariadb|mysql) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 + ;; + postgres) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver pdo_pgsql" >&2 + ;; + sqlite) + echo "DBMS: ${DBMS} driver pdo_sqlite" >&2 + ;; + esac +fi +if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 +else + echo "FAILURE" >&2 +fi +echo "###########################################################################" >&2 +echo "" >&2 + +# Exit with code of test suite - This script return non-zero if the executed test failed. exit $SUITE_EXIT_CODE diff --git a/Build/Testing/FunctionalTests.xml b/Build/phpunit/FunctionalTests.xml similarity index 59% rename from Build/Testing/FunctionalTests.xml rename to Build/phpunit/FunctionalTests.xml index ded3940..d3c9085 100644 --- a/Build/Testing/FunctionalTests.xml +++ b/Build/phpunit/FunctionalTests.xml @@ -1,19 +1,18 @@ + @@ -21,8 +20,8 @@ - - - + + + diff --git a/Build/Testing/FunctionalTestsBootstrap.php b/Build/phpunit/FunctionalTestsBootstrap.php similarity index 95% rename from Build/Testing/FunctionalTestsBootstrap.php rename to Build/phpunit/FunctionalTestsBootstrap.php index 443197d..58b29f5 100644 --- a/Build/Testing/FunctionalTestsBootstrap.php +++ b/Build/phpunit/FunctionalTestsBootstrap.php @@ -12,7 +12,7 @@ * The TYPO3 project - inspiring people to share! */ -call_user_func(function () { +call_user_func(function (): void { $testbase = new \TYPO3\TestingFramework\Core\Testbase(); $testbase->defineOriginalRootPath(); $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests'); diff --git a/Build/Testing/UnitTests.xml b/Build/phpunit/UnitTests.xml similarity index 57% rename from Build/Testing/UnitTests.xml rename to Build/phpunit/UnitTests.xml index d71bb3f..a1a307a 100644 --- a/Build/Testing/UnitTests.xml +++ b/Build/phpunit/UnitTests.xml @@ -1,21 +1,19 @@ + @@ -23,8 +21,8 @@ - - - + + + diff --git a/Build/Testing/UnitTestsBootstrap.php b/Build/phpunit/UnitTestsBootstrap.php similarity index 99% rename from Build/Testing/UnitTestsBootstrap.php rename to Build/phpunit/UnitTestsBootstrap.php index b3d535e..42acad0 100644 --- a/Build/Testing/UnitTestsBootstrap.php +++ b/Build/phpunit/UnitTestsBootstrap.php @@ -12,7 +12,7 @@ * The TYPO3 project - inspiring people to share! */ -call_user_func(function () { +call_user_func(function (): void { $testbase = new \TYPO3\TestingFramework\Core\Testbase(); // These if's are for core testing (package typo3/cms) only. cms-composer-installer does diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml deleted file mode 100644 index f68c9b2..0000000 --- a/Build/testing-docker/docker-compose.yml +++ /dev/null @@ -1,329 +0,0 @@ -version: '2.3' -services: - mariadb10: - image: mariadb:10 - environment: - MYSQL_ROOT_PASSWORD: funcp - tmpfs: - - /var/lib/mysql/:rw,noexec,nosuid - - mssql2019latest: - image: typo3/core-testing-mssql2019:latest - environment: - ACCEPT_EULA: Y - SA_PASSWORD: "Test1234!" - MSSQL_PID: Developer - - postgres10: - image: postgres:10-alpine - environment: - POSTGRES_PASSWORD: funcp - POSTGRES_USER: ${HOST_USER} - tmpfs: - - /var/lib/postgresql/data:rw,noexec,nosuid - - composer_install: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer config allow-plugins.typo3/class-alias-loader true - composer config allow-plugins.typo3/cms-composer-installers true - composer require typo3/minimal "^${TYPO3_VERSION}" --prefer-dist --no-progress --no-suggest - composer install --no-progress --no-interaction; - " - composer_install_max: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer config allow-plugins.typo3/class-alias-loader true - composer config allow-plugins.typo3/cms-composer-installers true - composer req typo3/cms-core:"^${TYPO3_VERSION}" \ - typo3/cms-backend:"^${TYPO3_VERSION}" \ - typo3/cms-frontend:"^${TYPO3_VERSION}" \ - typo3/cms-extbase:"^${TYPO3_VERSION}" \ - typo3/cms-fluid:"^${TYPO3_VERSION}" \ - typo3/cms-recordlist:"^${TYPO3_VERSION}" \ - typo3/cms-install:"^${TYPO3_VERSION}" - composer config --unset platform.php; - composer update --no-progress --no-interaction; - composer dumpautoload; - " - - composer_install_min: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer config allow-plugins.typo3/class-alias-loader true - composer config allow-plugins.typo3/cms-composer-installers true - composer req typo3/cms-core:"^${TYPO3_VERSION}" \ - typo3/cms-backend:"^${TYPO3_VERSION}" \ - typo3/cms-frontend:"^${TYPO3_VERSION}" \ - typo3/cms-extbase:"^${TYPO3_VERSION}" \ - typo3/cms-fluid:"^${TYPO3_VERSION}" \ - typo3/cms-recordlist:"^${TYPO3_VERSION}" \ - typo3/cms-install:"^${TYPO3_VERSION}" - composer config platform.php ${PHP_VERSION}.0; - composer update --prefer-lowest --no-progress --no-interaction; - composer dumpautoload; - " - - composer_validate: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - composer validate; - " - - functional_mariadb10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - links: - - mariadb10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - environment: - typo3DatabaseName: func_test - typo3DatabaseUsername: root - typo3DatabasePassword: funcp - typo3DatabaseHost: mariadb10 - working_dir: ${ROOT_DIR}/.Build - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mariadb10 3306; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " - - functional_mssql2019latest: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - links: - - mssql2019latest - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - environment: - typo3DatabaseDriver: sqlsrv - typo3DatabaseName: func - typo3DatabasePassword: "Test1234!" - typo3DatabaseUsername: SA - typo3DatabasePort: 1433 - typo3DatabaseCharset: utf-8 - typo3DatabaseHost: mssql2019latest - working_dir: ${ROOT_DIR}/.Build - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mssql2019latest 1433; do - sleep 1; - done; - sleep 5; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}; - fi - " - - functional_postgres10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - links: - - postgres10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - environment: - typo3DatabaseDriver: pdo_pgsql - typo3DatabaseName: bamboo - typo3DatabaseUsername: ${HOST_USER} - typo3DatabaseHost: postgres10 - typo3DatabasePassword: funcp - working_dir: ${ROOT_DIR}/.Build - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z postgres10 5432; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; - fi - " - - functional_sqlite: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - tmpfs: - - ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid,uid=${HOST_UID} - environment: - typo3DatabaseDriver: pdo_sqlite - working_dir: ${ROOT_DIR}/.Build - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; - fi - " - - lint_php: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - .Build/bin/phpcs --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore=.Build,Tests,ext_emconf.php . - " - - lint_editorconfig: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - .Build/bin/ec -exclude .phpunit.result.cache . - " - - unit: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: ${HOST_UID} - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - - ${HOST_HOME}:${HOST_HOME} - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - working_dir: ${ROOT_DIR}/.Build - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP' - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` - XDEBUG_MODE=\"debug,develop\" \ - XDEBUG_TRIGGER=\"foo\" \ - XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ - bin/phpunit -c Web/typo3conf/ext/media_components/Build/Testing/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " diff --git a/Classes/Domain/Model/CropArea.php b/Classes/Domain/Model/CropArea.php index dee2401..7540551 100644 --- a/Classes/Domain/Model/CropArea.php +++ b/Classes/Domain/Model/CropArea.php @@ -14,7 +14,7 @@ class CropArea implements ConstructibleFromArray, ConstructibleFromFloat, ConstructibleFromInteger, ConstructibleFromString, ConstructibleFromArea { - protected $area; + protected ?Area $area = null; public function __construct(Area $area = null) { @@ -26,38 +26,38 @@ public function getArea(): Area return $this->area; } - public static function fromArea(Area $area) + public static function fromArea(Area $area): static { return new static($area); } - public static function fromArray(array $config) + public static function fromArray(array $config): ?object { return new static(Area::createFromConfiguration($config)); } - public static function fromFloat(float $ratio) + public static function fromFloat(float $ratio): static { $ratio = new Ratio('', '', $ratio); return new static(Area::createEmpty()->applyRatioRestriction($ratio)); } - public static function fromInteger(int $ratio) + public static function fromInteger(int $ratio): object { $ratio = new Ratio('', '', $ratio); return new static(Area::createEmpty()->applyRatioRestriction($ratio)); } - public static function fromString(string $ratio) + public static function fromString(string $ratio): static { $area = Area::createEmpty(); if (substr_count($ratio, ':') === 1) { - list($x, $y) = GeneralUtility::trimExplode(':', $ratio); - $area = $area->applyRatioRestriction(new Ratio('', '', (float)$x/(float)$y)); + [$x, $y] = GeneralUtility::trimExplode(':', $ratio); + $area = $area->applyRatioRestriction(new Ratio('', '', (float) $x / (float) $y)); } elseif (substr_count($ratio, '/') === 1) { - list($x, $y) = GeneralUtility::trimExplode('/', $ratio); - $area = $area->applyRatioRestriction(new Ratio('', '', (float)$x/(float)$y)); + [$x, $y] = GeneralUtility::trimExplode('/', $ratio); + $area = $area->applyRatioRestriction(new Ratio('', '', (float) $x / (float) $y)); } return new static($area); diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index 96624d7..f1d95d3 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -6,18 +6,20 @@ use Sitegeist\MediaComponents\Domain\Model\CropArea; use Sitegeist\MediaComponents\Domain\Model\SourceSet; use Sitegeist\MediaComponents\Interfaces\ConstructibleFromImage; -use SMS\FluidComponents\Domain\Model\FalImage; +use SMS\FluidComponents\Domain\Model\FalFile; use SMS\FluidComponents\Domain\Model\Image; use SMS\FluidComponents\Interfaces\ConstructibleFromArray; use SMS\FluidComponents\Interfaces\ConstructibleFromExtbaseFile; use SMS\FluidComponents\Interfaces\ConstructibleFromFileInterface; use SMS\FluidComponents\Interfaces\ConstructibleFromInteger; use SMS\FluidComponents\Interfaces\ConstructibleFromString; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use SMS\FluidComponents\Interfaces\ProcessableImage; use SMS\FluidComponents\Utility\ComponentArgumentConverter; use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Domain\Model\FileReference; -use TYPO3\CMS\Extbase\Service\ImageService; class ImageSource implements ConstructibleFromArray, @@ -27,61 +29,27 @@ class ImageSource implements ConstructibleFromInteger, ConstructibleFromString { - /** - * Original image - * - * @var Image - */ - protected $originalImage; - /** * Image with applied modifications - * - * @var Image */ - protected $image; + protected ?Image $image = null; - /** - * @var float - */ - protected $scale = 1.0; + protected float $scale = 1.0; - /** - * @var CropArea - */ - protected $crop; + protected ?CropArea $crop = null; - /** - * @var string - */ - protected $format; + protected string $format = ''; - /** - * @var string - */ - protected $media; + protected string $media = ''; - /** - * @var SourceSet - */ - protected $srcset; - - /** - * @var string - */ - protected $sizes; + protected ?SourceSet $srcset = null; - /** - * @var ImageService - */ - protected $imageService; + protected string $sizes = ''; - public function __construct(Image $originalImage = null) - { - $this->imageService = GeneralUtility::makeInstance(ImageService::class); - $this - ->setOriginalImage($originalImage) - ->setCrop(new CropArea); + public function __construct( + protected ?Image $originalImage = null + ) { + $this->setCrop(new CropArea); } public static function fromArray(array $value): ImageSource @@ -89,30 +57,41 @@ public static function fromArray(array $value): ImageSource $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); try { - $image = $argumentConverter->convertValueToType($value['originalImage'], Image::class); - } catch (\SMS\FluidComponents\Exception\InvalidArgumentException $e) { + $image = $argumentConverter->convertValueToType($value['originalImage'] ?? $value, Image::class); + } catch (\SMS\FluidComponents\Exception\InvalidArgumentException) { // TODO better error handling here: // Image is not required, but invalid combination of parameters should // be catched } $imageSource = new static($image); - $imageSource - ->setScale($value['scale'] ?? null) - ->setFormat($value['format'] ?? null) - ->setMedia($value['media'] ?? null) - ->setSizes($value['sizes'] ?? null); - - if ($value['crop'] || $value['srcset']) { - $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); - - if ($value['crop']) { - $imageSource->setCrop($argumentConverter->convertValueToType($value['crop'], CropArea::class)); - } - - if ($value['srcset']) { - $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); - } + + if (isset($value['scale'])) { + $imageSource->setScale((float) $value['scale']); + } + if (isset($value['format'])) { + $imageSource->setFormat((string) $value['format']); + } + if (isset($value['media'])) { + $imageSource->setMedia((string) $value['media']); + } + if (isset($value['sizes'])) { + $imageSource->setSizes((string) $value['sizes']); + } + + if (isset($value['crop'])) { + $imageSource->setCrop( + $argumentConverter->convertValueToType($value['crop'], CropArea::class) + ); + } elseif ($image instanceof ImageWithCropVariants) { + $cropVariant = (isset($value['cropVariant'])) + ? $image->getCropVariant($value['cropVariant']) + : $image->getDefaultCrop(); + $imageSource->setCrop(new CropArea($cropVariant)); + } + + if (isset($value['srcset'])) { + $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); } return $imageSource; @@ -148,7 +127,7 @@ public function getOriginalImage(): ?Image return $this->originalImage; } - public function setOriginalImage(?Image $image): self + public function setOriginalImage(Image $image): self { $this->originalImage = $image; return $this; @@ -159,7 +138,7 @@ public function getScale(): ?float return $this->scale; } - public function setScale(?float $scale): self + public function setScale(float $scale): self { $this->scale = $scale; return $this; @@ -170,7 +149,7 @@ public function getFormat(): ?string return $this->format; } - public function setFormat(?string $format): self + public function setFormat(string $format): self { $this->format = $format; return $this; @@ -187,6 +166,40 @@ public function setCrop(CropArea $crop): self return $this; } + public function getCroppedWidth(): ?int + { + if (!($this->originalImage instanceof ImageWithDimensions)) { + return null; + } + + if ($this->crop->getArea()->isEmpty()) { + $this->originalImage->getWidth(); + } + + if ($this->getOriginalImage() instanceof FalFile) { + return (int) round($this->crop->getArea()->makeAbsoluteBasedOnFile($this->getOriginalImage()->getFile())->getWidth()); + } + + return (int) round($this->crop->getArea()->getWidth() * $this->originalImage->getWidth()); + } + + public function getCroppedHeight(): ?int + { + if (!($this->originalImage instanceof ImageWithDimensions)) { + return null; + } + + if ($this->crop->getArea()->isEmpty()) { + $this->originalImage->getHeight(); + } + + if ($this->getOriginalImage() instanceof FalFile) { + return (int) round($this->crop->getArea()->makeAbsoluteBasedOnFile($this->getOriginalImage()->getFile())->getHeight()); + } + + return (int) round($this->crop->getArea()->getHeight() * $this->originalImage->getHeight()); + } + public function getImage(): Image { // TODO throw exception if image is not defined @@ -216,12 +229,14 @@ public function getDescription(): ?string public function getHeight(): ?int { - return $this->getImage()->getHeight(); + $image = $this->getImage(); + return ($image instanceof ImageWithDimensions) ? $image->getHeight() : null; } public function getWidth(): ?int { - return $this->getImage()->getWidth(); + $image = $this->getImage(); + return ($image instanceof ImageWithDimensions) ? $image->getWidth() : null; } public function getMedia(): ?string @@ -229,7 +244,7 @@ public function getMedia(): ?string return $this->media; } - public function setMedia(?string $media): self + public function setMedia(string $media): self { $this->media = $media; return $this; @@ -240,7 +255,7 @@ public function getSizes(): ?string return $this->sizes; } - public function setSizes(?string $sizes): self + public function setSizes(string $sizes): self { $this->sizes = $sizes; return $this; @@ -251,7 +266,7 @@ public function getSrcset(): ?SourceSet return $this->srcset; } - public function setSrcset(?SourceSet $srcset): self + public function setSrcset(SourceSet $srcset): self { $this->srcset = $srcset; return $this; @@ -259,8 +274,6 @@ public function setSrcset(?SourceSet $srcset): self /** * Use public url of image as string representation of image objects - * - * @return string */ public function __toString(): string { @@ -269,25 +282,17 @@ public function __toString(): string protected function processImage(): void { - $originalImage = $this->getOriginalImage(); - - if ($originalImage) { - $crop = null; - if ($this->getCrop()) { - $cropArea = $this->getCrop()->getArea(); - if (!$cropArea->isEmpty()) { - $crop = $cropArea->makeAbsoluteBasedOnFile($originalImage->getFile()); - } - } - - $processingInstructions = [ - 'width' => $originalImage->getWidth() * $this->getScale(), - 'height' => $originalImage->getHeight() * $this->getScale(), - 'fileExtension' => $this->getFormat(), - 'crop' => $crop - ]; - - $this->image = new FalImage($this->imageService->applyProcessingInstructions($originalImage->getFile(), $processingInstructions)); + // TODO implement runtime cache + $this->image = null; + + $original = $this->getOriginalImage(); + if ($original instanceof ProcessableImage && $original instanceof ImageWithDimensions) { + $this->image = $original->process( + (int) round($this->getCroppedWidth() * $this->getScale()), + (int) round($this->getCroppedHeight() * $this->getScale()), + $this->getFormat(), + $this->getCrop()->getArea(), + ); } } } diff --git a/Classes/Domain/Model/SourceSet.php b/Classes/Domain/Model/SourceSet.php index 9f9a60d..d277fda 100644 --- a/Classes/Domain/Model/SourceSet.php +++ b/Classes/Domain/Model/SourceSet.php @@ -9,9 +9,7 @@ class SourceSet implements ConstructibleFromArray, ConstructibleFromInteger, ConstructibleFromString { - protected $srcset = []; - - public function __construct(array $srcset = []) + public function __construct(protected array $srcset = []) { $this->setSrcset($srcset); } @@ -25,7 +23,7 @@ public function getSrcsetAndWidths(int $baseWidth): array { $useAbsoluteWidth = false; foreach ($this->srcset as $widthDescriptor) { - if (substr($widthDescriptor, -1) !== 'x') { + if (!str_ends_with((string) $widthDescriptor, 'x')) { $useAbsoluteWidth = true; break; } @@ -33,11 +31,11 @@ public function getSrcsetAndWidths(int $baseWidth): array $srcset = []; foreach ($this->srcset as $widthDescriptor) { - $srcsetMode = substr($widthDescriptor, -1); + $srcsetMode = substr((string) $widthDescriptor, -1); switch ($srcsetMode) { // Relative dimensions case 'x': - $candidateWidth = (int) ($baseWidth * (float) substr($widthDescriptor, 0, -1)); + $candidateWidth = (int) ($baseWidth * (float) substr((string) $widthDescriptor, 0, -1)); if ($useAbsoluteWidth === true) { $widthDescriptor = $candidateWidth . 'w'; } @@ -45,7 +43,7 @@ public function getSrcsetAndWidths(int $baseWidth): array // Absolute dimensions case 'w': - $candidateWidth = (int) substr($widthDescriptor, 0, -1); + $candidateWidth = (int) substr((string) $widthDescriptor, 0, -1); break; default: diff --git a/Classes/Domain/Model/Track.php b/Classes/Domain/Model/Track.php index 2af0130..79dc6f1 100644 --- a/Classes/Domain/Model/Track.php +++ b/Classes/Domain/Model/Track.php @@ -12,101 +12,55 @@ class Track extends File /** * Type of file to differentiate implementations in Fluid templates - * - * @var string */ - protected $type = 'Track'; + protected string $type = 'Track'; - /** - * Default flag for track - * - * @var int - */ - protected $default = 0; + protected int $default = 0; - /** - * Kind of track - * - * @var string - */ - protected $kind = 'subtitles'; + protected string $kind = 'subtitles'; - /** - * Label for the track - * - * @var string - */ - protected $label = ''; + protected string $label = ''; - /** - * Language of track - * - * @var string - */ - protected $srclang = 'en'; + protected string $srclang = 'en'; - /** - * @return int - */ public function getDefault(): int { return $this->default; } - /** - * @param int $default - */ public function setDefault(int $default): self { $this->default = $default; return $this; } - /** - * @return string - */ public function getKind(): string { return $this->kind; } - /** - * @param string $kind - */ public function setKind(string $kind): self { $this->kind = $kind; return $this; } - /** - * @return string - */ public function getLabel(): string { return $this->label; } - /** - * @param string $label - */ public function setLabel(string $label): self { $this->label = $label; return $this; } - /** - * @return string - */ public function getSrclang(): string { return $this->srclang; } - /** - * @param string $srclang - */ public function setSrclang(string $srclang): self { $this->srclang = $srclang; diff --git a/Classes/Interfaces/ConstructibleFromArea.php b/Classes/Interfaces/ConstructibleFromArea.php index 56d106d..e24ebc7 100644 --- a/Classes/Interfaces/ConstructibleFromArea.php +++ b/Classes/Interfaces/ConstructibleFromArea.php @@ -13,9 +13,6 @@ interface ConstructibleFromArea { /** * Creates an instance of the class based on the provided Area object - * - * @param Area $value - * @return object */ - public static function fromArea(Area $value); + public static function fromArea(Area $value): object; } diff --git a/Classes/Interfaces/ConstructibleFromFloat.php b/Classes/Interfaces/ConstructibleFromFloat.php index d05c71d..d8ee614 100644 --- a/Classes/Interfaces/ConstructibleFromFloat.php +++ b/Classes/Interfaces/ConstructibleFromFloat.php @@ -11,9 +11,6 @@ interface ConstructibleFromFloat { /** * Creates an instance of the class based on the provided float - * - * @param float $value - * @return object */ - public static function fromFloat(float $value); + public static function fromFloat(float $value): object; } diff --git a/Classes/Interfaces/ConstructibleFromImage.php b/Classes/Interfaces/ConstructibleFromImage.php index 357b2e8..5836cf3 100644 --- a/Classes/Interfaces/ConstructibleFromImage.php +++ b/Classes/Interfaces/ConstructibleFromImage.php @@ -9,9 +9,6 @@ interface ConstructibleFromImage { /** * Creates an instance of the class based on the provided Image object - * - * @param Image $value - * @return object */ - public static function fromImage(Image $value); + public static function fromImage(Image $value): object; } diff --git a/Classes/ViewHelpers/Image/CropVariantViewHelper.php b/Classes/ViewHelpers/Image/CropVariantViewHelper.php index 6c43004..583ba50 100644 --- a/Classes/ViewHelpers/Image/CropVariantViewHelper.php +++ b/Classes/ViewHelpers/Image/CropVariantViewHelper.php @@ -3,28 +3,31 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image; -use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use Sitegeist\MediaComponents\Domain\Model\CropArea; +use Sitegeist\MediaComponents\Domain\Model\ImageSource; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class CropVariantViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class CropVariantViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - - public function initializeArguments() + public function initializeArguments(): void { - $this->registerArgument('image', FileInterface::class, 'FAL file'); + $this->registerArgument('image', ImageSource::class, 'Image object'); $this->registerArgument('name', 'string', 'name of the crop variant that should be used', false, 'default'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): Area { - $arguments['image'] = $arguments['image'] ?? $renderChildrenClosure(); - $cropVariantCollection = CropVariantCollection::create((string)$arguments['image']->getProperty('crop')); - return $cropVariantCollection->getCropArea($arguments['name']); + public function render(): CropArea + { + $this->arguments['image'] ??= $this->renderChildren(); + if (!($this->arguments['image']->getOriginalImage() instanceof ImageWithDimensions)) { + return $this->arguments['image']; + } + + if ($this->arguments['image']->getOriginalImage() instanceof ImageWithCropVariants) { + return new CropArea($this->arguments['image']->getOriginalImage()->getCropVariant($this->arguments['name'])); + } else { + return new CropArea; + } } } diff --git a/Classes/ViewHelpers/Image/Modify/CropViewHelper.php b/Classes/ViewHelpers/Image/Modify/CropViewHelper.php index e085f43..f87e8a1 100644 --- a/Classes/ViewHelpers/Image/Modify/CropViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/CropViewHelper.php @@ -5,28 +5,22 @@ use Sitegeist\MediaComponents\Domain\Model\CropArea; use Sitegeist\MediaComponents\Domain\Model\ImageSource; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class CropViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class CropViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('crop', CropArea::class, 'Crop area'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): ImageSource { - $imageSource = $arguments['imageSource'] ?? $renderChildrenClosure(); - if ($arguments['crop']) { + public function render(): ImageSource + { + $imageSource = $this->arguments['imageSource'] ?? $this->renderChildren(); + if ($this->arguments['crop']) { $croppedSource = clone $imageSource; - $croppedSource->setCrop($arguments['crop']); + $croppedSource->setCrop($this->arguments['crop']); } return $croppedSource ?? $imageSource; } diff --git a/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php b/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php index 623ac3e..e03cda8 100644 --- a/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/FormatViewHelper.php @@ -4,28 +4,22 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image\Modify; use Sitegeist\MediaComponents\Domain\Model\ImageSource; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class FormatViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class FormatViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('format', 'string', 'File format that should be used'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): ImageSource { - $imageSource = $arguments['imageSource'] ?? $renderChildrenClosure(); - if ($arguments['format']) { + public function render(): ImageSource + { + $imageSource = $this->arguments['imageSource'] ?? $this->renderChildren(); + if ($this->arguments['format']) { $imageSourceWithFormat = clone $imageSource; - $imageSourceWithFormat->setFormat($arguments['format']); + $imageSourceWithFormat->setFormat($this->arguments['format']); } return $imageSourceWithFormat ?? $imageSource; } diff --git a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php index c772277..7e309c4 100644 --- a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php @@ -4,14 +4,12 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image\Modify; use Sitegeist\MediaComponents\Domain\Model\ImageSource; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class ScaleViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class ScaleViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('height', 'integer', 'Desired image height'); @@ -19,24 +17,22 @@ public function initializeArguments() $this->registerArgument('maxDimensions', 'boolean', 'If true, height and width will be considered maximums', true, false); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): ImageSource { - $imageSource = $arguments['imageSource'] ?? $renderChildrenClosure(); - - if ($arguments['height'] || $arguments['width']) { - $heightFactor = $arguments['height'] ? $arguments['height'] / $imageSource->getOriginalImage()->getHeight() : 1; - $widthFactor = $arguments['width'] ? $arguments['width'] / $imageSource->getOriginalImage()->getWidth() : 1; - $scaleFactor = ($arguments['maxDimensions']) + public function render(): ImageSource + { + $imageSource = $this->arguments['imageSource'] ?? $this->renderChildren(); + if (!($imageSource->getOriginalImage() instanceof ImageWithDimensions)) { + return $imageSource; + } + if ($this->arguments['height'] || $this->arguments['width']) { + $heightFactor = $this->arguments['height'] ? $this->arguments['height'] / $imageSource->getCroppedWidth() : 1; + $widthFactor = $this->arguments['width'] ? $this->arguments['width'] / $imageSource->getCroppedHeight() : 1; + $scaleFactor = ($this->arguments['maxDimensions']) ? min($heightFactor, $widthFactor) : max($heightFactor, $widthFactor); $scaledSource = clone $imageSource; $scaledSource->setScale($scaleFactor); } - return $scaledSource ?? $imageSource; } } diff --git a/Classes/ViewHelpers/Image/SrcsetViewHelper.php b/Classes/ViewHelpers/Image/SrcsetViewHelper.php index db00844..18d5064 100644 --- a/Classes/ViewHelpers/Image/SrcsetViewHelper.php +++ b/Classes/ViewHelpers/Image/SrcsetViewHelper.php @@ -5,47 +5,42 @@ use Sitegeist\MediaComponents\Domain\Model\ImageSource; use Sitegeist\MediaComponents\Domain\Model\SourceSet; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Service\ImageService; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -class SrcsetViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class SrcsetViewHelper extends AbstractViewHelper { - use CompileWithContentArgumentAndRenderStatic; - - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('imageSource', ImageSource::class, 'Image source (if not provided via content)'); $this->registerArgument('srcset', SourceSet::class, 'srcset definition'); $this->registerArgument('base', ImageSource::class, 'Base image for pixel density calculations'); } - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ): string { - $arguments['imageSource'] = $arguments['imageSource'] ?? $renderChildrenClosure(); - - if ($arguments['srcset'] instanceof SourceSet) { - return self::generateSrcsetString($arguments['imageSource'], $arguments['srcset'], $arguments['base']); - } else { - return $arguments['imageSource']->getPublicUrl(); + public function render(): string + { + if (!$this->arguments['srcset'] instanceof SourceSet) { + return ''; } + $this->arguments['imageSource'] ??= $this->renderChildren(); + + if (!($this->arguments['imageSource']->getOriginalImage() instanceof ImageWithDimensions)) { + return ''; + } + + return self::generateSrcsetString($this->arguments['imageSource'], $this->arguments['srcset'], $this->arguments['base']); } public static function generateSrcsetString(ImageSource $imageSource, SourceSet $srcset, ImageSource $base = null): string { $output = []; - $base = $base ?? $imageSource; + $base ??= $imageSource; $widths = $srcset->getSrcsetAndWidths($base->getWidth()); - $imageService = GeneralUtility::makeInstance(ImageService::class); $localImageSource = clone $imageSource; foreach ($widths as $widthDescriptor => $width) { - $localImageSource->setScale($width / $imageSource->getOriginalImage()->getWidth()); - $output[] = $imageService->getImageUri($localImageSource->getImage()->getFile()) . ' ' . $widthDescriptor; + $localImageSource->setScale($width / $localImageSource->getCroppedWidth()); + $output[] = (string) $localImageSource . ' ' . $widthDescriptor; } return implode(', ', $output); diff --git a/Classes/ViewHelpers/PreloadViewHelper.php b/Classes/ViewHelpers/PreloadViewHelper.php deleted file mode 100644 index d10e3ac..0000000 --- a/Classes/ViewHelpers/PreloadViewHelper.php +++ /dev/null @@ -1,12 +0,0 @@ - -* [Respect class inheritance during component argument conversion](https://github.com/sitegeist/fluid-components/pull/121) -* [Provide image dimensions in data structures](https://github.com/sitegeist/fluid-components/pull/122) + +``` -If you want to participate in the development, feel free to get in contact. You can also create issues, review pull requests or even contribute code. +### Pictures +```html + +``` + +### Video +```html + +``` + +### Audio +```html + +``` diff --git a/Resources/Private/Components/Image/Image.html b/Resources/Private/Components/Image/Image.html index 9a2bad0..933713e 100644 --- a/Resources/Private/Components/Image/Image.html +++ b/Resources/Private/Components/Image/Image.html @@ -12,6 +12,7 @@ + @@ -27,6 +28,9 @@ + + + - + @@ -22,6 +23,9 @@ + + + - 'fileadmin/test_files', + 'typo3conf/ext/media_components/Tests/Functional/Fixtures/Files' => 'fileadmin/test_files', ]; public function setUp(): void { parent::setUp(); - $this->setUpBackendUserFromFixture(1); $this->importCSVDataSet(__DIR__ . '/Fixtures/Databases/test_files.csv'); + $this->setUpBackendUser(1); } protected function cleanUpTestResult($result = '') { - return trim(preg_replace('/\\>\\s+\\<', str_replace("\n", '', (string)$result))); + return trim((string) preg_replace('/\\>\\s+\\<', str_replace("\n", '', (string) $result))); } protected function getTestView($html = '') diff --git a/Tests/Functional/AudioComponentTest.php b/Tests/Functional/AudioComponentTest.php index e9180b8..284c764 100644 --- a/Tests/Functional/AudioComponentTest.php +++ b/Tests/Functional/AudioComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class AudioComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class AudioComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function audioComponentTestProvider(): array + public static function audioComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -30,11 +33,9 @@ public function audioComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider audioComponentTestProvider - */ - public function audioComponentTest(string $expectedResult, string $input) + #[Test] + #[DataProvider('audioComponentTestProvider')] + public function audioComponentTest(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/Fixtures/Databases/test_files.csv b/Tests/Functional/Fixtures/Databases/test_files.csv index 1a926ac..d32fbdb 100644 --- a/Tests/Functional/Fixtures/Databases/test_files.csv +++ b/Tests/Functional/Fixtures/Databases/test_files.csv @@ -17,5 +17,9 @@ sys_file_metadata,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, pages,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -,uid,pid,tstamp,crdate,cruser_id,deleted,hidden,starttime,endtime,fe_group,sorting,rowDescription,editlock,sys_language_uid,l10n_parent,l10n_source,l10n_state,t3_origuid,l10n_diffsource,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,title,slug,doktype,TSconfig,is_siteroot,php_tree_stop,url,shortcut,shortcut_mode,subtitle,layout,target,media,lastUpdated,keywords,cache_timeout,cache_tags,newUntil,no_search,module,extendToSubpages,nav_title,nav_hide,content_from_pid,mount_pid,mount_pid_ol,l18n_cfg,fe_login_mode,backend_layout,backend_layout_next_level,tsconfig_includes,categories -,1,0,1634541815,1634541815,1,0,0,0,0,0,0,NULL,0,0,0,0,NULL,0,NULL,1,1,31,31,1,Home,/,1,NULL,1,0,,0,0,,0,,0,0,NULL,0,,0,0,,0,,0,0,0,0,0,0,,,NULL,0 +,uid,pid,tstamp,crdate,deleted,hidden,starttime,endtime,fe_group,sorting,rowDescription,editlock,sys_language_uid,l10n_parent,l10n_source,l10n_state,l10n_diffsource,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,title,slug,doktype,TSconfig,is_siteroot,php_tree_stop,url,shortcut,shortcut_mode,subtitle,layout,target,media,lastUpdated,keywords,cache_timeout,cache_tags,newUntil,no_search,module,extendToSubpages,nav_title,nav_hide,content_from_pid,mount_pid,mount_pid_ol,l18n_cfg,backend_layout,backend_layout_next_level,tsconfig_includes,categories +,1,0,1634541815,1634541815,0,0,0,0,0,0,NULL,0,0,0,0,NULL,NULL,1,1,31,31,1,Home,/,1,NULL,1,0,,0,0,,0,,0,0,NULL,0,,0,0,,0,,0,0,0,0,0,,,NULL,0 + +"be_users",,,,,,, +,"uid","pid","username","usergroup","deleted","admin","options" +,1,0,"admin",,0,1,0 diff --git a/Tests/Functional/ImageComponentTest.php b/Tests/Functional/ImageComponentTest.php index a4858f1..b4c1f29 100644 --- a/Tests/Functional/ImageComponentTest.php +++ b/Tests/Functional/ImageComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class ImageComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class ImageComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function imageComponentTestProvider(): array + public static function imageComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -34,11 +37,9 @@ public function imageComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider imageComponentTestProvider - */ - public function imageComponentTest(string $expectedResult, string $input) { + #[Test] + #[DataProvider('imageComponentTestProvider')] + public function imageComponentTest(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/PictureComponentTest.php b/Tests/Functional/PictureComponentTest.php index ab731fc..9808f61 100644 --- a/Tests/Functional/PictureComponentTest.php +++ b/Tests/Functional/PictureComponentTest.php @@ -2,20 +2,23 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class PictureComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class PictureComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function pictureComponentTestProvider(): array + public static function pictureComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ - '', + '', '' ], 'All data provided' => [ - 'Alt text', + 'Alt text', 'getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Functional/VideoComponentTest.php b/Tests/Functional/VideoComponentTest.php index 813b75f..80895d4 100644 --- a/Tests/Functional/VideoComponentTest.php +++ b/Tests/Functional/VideoComponentTest.php @@ -2,12 +2,15 @@ namespace Sitegeist\MediaComponents\Tests\Functional; -class VideoComponentTest extends AbstractComponentTest +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; + +class VideoComponentTest extends AbstractComponentTestCase { /** * @return string[][] */ - public function videoComponentTestProvider(): array + public static function videoComponentTestProvider(): array { return [ 'Only mandatory data provided' => [ @@ -35,11 +38,9 @@ public function videoComponentTestProvider(): array ]; } - /** - * @test - * @dataProvider videoComponentTestProvider - */ - public function videoComponentTestMinimal(string $expectedResult, string $input) { + #[Test] + #[DataProvider('videoComponentTestProvider')] + public function videoComponentTestMinimal(string $expectedResult, string $input): void { $view = $this->getTestView($input); $result = $this->cleanUpTestResult($view->render()); diff --git a/Tests/Unit/CropAreaTest.php b/Tests/Unit/CropAreaTest.php index c560ec4..991789e 100644 --- a/Tests/Unit/CropAreaTest.php +++ b/Tests/Unit/CropAreaTest.php @@ -2,15 +2,14 @@ namespace Sitegeist\MediaComponents\Tests\Unit; +use PHPUnit\Framework\Attributes\Test; use Sitegeist\MediaComponents\Domain\Model\CropArea; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; class CropAreaTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { - /** - * @test - */ - public function testFromArray() + #[Test] + public function testFromArray(): void { $cropArea = CropArea::fromArray(['x' => 0, 'y' => 0, 'width' => 1.0, 'height' => 1.0]); @@ -19,10 +18,8 @@ public function testFromArray() $this->assertEquals(['x' => 0, 'y' => 0, 'width' => 1.0, 'height' => 1.0], $cropArea->getArea()->asArray()); } - /** - * @test - */ - public function testFromFloat() + #[Test] + public function testFromFloat(): void { $cropArea = CropArea::fromFloat(2.0); @@ -31,10 +28,8 @@ public function testFromFloat() $this->assertEquals(['x' => 0, 'y' => 0.25, 'width' => 1.0, 'height' => 0.5], $cropArea->getArea()->asArray()); } - /** - * @test - */ - public function testFromInteger() + #[Test] + public function testFromInteger(): void { $cropArea = CropArea::fromInteger(4); @@ -43,10 +38,8 @@ public function testFromInteger() $this->assertEquals(['x' => 0, 'y' => 0.375, 'width' => 1.0, 'height' => 0.25], $cropArea->getArea()->asArray()); } - /** - * @test - */ - public function testFromString() + #[Test] + public function testFromString(): void { $cropArea = CropArea::fromString(''); diff --git a/Tests/Unit/SourceSetTest.php b/Tests/Unit/SourceSetTest.php index dccf63c..d1dd7d8 100644 --- a/Tests/Unit/SourceSetTest.php +++ b/Tests/Unit/SourceSetTest.php @@ -2,14 +2,13 @@ namespace Sitegeist\MediaComponents\Tests\Unit; +use PHPUnit\Framework\Attributes\Test; use Sitegeist\MediaComponents\Domain\Model\SourceSet; class SourceSetTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { - /** - * @test - */ - public function testFromArray() + #[Test] + public function testFromArray(): void { $sourceSet = SourceSet::fromArray(['10w', '20w', '30w']); @@ -17,10 +16,8 @@ public function testFromArray() $this->assertEquals(['10w', '20w', '30w'], $sourceSet->getSrcset()); } - /** - * @test - */ - public function testFromInteger() + #[Test] + public function testFromInteger(): void { $sourceSet = SourceSet::fromInteger(200); @@ -28,10 +25,8 @@ public function testFromInteger() $this->assertEquals([200], $sourceSet->getSrcset()); } - /** - * @test - */ - public function testFromString() + #[Test] + public function testFromString(): void { $sourceSet = SourceSet::fromString('10w,20w,30w'); diff --git a/composer.json b/composer.json index c742ae1..7648720 100644 --- a/composer.json +++ b/composer.json @@ -6,23 +6,31 @@ "license": ["GPL-2.0-or-later"], "keywords": ["typo3", "typo3-extension", "fluid", "typo3-fluid", "components", "html", "template", "media", "image", "responsive images", "video", "audio"], "authors": [ + { + "name": "Benjamin Tammling", + "email": "benjamin.tammling@sitegeist.de" + }, + { + "name": "Ulrich Mathes", + "email": "mathes@sitegeist.de" + }, { "name": "Simon Praetorius", - "email": "praetorius@sitegeist.de" + "email": "moin@praetorius.me" } ], "support": { "issues": "https://github.com/sitegeist/media-components/issues" }, "require": { - "php": ">=7.4.0 <=8.2.99", - "sitegeist/fluid-components": "^3.3.0", - "typo3fluid/fluid": "^2", - "sitegeist/fluid-tagbuilder": "^1", - "typo3/cms-core": "^11.4 || ^12.0" + "php": "^8.2", + "sitegeist/fluid-components": "^3.8.1", + "typo3fluid/fluid": "^4.0", + "sitegeist/fluid-tagbuilder": "^1 || dev-main", + "typo3/cms-core": "^13.3 || dev-main" }, "require-dev": { - "typo3/testing-framework": "^6.0 || 7.x-dev", + "typo3/testing-framework": "^8.2", "squizlabs/php_codesniffer": "^3.0", "editorconfig-checker/editorconfig-checker": "^10.0" }, @@ -39,6 +47,7 @@ "config": { "vendor-dir": ".Build/vendor", "bin-dir": ".Build/bin", + "lock": false, "allow-plugins": { "typo3/cms-composer-installers": true, "typo3/class-alias-loader": true @@ -46,27 +55,26 @@ }, "extra": { "typo3/cms": { - "cms-package-dir": "{$vendor-dir}/typo3/cms", - "app-dir": ".Build", - "web-dir": ".Build/Web", - "extension-key": "media_components" + "extension-key": "media_components", + "web-dir": ".Build/public" } }, "scripts": { - "post-autoload-dump": [ - "TYPO3\\TestingFramework\\Composer\\ExtensionTestEnvironment::prepare" - ], "lint": [ "@lint:php", "@lint:editorconfig" ], - "lint:php": "Build/Scripts/runTests.sh -s lintPhp", - "lint:editorconfig": "Build/Scripts/runTests.sh -s lintEditorconfig", + "lint:php": "phpcs --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore=.Build,.cache,Tests,ext_emconf.php .", + "lint:editorconfig": "ec -exclude .Build .", "test": [ "@test:unit", "@test:functional" ], "test:unit": "Build/Scripts/runTests.sh", - "test:functional": "Build/Scripts/runTests.sh -s functional" + "test:functional": "Build/Scripts/runTests.sh -s functional", + "prepare-release": [ + "sed -i'' -e \"s/'version' => ''/'version' => '$(echo ${GITHUB_REF#refs/tags/} | sed 's/v//')'/\" ext_emconf.php", + "rm -r .github .ecrc .editorconfig .gitattributes Build Tests" + ] } } diff --git a/ext_emconf.php b/ext_emconf.php index 585fcb4..380e1a6 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -3,17 +3,14 @@ 'title' => 'Media Components', 'description' => 'Ready-to-use fluid components for embedding different media files', 'category' => 'fe', - 'author' => 'Simon Praetorius', - 'author_email' => 'praetorius@sitegeist.de', - 'author_company' => 'sitegeist media solutions GmbH', + 'author' => 'Benjamin Tammling, Ulrich Mathes, Simon Praetorius', + 'author_email' => 'benjamin.tammling@sitegeist.de, mathes@sitegeist.de, moin@praetorius.me', 'state' => 'stable', - 'uploadfolder' => false, - 'clearCacheOnLoad' => false, - 'version' => '1.0.0-dev', + 'version' => '', 'constraints' => [ 'depends' => [ - 'typo3' => '9.5.0-11.9.99', - 'php' => '7.2.0-7.9.99' + 'typo3' => '13.2.0-13.2.99', + 'php' => '8.2.0-8.3.99' ], 'conflicts' => [ ], diff --git a/ext_localconf.php b/ext_localconf.php index 2afc2b6..1abaec3 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,30 +1,43 @@ addConversionInterface( - 'float', - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromFloat::class, - 'fromFloat' - ); - $componentArgumentConverter->addConversionInterface( - 'double', - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromFloat::class, - 'fromFloat' - ); - $componentArgumentConverter->addConversionInterface( - \TYPO3\CMS\Core\Imaging\ImageManipulation\Area::class, - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromArea::class, - 'fromArea' - ); - $componentArgumentConverter->addConversionInterface( - \SMS\FluidComponents\Domain\Model\Image::class, - \Sitegeist\MediaComponents\Interfaces\ConstructibleFromImage::class, - 'fromImage' - ); -}); +defined('TYPO3') or die(); + +$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['fc'] ??= []; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['fc'][] = 'Sitegeist\\MediaComponents\\Components'; + +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['namespaces']['Sitegeist\\MediaComponents\\Components'] = + ExtensionManagementUtility::extPath('media_components', 'Resources/Private/Components'); + +$componentArgumentConverter = GeneralUtility::makeInstance( + ComponentArgumentConverter::class +); +$componentArgumentConverter->addConversionInterface( + 'float', + ConstructibleFromFloat::class, + 'fromFloat' +); +$componentArgumentConverter->addConversionInterface( + 'double', + ConstructibleFromFloat::class, + 'fromFloat' +); +$componentArgumentConverter->addConversionInterface( + Area::class, + ConstructibleFromArea::class, + 'fromArea' +); +$componentArgumentConverter->addConversionInterface( + Image::class, + ConstructibleFromImage::class, + 'fromImage' +);