├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── docker-image.yml ├── docker-compose.apache.yml ├── php ├── image-files │ ├── nginx │ │ └── etc │ │ │ ├── supervisor │ │ │ ├── supervisord.conf │ │ │ └── conf.d │ │ │ │ ├── fpm.conf │ │ │ │ └── nginx.conf │ │ │ └── nginx │ │ │ ├── conf.d │ │ │ └── default.conf │ │ │ ├── ssl │ │ │ ├── nginx-selfsigned.crt │ │ │ └── nginx-selfsigned.key │ │ │ └── nginx.conf │ ├── dev │ │ └── usr │ │ │ └── local │ │ │ ├── bin │ │ │ ├── composer │ │ │ └── docker-php-entrypoint │ │ │ └── etc │ │ │ └── php │ │ │ └── conf.d │ │ │ └── xdebug.ini │ └── min │ │ ├── usr │ │ └── local │ │ │ ├── bin │ │ │ └── docker-php-entrypoint │ │ │ └── etc │ │ │ └── php │ │ │ └── conf.d │ │ │ └── base.ini │ │ ├── root │ │ └── .bashrc │ │ └── etc │ │ └── apache2 │ │ └── sites-available │ │ └── 000-default.conf └── Dockerfile-debian ├── docker-compose.fpm-nginx.yml ├── docs ├── install-application-template.md ├── test-framework.md ├── gitlab-build.md └── install-extensions.md ├── .env-dist ├── docker-compose.yml ├── tests ├── requirements │ ├── views │ │ ├── console │ │ │ └── index.php │ │ └── web │ │ │ ├── index.php │ │ │ └── css.php │ ├── requirements.php │ └── YiiRequirementChecker.php └── requirements.php ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | *.swp 3 | _host-volumes 4 | 5 | \.idea/ 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [schmunk42] 4 | open_collective: yiisoft 5 | -------------------------------------------------------------------------------- /docker-compose.apache.yml: -------------------------------------------------------------------------------- 1 | version: '2.2' 2 | services: 3 | 4 | php-min: 5 | ports: 6 | - '8101:80' 7 | 8 | php-dev: 9 | ports: 10 | - '8102:80' 11 | -------------------------------------------------------------------------------- /php/image-files/nginx/etc/supervisor/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | logfile = /dev/null 3 | loglevel = info 4 | pidfile = /var/run/supervisord.pid 5 | nodaemon = true 6 | 7 | [include] 8 | files = /etc/supervisor/conf.d/*.conf 9 | 10 | -------------------------------------------------------------------------------- /php/image-files/nginx/etc/supervisor/conf.d/fpm.conf: -------------------------------------------------------------------------------- 1 | [program:php-fpm] 2 | command = php-fpm 3 | autostart=%(ENV_SUPERVISOR_START_FPM)s 4 | autorestart = true 5 | priority=40 6 | stdout_logfile = /dev/stdout 7 | stdout_logfile_maxbytes = 0 8 | stderr_logfile = /dev/stderr 9 | stderr_logfile_maxbytes = 0 10 | -------------------------------------------------------------------------------- /php/image-files/dev/usr/local/bin/composer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ -n "${GITHUB_API_TOKEN}" ] 6 | then 7 | composer.phar config -g github-oauth.github.com ${GITHUB_API_TOKEN} 8 | fi 9 | 10 | if [ -n "${GITLAB_ACCESS_TOKEN}" ] 11 | then 12 | composer.phar config -g gitlab-token.gitlab.com ${GITLAB_ACCESS_TOKEN} 13 | fi 14 | 15 | composer.phar "$@" -------------------------------------------------------------------------------- /php/image-files/nginx/etc/supervisor/conf.d/nginx.conf: -------------------------------------------------------------------------------- 1 | [program:nginx] 2 | command=/usr/sbin/nginx -g 'daemon off;' 3 | autostart=%(ENV_SUPERVISOR_START_NGINX)s 4 | autorestart=true 5 | priority=50 6 | stdout_events_enabled=true 7 | stderr_events_enabled=true 8 | stdout_logfile = /dev/stdout 9 | stdout_logfile_maxbytes = 0 10 | stderr_logfile = /dev/stderr 11 | stderr_logfile_maxbytes = 0 12 | -------------------------------------------------------------------------------- /php/image-files/dev/usr/local/etc/php/conf.d/xdebug.ini: -------------------------------------------------------------------------------- 1 | ;zend_extension=xdebug.so 2 | xdebug.mode=debug 3 | xdebug.start_with_request=yes 4 | ;;; if you are on macOS, use host.docker.internal to identify the host machine, due to a network limitation on mac (https://docs.docker.com/docker-for-mac/networking/#port-mapping) 5 | ;;; otherwise find host ip 6 | xdebug.client_host=host.docker.internal 7 | ;;; avoid standard port (9000) conflicts using 9005 port 8 | xdebug.client_port=9005 9 | xdebug.idekey=PHPStorm 10 | -------------------------------------------------------------------------------- /docker-compose.fpm-nginx.yml: -------------------------------------------------------------------------------- 1 | version: '2.2' 2 | services: 3 | 4 | php-nginx-min: 5 | extends: 6 | file: docker-compose.yml 7 | service: php-min 8 | build: 9 | target: nginx-min 10 | image: ${PHP_IMAGE_NAME}:${PHP_BASE_IMAGE_VERSION}${PHP_IMAGE_VERSION_SUFFIX}-nginx-min 11 | ports: 12 | - '8201:80' 13 | 14 | php-nginx-dev: 15 | extends: 16 | file: docker-compose.yml 17 | service: php-dev 18 | build: 19 | target: nginx-dev 20 | image: ${PHP_IMAGE_NAME}:${PHP_BASE_IMAGE_VERSION}${PHP_IMAGE_VERSION_SUFFIX}-nginx 21 | ports: 22 | - '8202:80' 23 | 24 | -------------------------------------------------------------------------------- /docs/install-application-template.md: -------------------------------------------------------------------------------- 1 | # Install an application template 2 | 3 | Enter the `php` container 4 | 5 | composer create-project yiisoft/yii2-app-basic /app 6 | 7 | Open in your browser 8 | 9 | http://127.0.0.1:8101 10 | 11 | When running Apache you need a configuration like the following to use `enablePrettyUrl` in `.htaccess` in your public `web` folder 12 | 13 | RewriteEngine on 14 | # If a directory or a file exists, use it directly 15 | RewriteCond %{REQUEST_FILENAME} !-f 16 | RewriteCond %{REQUEST_FILENAME} !-d 17 | # Otherwise forward it to index.php 18 | RewriteRule . index.php 19 | -------------------------------------------------------------------------------- /php/image-files/min/usr/local/bin/docker-php-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Set permissions based on ENV variable (debian only) 5 | if [ -x "$(command -v usermod)" ] && [ $PHP_USER_ID -ne "0" ] ; then 6 | echo "Switching uid for 'www-data' to $PHP_USER_ID" 7 | usermod -u ${PHP_USER_ID} www-data 8 | fi 9 | 10 | # first arg is `-f` or `--some-option` 11 | if [ "${1#-}" != "$1" ]; then 12 | if [ -x "$(command -v apache2-foreground)" ]; then 13 | set -- apache2-foreground "$@" 14 | elif [ -x "$(command -v php-fpm)" ]; then 15 | set -- php-fpm "$@" 16 | else 17 | set -- php "$@" 18 | fi 19 | fi 20 | 21 | exec "$@" 22 | -------------------------------------------------------------------------------- /php/image-files/min/usr/local/etc/php/conf.d/base.ini: -------------------------------------------------------------------------------- 1 | ; Required timezone 2 | date.timezone = UTC 3 | 4 | ; General settings 5 | memory_limit=128M 6 | max_execution_time=30 7 | sys_temp_dir=/tmp 8 | upload_max_filesize=512M 9 | upload_tmp_dir=/tmp 10 | post_max_size=512M 11 | 12 | ; Security, Debug & Logs 13 | expose_php=off 14 | cgi.fix_pathinfo=0 15 | log_errors=on 16 | error_reporting=E_ALL 17 | html_errors=on 18 | xdebug.default_enable=off 19 | 20 | ; Opcache 21 | opcache.memory_consumption=128 22 | opcache.interned_strings_buffer=8 23 | opcache.max_accelerated_files=4000 24 | ;opcache.validate_timestamps=off 25 | opcache.fast_shutdown=0 26 | opcache.enable_cli=1 27 | 28 | ; PHP language options 29 | short_open_tag=0 -------------------------------------------------------------------------------- /php/image-files/min/root/.bashrc: -------------------------------------------------------------------------------- 1 | cat <<'MSG' 2 | _ _ __ _ 3 | (_|_)/ _| | | 4 | _ _ _ _| |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ 5 | | | | | | | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / 6 | | |_| | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < 7 | \__, |_|_|_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ 8 | __/ | 9 | |___/ 10 | 11 | MSG 12 | 13 | echo "PHP version: ${PHP_VERSION}" 14 | 15 | if ! shopt -oq posix; then 16 | if [ -f /usr/share/bash-completion/bash_completion ]; then 17 | . /usr/share/bash-completion/bash_completion 18 | elif [ -f /etc/bash_completion.d/yii ]; then 19 | . /etc/bash_completion.d/yii 20 | fi 21 | fi 22 | -------------------------------------------------------------------------------- /docs/test-framework.md: -------------------------------------------------------------------------------- 1 | # Running framework tests 2 | 3 | Get the source and place it into a host-volume folder for mounting it into the container. 4 | 5 | git clone https://github.com/yiisoft/yii2 _host-volumes/yii2 6 | 7 | Enter the container with 8 | 9 | docker-compose run --rm -w /yii2 php bash 10 | 11 | Go into the container and install packages 12 | 13 | > composer install 14 | 15 | Run the tests 16 | 17 | > vendor/bin/phpunit tests/framework/ --exclude db 18 | 19 | Switching to another framework version 20 | 21 | > git checkout 2.0.12 22 | 23 | 24 | ## Using a specific PHP version 25 | 26 | DOCKERFILE_FLAVOUR=debian PHP_BASE_IMAGE_VERSION=7.1.2-fpm docker-compose build 27 | DOCKERFILE_FLAVOUR=debian PHP_BASE_IMAGE_VERSION=7.1.2-fpm docker-compose run --rm php bash 28 | -------------------------------------------------------------------------------- /.env-dist: -------------------------------------------------------------------------------- 1 | ## Environment variables for docker-compose command 2 | 3 | ## Windows settings 4 | # COMPOSE_PATH_SEPARATOR=: 5 | 6 | ## Built image name 7 | PHP_IMAGE_NAME=local/yiisoftware/yii2-php 8 | PHP_IMAGE_VERSION_SUFFIX= 9 | PECL_XDEBUG_INSTALL_SUFFIX= 10 | PECL_MONGODB_INSTALL_SUFFIX= 11 | 12 | ## Apache 13 | COMPOSE_PROJECT_NAME=yii2apache 14 | COMPOSE_FILE=docker-compose.yml:docker-compose.apache.yml 15 | DOCKERFILE_FLAVOUR=debian 16 | PHP_BASE_IMAGE_VERSION=8.2-apache 17 | X_LEGACY_GD_LIB=0 18 | 19 | ## PHP-fpm & nginx 20 | #COMPOSE_PROJECT_NAME=yii2fpm 21 | #COMPOSE_FILE=docker-compose.yml:docker-compose.fpm-nginx.yml 22 | #DOCKERFILE_FLAVOUR=debian 23 | #PHP_BASE_IMAGE_VERSION=8.2-fpm 24 | #X_LEGACY_GD_LIB=1 25 | 26 | ## Runtime settings 27 | # Xdebug (calling the xdebug.remote_host on 9005 port) 28 | PHP_ENABLE_XDEBUG=0 29 | -------------------------------------------------------------------------------- /php/image-files/dev/usr/local/bin/docker-php-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Set permissions based on ENV variable (debian only) 5 | if [ -x "$(command -v usermod)" ] && [ $PHP_USER_ID -ne "0" ] ; then 6 | echo "Switching uid for 'www-data' to $PHP_USER_ID" 7 | usermod -u ${PHP_USER_ID} www-data 8 | fi 9 | 10 | # first arg is `-f` or `--some-option` 11 | if [ "${1#-}" != "$1" ]; then 12 | if [ -x "$(command -v apache2-foreground)" ]; then 13 | set -- apache2-foreground "$@" 14 | elif [ -x "$(command -v php-fpm)" ]; then 15 | set -- php-fpm "$@" 16 | else 17 | set -- php "$@" 18 | fi 19 | fi 20 | 21 | 22 | # Enable xdebug by ENV variable 23 | if [ 0 -ne "${PHP_ENABLE_XDEBUG:-0}" ] ; then 24 | docker-php-ext-enable xdebug 25 | echo "Enabled xdebug" 26 | fi 27 | 28 | exec "$@" 29 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.2' 2 | 3 | networks: 4 | default: 5 | 6 | services: 7 | 8 | php-min: 9 | build: 10 | dockerfile: Dockerfile-${DOCKERFILE_FLAVOUR} 11 | context: 'php' 12 | target: min 13 | args: 14 | - PHP_BASE_IMAGE_VERSION 15 | - X_LEGACY_GD_LIB 16 | - PECL_XDEBUG_INSTALL_SUFFIX 17 | - PECL_MONGODB_INSTALL_SUFFIX 18 | - PECL_IMAGICK_INSTALL_SUFFIX 19 | image: ${PHP_IMAGE_NAME}:${PHP_BASE_IMAGE_VERSION}${PHP_IMAGE_VERSION_SUFFIX}-min 20 | environment: 21 | - GITHUB_API_TOKEN=${GITHUB_API_TOKEN} 22 | - PHP_ENABLE_XDEBUG 23 | - TEST_RUNTIME_PATH=/tmp/runtime 24 | volumes: 25 | - ./tests:/tests:delegated 26 | # Framework testing 27 | - ./_host-volumes/yii2:/yii2:delegated 28 | # Application testing 29 | - ./_host-volumes/app:/app:delegated 30 | # Composer cache 31 | - ~/.composer-docker/cache:/root/.composer/cache:delegated 32 | networks: 33 | default: 34 | aliases: 35 | - php 36 | 37 | php-dev: 38 | extends: 39 | service: php-min 40 | build: 41 | target: dev 42 | image: ${PHP_IMAGE_NAME}:${PHP_BASE_IMAGE_VERSION}${PHP_IMAGE_VERSION_SUFFIX} 43 | -------------------------------------------------------------------------------- /php/image-files/nginx/etc/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | charset utf-8; 3 | client_max_body_size 512M; 4 | server_name app; 5 | 6 | listen 80; 7 | listen 443 ssl http2 default_server; 8 | 9 | ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt; 10 | ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key; 11 | 12 | root /app/web; 13 | index index.php index.html index.htm; 14 | 15 | location / { 16 | # Redirect everything that isn't a real file to index.php 17 | try_files $uri $uri/ /index.php?$args; 18 | } 19 | 20 | # define suffixes for static files 21 | # set caching header and avoid processing of non-existing files by Yii 22 | location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar|svg|woff|woff2|eot|ttf|otf)$ { 23 | 24 | # set expire Header 25 | expires 6M; 26 | #add_header Cache-Control "public"; 27 | add_header Last-Modified ""; 28 | 29 | # keep logs clean 30 | log_not_found off; 31 | 32 | try_files $uri =404; 33 | } 34 | 35 | location ~ \.php$ { 36 | include /etc/nginx/fastcgi_params; 37 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 38 | fastcgi_pass 127.0.0.1:9000; 39 | try_files $uri =404; 40 | } 41 | 42 | location ~ /\.(ht|svn|git) { 43 | deny all; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/requirements/views/console/index.php: -------------------------------------------------------------------------------- 1 | $requirement) { 18 | if ($requirement['condition']) { 19 | echo $requirement['name'].": OK\n"; 20 | echo "\n"; 21 | } else { 22 | echo $requirement['name'].': '.($requirement['mandatory'] ? 'FAILED!!!' : 'WARNING!!!')."\n"; 23 | echo 'Required by: '.strip_tags($requirement['by'])."\n"; 24 | $memo = strip_tags($requirement['memo']); 25 | if (!empty($memo)) { 26 | echo 'Memo: '.strip_tags($requirement['memo'])."\n"; 27 | } 28 | echo "\n"; 29 | } 30 | } 31 | 32 | $summaryString = 'Errors: '.$summary['errors'].' Warnings: '.$summary['warnings'].' Total checks: '.$summary['total']; 33 | echo str_pad('', strlen($summaryString), '-')."\n"; 34 | echo $summaryString; 35 | 36 | echo "\n\n"; 37 | -------------------------------------------------------------------------------- /php/image-files/nginx/etc/nginx/ssl/nginx-selfsigned.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID3zCCAsegAwIBAgIJAMoMt2gdriBpMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYD 3 | VQQGEwJERTELMAkGA1UECAwCQlcxEjAQBgNVBAcMCVN0dXR0Z2FydDEOMAwGA1UE 4 | CgwFZG1zdHIxNzA1BgNVBAsMLnBocC15aWkyIC0gc2VsZiBzaWduZWQgY2VydGlm 5 | aWNhdGUgZm9yIHRlc3RpbmcxDDAKBgNVBAMMA2FwcDAeFw0xNzEwMDkwOTUxMjRa 6 | Fw0xODEwMDkwOTUxMjRaMIGFMQswCQYDVQQGEwJERTELMAkGA1UECAwCQlcxEjAQ 7 | BgNVBAcMCVN0dXR0Z2FydDEOMAwGA1UECgwFZG1zdHIxNzA1BgNVBAsMLnBocC15 8 | aWkyIC0gc2VsZiBzaWduZWQgY2VydGlmaWNhdGUgZm9yIHRlc3RpbmcxDDAKBgNV 9 | BAMMA2FwcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALEel8/pjWaB 10 | n8y4wt6dK9Rd0QtOGHpYcjMbC1ALSbl7UN2GByXGtEAlgsCkH6XSt4qBoJSeuyhD 11 | zzVFir55kimk57DrBaDzQoXTF9wtoeJdpUjszwphfm8mbo+QYUTBeUQdIrZ/0Kjn 12 | 2mU85xVPPoDMMXWeCWHCob0Fhnv59II5pL5PeUbnz0mmOftM06wSI5THw5dx/cHM 13 | rt3DSenecQQK6SXRxuiOLyy1fWQ5nTTbG4IzW1gTfuD+PFXzQlDQNg94sPqDw2V9 14 | DGh/viP6bqm7C5HglnCaKvy3PAqWuSdWpxflfETKNeuyaflk3bOEoDx/AbmhgK5W 15 | 8RGw00VLYvkCAwEAAaNQME4wHQYDVR0OBBYEFK4tI2XnhxAPBSxkdUm+LJEq6Bdh 16 | MB8GA1UdIwQYMBaAFK4tI2XnhxAPBSxkdUm+LJEq6BdhMAwGA1UdEwQFMAMBAf8w 17 | DQYJKoZIhvcNAQELBQADggEBAABLbPdM2bt3n9eJUGdyvqmxT0YPZESl1OfR2CYV 18 | zyv+qBPeqxlrpuMzZwqBnRHKP4r+tinO6bIKRGuL65w1psr2gzVpe3365lZVABPT 19 | CqWiMP/6cR8rYPKRKhdvI7MVyDn2VItzVFAIc80RbMm7Gu4cTElOsiFeUmdoOEms 20 | o9qvhOuXSfDtlexzXqpxBMMjRw6PFhIfxAcGGZARWpyrNpBfUJGDJkWtu/qki8eE 21 | o5NTyQskkaT/DzmOFN3UwBA71DtSYfy2qWkHprI8kMOfEDRfAjU7p2W0lWKdZK9f 22 | 4SEY2g/oTgvmbI3Qbf6avaRRFJwLPzmMDMB7RDl0zqmptPQ= 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Yii Software LLC 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /php/image-files/min/etc/apache2/sites-available/000-default.conf: -------------------------------------------------------------------------------- 1 | 2 | Options Indexes FollowSymLinks 3 | AllowOverride All 4 | Require all granted 5 | 6 | 7 | 8 | # The ServerName directive sets the request scheme, hostname and port that 9 | # the server uses to identify itself. This is used when creating 10 | # redirection URLs. In the context of virtual hosts, the ServerName 11 | # specifies what hostname must appear in the request's Host: header to 12 | # match this virtual host. For the default virtual host (this file) this 13 | # value is not decisive as it is used as a last resort host regardless. 14 | # However, you must set it for any further virtual host explicitly. 15 | #ServerName www.example.com 16 | 17 | ServerAdmin webmaster@localhost 18 | DocumentRoot /app/web 19 | 20 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 21 | # error, crit, alert, emerg. 22 | # It is also possible to configure the loglevel for particular 23 | # modules, e.g. 24 | #LogLevel info ssl:warn 25 | 26 | ErrorLog ${APACHE_LOG_DIR}/error.log 27 | CustomLog ${APACHE_LOG_DIR}/access.log combined 28 | 29 | # For most configuration files from conf-available/, which are 30 | # enabled or disabled at a global level, it is possible to 31 | # include a line for only one particular virtual host. For example the 32 | # following line enables the CGI configuration for this host only 33 | # after it has been globally disabled with "a2disconf". 34 | #Include conf-available/serve-cgi-bin.conf 35 | 36 | -------------------------------------------------------------------------------- /php/image-files/nginx/etc/nginx/ssl/nginx-selfsigned.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxHpfP6Y1mgZ/M 3 | uMLenSvUXdELThh6WHIzGwtQC0m5e1DdhgclxrRAJYLApB+l0reKgaCUnrsoQ881 4 | RYq+eZIppOew6wWg80KF0xfcLaHiXaVI7M8KYX5vJm6PkGFEwXlEHSK2f9Co59pl 5 | POcVTz6AzDF1nglhwqG9BYZ7+fSCOaS+T3lG589Jpjn7TNOsEiOUx8OXcf3BzK7d 6 | w0np3nEECukl0cboji8stX1kOZ002xuCM1tYE37g/jxV80JQ0DYPeLD6g8NlfQxo 7 | f74j+m6puwuR4JZwmir8tzwKlrknVqcX5XxEyjXrsmn5ZN2zhKA8fwG5oYCuVvER 8 | sNNFS2L5AgMBAAECggEAdMVNoyZAMe+NsgOxfeCzD7yWYFSpcKe1Cppu1+PJRD7X 9 | rmR5e99H4O6aCHVr5ABLP0Z83t7TVxV1m5JwiWlPAqOUmKApwtX96oqkNWXkT4Ab 10 | wztUuGxUVeVJiN7QMes4Dr8Crr0PjPvmkU4Yr4TiGToF9ZwuZURTK3JkGoJNQDi1 11 | 09pNZ6VUc/kPXGByFROiRdhxMygyRQAXWgPl9vYJ04aOIaBG4MSS0Pety5Bo1V6o 12 | moXv6CzOYEq0deM84V/O14ajSNadv5W99oMErnwIYY+GJprlV1kFfuDkJnfb5MyL 13 | m/tPUv5Sw53X9pZjC+z9LhJSaFIMHfRLiFhPBEFvrQKBgQDapfI40guVlvD6bqe4 14 | P84Z2ILNS+xA3DYBE8OWHkIOI/MMOiyzu2vBlVYORma4qGzaBpNzgpqgy5YQbNLg 15 | LlGbVy3J3Chpba7VQL1JZsxq2N8vu20UpVonhxDt0/PqGnCZ+nCWtFmM98iWGrSB 16 | lC5dGBNiD8v/z+T4k3OQFsEpXwKBgQDPYHz0anCXhKNrBK4TJghsxLVOGvoQ7fDO 17 | rw/9fCg19HF2W7aP+sH75Ng+2axzAPpPb2RtoXt1milGB8Km2U+r43J7kls9xpuN 18 | wk/8CdzCvo6iawgCu0q+myuOziYuUGMnixAkEJxsVzrgseNeu8SH++0caOZMTvzL 19 | sM9RDgJapwKBgHtKlfJ6ZrPcYFgm64ZW3EXws5lGFZthUUZ8OY690yucE+IyL6DN 20 | r26CYfs8sZGwz2iFysGYxM6k3iK26pYlKDk7CIPKFDOW+L4helsYj240im2k91Zs 21 | AxFE9UPWf7TjYdlBwhkI63I1CrOxrVVgFGDEQbHLDJJcTVXp94Epg5RrAoGBALM1 22 | Y+ZoUVJvGjXyfmgn1uH//9o2b+HPbel+2aep8qnly2OIEDVOcvK2qbVusJtVvfqF 23 | fdlL0oAnsAFH1YwQgcw+KwHk0ZqKFoUj5bEd6LwWpvZu3iZWKlqbh8HKHq6pY3aZ 24 | s2InRLZPj577u0xzoXwOVrLVaCeLnuR7sD0QiTJDAoGAUewb/2gQNJPKKZCfy4mD 25 | OLXpT/IvV1YElXu8hL2OTElY/l+PCUFQLkmwNP0FXw0Gu4Q0ua05D+BloBWkEPl3 26 | WDheXYwIYiDoZuPAvFGx3WlftXyc7IusJarxY5LQ2q1tl1OwjRV2EeDm+YSTXlSg 27 | Dl+Wm2rCjL43qFrClv7Zjdc= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /docs/gitlab-build.md: -------------------------------------------------------------------------------- 1 | # Build on GitLab 2 | 3 | ## Configuration 4 | 5 | Example build configuration 6 | 7 | ```yaml 8 | image: docker:latest 9 | 10 | services: 11 | - docker:dind 12 | 13 | variables: 14 | DOCKERFILE_FLAVOUR: debian 15 | PHP_BASE_IMAGE_VERSION: fpm 16 | PHP_IMAGE_NAME: yiiframework/php 17 | TEST_YII_VERSION: 11b14ea7df25c37ae262ce6b20167fe30a407367 18 | 19 | before_script: 20 | - env 21 | - apk add --no-cache git curl docker-compose 22 | - git clone https://github.com/yiisoft/yii2 _host-volumes/yii2 23 | - git -C _host-volumes/yii2 checkout ${TEST_YII_VERSION} 24 | - cp .env-dist .env 25 | - docker info 26 | 27 | build: 28 | environment: 29 | name: ${DOCKERFILE_FLAVOUR}/php-${PHP_BASE_IMAGE_VERSION} 30 | script: 31 | - docker-compose build 32 | - docker-compose run --rm php-min php -v 33 | - docker-compose run --rm php-min php /tests/requirements.php 34 | - docker-compose run --rm php-dev php /tests/requirements.php 35 | - docker-compose run --rm -w /yii2 php-dev composer install 36 | - docker-compose run --rm -w /yii2 php-dev php -d error_reporting="E_ALL ^ E_DEPRECATED" vendor/bin/phpunit tests/framework/ --exclude db 37 | ``` 38 | ## Triggers 39 | 40 | Repo maintainers can trigger the build of a specific version via GitLab API 41 | 42 | curl -X POST \ 43 | -F token=${GITLAB_YII2_DOCKER_TOKEN} \ 44 | -F ref=master \ 45 | -F "variables[DOCKERFILE_FLAVOUR]=debian" \ 46 | -F "variables[PHP_BASE_IMAGE_VERSION]=7.1.3-apache" \ 47 | -F "variables[TEST_YII_VERSION]=2.0.11" \ 48 | https://gitlab.com/api/v4/projects/2858803/trigger/pipeline 49 | 50 | This can also be used to test pre-releases of PHP or other flavors, if there is a Dockerfile available for them. 51 | 52 | > Tokens are managed under [GitLab settings](https://gitlab.com/yiisoft/yii2-docker/settings/ci_cd). 53 | -------------------------------------------------------------------------------- /php/image-files/nginx/etc/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user www-data; 3 | worker_processes 1; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | pid /var/run/nginx.pid; 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | 21 | log_format extended '$remote_addr - $remote_user [$time_local] "$request" ' 22 | '$status $body_bytes_sent "$http_referer" ' 23 | '"$http_user_agent" "$http_x_forwarded_for" ' 24 | '$request_time $request_method'; 25 | 26 | log_format logstash_json '{ "@timestamp": "$time_iso8601", ' 27 | '"@fields": { ' 28 | '"remote_addr": "$remote_addr", ' 29 | '"remote_user": "$remote_user", ' 30 | '"body_bytes_sent": "$body_bytes_sent", ' 31 | '"request_time": "$request_time", ' 32 | '"status": "$status", ' 33 | '"request": "$request", ' 34 | '"request_method": "$request_method", ' 35 | '"http_x_forwarded_for": "$http_x_forwarded_for", ' 36 | '"http_referrer": "$http_referer", ' 37 | '"http_user_agent": "$http_user_agent" } }'; 38 | 39 | access_log /var/log/nginx/access.log extended; 40 | 41 | sendfile off; 42 | keepalive_timeout 10; 43 | fastcgi_temp_path /tmp/nginx_fastcgi_temp 1 2; 44 | client_body_temp_path /tmp/nginx_client_temp 1 2; 45 | 46 | # disable version in error messages and response header 47 | server_tokens off; 48 | 49 | gzip on; 50 | gzip_disable "msie6"; 51 | gzip_comp_level 6; 52 | gzip_min_length 1100; 53 | gzip_buffers 16 8k; 54 | gzip_proxied any; 55 | gzip_types 56 | text/plain 57 | text/css 58 | text/js 59 | text/xml 60 | text/javascript 61 | application/javascript 62 | application/x-javascript 63 | application/json 64 | application/xml 65 | application/rss+xml 66 | image/svg+xml; 67 | 68 | # don't use etag with expire 69 | etag off; 70 | 71 | include /etc/nginx/conf.d/*.conf; 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

Yii2 PHP Docker Image

3 |
4 |

5 | 6 | [![Build Status](https://github.com/yiisoft/yii2-docker/actions/workflows/docker-image.yml/badge.svg)](https://github.com/yiisoft/yii2-docker/actions/workflows/docker-image.yml) 7 | 8 | This is the repo of the official [Yii 2.0 Framework](http://www.yiiframework.com/) image on [DockerHub](https://hub.docker.com/r/yiisoftware/yii2-php/) for PHP. 9 | 10 | ## About 11 | 12 | These Docker images are built on top of the official PHP Docker image, they contain additional PHP extensions required to run Yii 2.0 framework, but no code of the framework itself. 13 | The `Dockerfile`(s) of this repository are designed to build from different PHP-versions by using *build arguments*. 14 | 15 | ### Available versions for `yiisoftware/yii2-php` 16 | 17 | The following images are built on a *weekly* basis for **arm64** and **amd64**. For regular commits on **master** we only build images for **amd64** suffixed with `-latest`/`-latest-min`. 18 | 19 | Minimal images 20 | 21 | ``` 22 | 8.3-apache-min, 8.3-fpm-min, 8.3-fpm-nginx-min 23 | 8.2-apache-min, 8.2-fpm-min, 8.2-fpm-nginx-min 24 | 8.1-apache-min, 8.1-fpm-min, 8.1-fpm-nginx-min 25 | ``` 26 | 27 | Development images 28 | 29 | ``` 30 | 8.3-apache, 8.3-fpm, 8.3-fpm-nginx 31 | 8.2-apache, 8.2-fpm, 8.2-fpm-nginx 32 | 8.1-apache, 8.1-fpm, 8.1-fpm-nginx 33 | ``` 34 | 35 | #### Deprecated or EOL versions 36 | 37 | ``` 38 | 8.0-* 39 | 7.4-* 40 | 7.3-* 41 | 7.2-* 42 | 7.1-* 43 | 7.0-* 44 | 5.6-* 45 | ``` 46 | 47 | ## Setup 48 | 49 | cp .env-dist .env 50 | 51 | Adjust the versions in `.env` if you want to build a specific version. 52 | 53 | > **Note:** Please make sure to use a matching combination of `DOCKERFILE_FLAVOUR` and `PHP_BASE_IMAGE_VERSION` 54 | 55 | ## Configuration 56 | 57 | - `GITHUB_API_TOKEN` access key for repositories on GitHub 58 | - `GITLAB_ACCESS_TOKEN` access key for repositories in GitLab 59 | - `PHP_ENABLE_XDEBUG` whether to load an enable Xdebug, defaults to `0` (false) *not available in `-min` images* 60 | - `PHP_USER_ID` (Debian only) user ID, when running commands as webserver (`www-data`), see also [#15](https://github.com/yiisoft/yii2-docker/issues/15) 61 | 62 | ## Building 63 | 64 | docker-compose build 65 | 66 | ## Testing 67 | 68 | docker-compose run --rm php php /tests/requirements.php 69 | 70 | ### Xdebug 71 | 72 | To enable Xdebug, set `PHP_ENABLE_XDEBUG=1` in .env file 73 | 74 | Xdebug is configured to call ip `xdebug.remote_host` on `9005` port (not use standard port to avoid conflicts), 75 | so you have to configure your IDE to receive connections from that ip. 76 | 77 | ## Documentation 78 | 79 | More information can be found in the [docs](/docs) folder. 80 | 81 | ## FAQ 82 | 83 | - We do not officially support Alpine images, due to numerous issues with PHP requirements and because framework tests are not passing. 84 | - Depending on the (Debian) base-image (usually PHP <7.4) you might need to set `X_LEGACY_GD_LIB=1` 85 | - test 86 | -------------------------------------------------------------------------------- /tests/requirements/views/web/index.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | Yii Application Requirement Checker 10 | renderViewFile(dirname(__FILE__) . '/css.php'); ?> 11 | 12 | 13 |
14 |
15 |

Yii Application Requirement Checker

16 |
17 |
18 |
19 |

Description

20 |

21 | This script checks if your server configuration meets the requirements 22 | for running Yii application. 23 | It checks if the server is running the right version of PHP, 24 | if appropriate PHP extensions have been loaded, and if php.ini file settings are correct. 25 |

26 |

27 | There are two kinds of requirements being checked. Mandatory requirements are those that have to be met 28 | to allow Yii to work as expected. There are also some optional requirements being checked which will 29 | show you a warning when they do not meet. You can use Yii framework without them but some specific 30 | functionality may be not available in this case. 31 |

32 | 33 |

Conclusion

34 | 0): ?> 35 |
36 | Unfortunately your server configuration does not satisfy the requirements by this application.
Please refer to the table below for detailed explanation.
37 |
38 | 0): ?> 39 |
40 | Your server configuration satisfies the minimum requirements by this application.
Please pay attention to the warnings listed below and check if your application will use the corresponding features.
41 |
42 | 43 |
44 | Congratulations! Your server configuration satisfies all requirements. 45 |
46 | 47 | 48 |

Details

49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 63 | 66 | 67 | 68 |
NameResultRequired ByMemo
55 | 56 | 58 | 59 | 61 | 62 | 64 | 65 |
69 |
70 |
71 | 75 |
76 | 77 | 78 | -------------------------------------------------------------------------------- /docs/install-extensions.md: -------------------------------------------------------------------------------- 1 | ## Installing additional extensions 2 | 3 | *Note: Alpine images are not officially supported* 4 | 5 | ### Composer asset plugin 6 | 7 | RUN composer global require "fxp/composer-asset-plugin:^1.4.2" 8 | 9 | ### mcrypt 10 | 11 | #### Debian 12 | 13 | ``` 14 | RUN apt-get update && \ 15 | apt-get -y install \ 16 | libmcrypt-dev && \ 17 | docker-php-ext-install \ 18 | mycrypt 19 | ``` 20 | 21 | 22 | ### APC 23 | 24 | *TBD* 25 | 26 | RUN pecl install apc 27 | RUN echo "extension=apcu.so" > /usr/local/etc/php/conf.d/pecl-apcu.ini 28 | 29 | ### APC (>= PHP 7) 30 | 31 | RUN pecl install apcu_bc 32 | RUN echo "extension=apcu.so" > /usr/local/etc/php/conf.d/docker-php-ext-apc1.ini 33 | RUN echo "extension=apc.so" > /usr/local/etc/php/conf.d/docker-php-ext-apc2.ini 34 | 35 | --- 36 | 37 | RUN docker-php-ext-enable \ 38 | imagick 39 | 40 | ### memcached 41 | 42 | #### Debian (PHP 7) 43 | 44 | # memcached 45 | ENV MEMCACHED_DEPS libmemcached-dev git 46 | RUN set -xe \ 47 | && apt-get update \ 48 | && apt-get install -y $MEMCACHED_DEPS \ 49 | && curl https://codeload.github.com/php-memcached-dev/php-memcached/zip/php7 -o /tmp/memcached.zip \ 50 | && mkdir -p /usr/src/php/ext \ 51 | && unzip /tmp/memcached.zip -d /usr/src/php/ext \ 52 | && docker-php-ext-configure /usr/src/php/ext/php-memcached-php7 \ 53 | --disable-memcached-sasl \ 54 | && docker-php-ext-install /usr/src/php/ext/php-memcached-php7 \ 55 | && rm -rf /usr/src/php/ext/php-memcached-php7 /tmp/memcached.zip 56 | 57 | ### Xdebug 58 | 59 | 60 | ### oci8 / pdo_oci 61 | You need to download the oracle instant client for your distro here: https://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html 62 | 63 | #### Debian 64 | Put the basic, devel and odbc rpm packages in a folder (oracle_instant_client in this example) 65 | 66 | RUN mkdir /opt/oracle 67 | COPY oracle_instant_client/ /opt/oracle 68 | 69 | ENV LD_LIBRARY_PATH=/usr/lib/oracle/18.3/client64/lib/${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} 70 | ENV ORACLE_HOME=/usr/lib/oracle/18.3/client64 71 | ENV C_INCLUDE_PATH=/usr/include/oracle/18.3/client64/ 72 | 73 | RUN apt-get update && \ 74 | apt-get install --no-install-recommends --assume-yes --quiet \ 75 | rpm \ 76 | libaio1 \ 77 | alien \ 78 | build-essential \ 79 | apt-transport-https \ 80 | libmcrypt-dev \ 81 | zlib1g-dev \ 82 | libxslt-dev \ 83 | libpng-dev && \ 84 | rm -rf /var/lib/apt/lists/* 85 | alien -i /opt/oracle/oracle-instantclient18.3-basic-18.3.0.0.0-1.x86_64.rpm && \ 86 | alien -i /opt/oracle/oracle-instantclient18.3-devel-18.3.0.0.0-1.x86_64.rpm && \ 87 | echo "/usr/lib/oracle/18.3/client64/lib/" > /etc/ld.so.conf.d/oracle.conf && \ 88 | chmod o+r /etc/ld.so.conf.d/oracle.conf && \ 89 | ln -s /usr/include/oracle/18.3/client64 $ORACLE_HOME/include && \ 90 | docker-php-ext-install pdo oci8 pdo_oci && \ 91 | rm -rf /opt/oracle 92 | 93 | 94 | ### igbinary 95 | Add faster serialization with the [igbinary](https://github.com/igbinary/igbinary) library. 96 | 97 | #### Debian 98 | 99 | RUN pecl install igbinary && \ 100 | docker-php-ext-enable igbinary 101 | 102 | 103 | ### MsSQL 104 | 105 | RUN apt-get update \ 106 | && apt-get install -y gnupg2 107 | RUN curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/mssql-release.list 108 | RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - 109 | RUN apt-get update \ 110 | && ACCEPT_EULA=Y apt-get install -y \ 111 | msodbcsql17 \ 112 | unixodbc-dev 113 | RUN pecl install sqlsrv pdo_sqlsrv \ 114 | && docker-php-ext-enable sqlsrv pdo_sqlsrv 115 | -------------------------------------------------------------------------------- /tests/requirements/views/web/css.php: -------------------------------------------------------------------------------- 1 | 64 | -------------------------------------------------------------------------------- /php/Dockerfile-debian: -------------------------------------------------------------------------------- 1 | # PHP Docker image for Yii 2.0 Framework runtime 2 | # ============================================== 3 | 4 | ARG PHP_BASE_IMAGE_VERSION 5 | FROM php:${PHP_BASE_IMAGE_VERSION} AS min 6 | 7 | # Install required system packages for PHP extensions for Yii 2.0 Framework 8 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ 9 | RUN install-php-extensions \ 10 | intl 11 | 12 | # Environment settings 13 | ENV PHP_USER_ID=33 \ 14 | PATH=/app:/app/vendor/bin:/root/.composer/vendor/bin:$PATH \ 15 | TERM=linux 16 | 17 | # Add configuration files 18 | COPY image-files/min/ / 19 | 20 | # Enable mod_rewrite for images with apache 21 | RUN if command -v a2enmod >/dev/null 2>&1; then \ 22 | a2enmod rewrite headers \ 23 | ;fi 24 | 25 | # Install Yii framework bash autocompletion 26 | RUN mkdir /etc/bash_completion.d && \ 27 | curl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/bash/yii \ 28 | -o /etc/bash_completion.d/yii 29 | 30 | # Application environment 31 | WORKDIR /app 32 | 33 | RUN chmod 755 \ 34 | /usr/local/bin/docker-php-entrypoint 35 | 36 | 37 | FROM min AS dev 38 | ARG PECL_IMAGICK_INSTALL_SUFFIX 39 | ARG PECL_MONGODB_INSTALL_SUFFIX 40 | ARG PECL_XDEBUG_INSTALL_SUFFIX 41 | 42 | # Install system packages 43 | RUN apt-get update && \ 44 | apt-get -y install \ 45 | git \ 46 | unzip \ 47 | --no-install-recommends && \ 48 | apt-get clean && \ 49 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 50 | 51 | # Install common system packages for PHP extensions recommended for Yii 2.0 Framework 52 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ 53 | RUN install-php-extensions \ 54 | imagick${PECL_IMAGICK_INSTALL_SUFFIX} \ 55 | mongodb${PECL_MONGODB_INSTALL_SUFFIX} \ 56 | xdebug${PECL_XDEBUG_INSTALL_SUFFIX} \ 57 | pdo_mysql \ 58 | pdo_pgsql \ 59 | gd \ 60 | pcntl \ 61 | soap \ 62 | zip \ 63 | bcmath \ 64 | exif \ 65 | opcache 66 | 67 | # Add configuration files 68 | COPY image-files/dev/ / 69 | 70 | # Disable xdebug by default (see PHP_ENABLE_XDEBUG) 71 | RUN rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini 72 | 73 | # Add GITHUB_API_TOKEN support for composer 74 | RUN chmod 755 \ 75 | /usr/local/bin/docker-php-entrypoint \ 76 | /usr/local/bin/composer 77 | 78 | # Install composer 79 | RUN curl -sS https://getcomposer.org/installer | php -- \ 80 | --filename=composer.phar \ 81 | --install-dir=/usr/local/bin && \ 82 | composer clear-cache 83 | 84 | # Environment settings 85 | ENV COMPOSER_ALLOW_SUPERUSER=1 \ 86 | PHP_ENABLE_XDEBUG=0 87 | 88 | 89 | FROM min AS nginx-min 90 | 91 | # Install nginx 92 | RUN apt-get update \ 93 | && apt-get install -y --force-yes \ 94 | nginx-full \ 95 | cron \ 96 | supervisor \ 97 | procps \ 98 | --no-install-recommends && \ 99 | apt-get clean && \ 100 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 101 | 102 | ENV SUPERVISOR_START_FPM=true \ 103 | SUPERVISOR_START_NGINX=true 104 | 105 | # Add configuration files 106 | COPY image-files/nginx/ / 107 | 108 | # forward request and error logs to docker log collector 109 | RUN ln -sf /dev/stdout /var/log/nginx/access.log \ 110 | && ln -sf /dev/stderr /var/log/nginx/error.log \ 111 | && ln -sf /usr/sbin/cron /usr/sbin/crond 112 | 113 | CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"] 114 | 115 | EXPOSE 80 443 116 | 117 | 118 | FROM dev AS nginx-dev 119 | 120 | # Install nginx 121 | RUN apt-get update \ 122 | && apt-get install -y --force-yes \ 123 | nginx-full \ 124 | cron \ 125 | supervisor \ 126 | procps \ 127 | --no-install-recommends && \ 128 | apt-get clean && \ 129 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 130 | 131 | ENV SUPERVISOR_START_FPM=true \ 132 | SUPERVISOR_START_NGINX=true 133 | 134 | # Add configuration files 135 | COPY image-files/nginx/ / 136 | 137 | # forward request and error logs to docker log collector 138 | RUN ln -sf /dev/stdout /var/log/nginx/access.log \ 139 | && ln -sf /dev/stderr /var/log/nginx/error.log \ 140 | && ln -sf /usr/sbin/cron /usr/sbin/crond 141 | 142 | CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"] 143 | 144 | EXPOSE 80 443 145 | -------------------------------------------------------------------------------- /tests/requirements/requirements.php: -------------------------------------------------------------------------------- 1 | 'PHP version', 11 | 'mandatory' => true, 12 | 'condition' => version_compare(PHP_VERSION, '5.4.0', '>='), 13 | 'by' => 'Yii Framework', 14 | 'memo' => 'PHP 5.4.0 or higher is required.', 15 | ), 16 | array( 17 | 'name' => 'Reflection extension', 18 | 'mandatory' => true, 19 | 'condition' => class_exists('Reflection', false), 20 | 'by' => 'Yii Framework', 21 | ), 22 | array( 23 | 'name' => 'PCRE extension', 24 | 'mandatory' => true, 25 | 'condition' => extension_loaded('pcre'), 26 | 'by' => 'Yii Framework', 27 | ), 28 | array( 29 | 'name' => 'SPL extension', 30 | 'mandatory' => true, 31 | 'condition' => extension_loaded('SPL'), 32 | 'by' => 'Yii Framework', 33 | ), 34 | array( 35 | 'name' => 'Ctype extension', 36 | 'mandatory' => true, 37 | 'condition' => extension_loaded('ctype'), 38 | 'by' => 'Yii Framework' 39 | ), 40 | array( 41 | 'name' => 'MBString extension', 42 | 'mandatory' => true, 43 | 'condition' => extension_loaded('mbstring'), 44 | 'by' => 'Multibyte string processing', 45 | 'memo' => 'Required for multibyte encoding string processing.' 46 | ), 47 | array( 48 | 'name' => 'OpenSSL extension', 49 | 'mandatory' => false, 50 | 'condition' => extension_loaded('openssl'), 51 | 'by' => 'Security Component', 52 | 'memo' => 'Required by encrypt and decrypt methods.' 53 | ), 54 | array( 55 | 'name' => 'Intl extension', 56 | 'mandatory' => false, 57 | 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2', '>='), 58 | 'by' => 'Internationalization support', 59 | 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use advanced parameters formatting 60 | in Yii::t(), non-latin languages with Inflector::slug(), 61 | IDN-feature of 62 | EmailValidator or UrlValidator or the yii\i18n\Formatter class.' 63 | ), 64 | array( 65 | 'name' => 'ICU version', 66 | 'mandatory' => false, 67 | 'condition' => defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '49', '>='), 68 | 'by' => 'Internationalization support', 69 | 'memo' => 'ICU 49.0 or higher is required when you want to use # placeholder in plural rules 70 | (for example, plural in 71 | 72 | Formatter::asRelativeTime()) in the yii\i18n\Formatter class. Your current ICU version is ' . 73 | (defined('INTL_ICU_VERSION') ? INTL_ICU_VERSION : '(ICU is missing)') . '.' 74 | ), 75 | array( 76 | 'name' => 'ICU Data version', 77 | 'mandatory' => false, 78 | 'condition' => defined('INTL_ICU_DATA_VERSION') && version_compare(INTL_ICU_DATA_VERSION, '49.1', '>='), 79 | 'by' => 'Internationalization support', 80 | 'memo' => 'ICU Data 49.1 or higher is required when you want to use # placeholder in plural rules 81 | (for example, plural in 82 | 83 | Formatter::asRelativeTime()) in the yii\i18n\Formatter class. Your current ICU Data version is ' . 84 | (defined('INTL_ICU_DATA_VERSION') ? INTL_ICU_DATA_VERSION : '(ICU Data is missing)') . '.' 85 | ), 86 | array( 87 | 'name' => 'Fileinfo extension', 88 | 'mandatory' => false, 89 | 'condition' => extension_loaded('fileinfo'), 90 | 'by' => 'File Information', 91 | 'memo' => 'Required for files upload to detect correct file mime-types.' 92 | ), 93 | array( 94 | 'name' => 'DOM extension', 95 | 'mandatory' => false, 96 | 'condition' => extension_loaded('dom'), 97 | 'by' => 'Document Object Model', 98 | 'memo' => 'Required for REST API to send XML responses via yii\web\XmlResponseFormatter.' 99 | ), 100 | ); 101 | -------------------------------------------------------------------------------- /tests/requirements.php: -------------------------------------------------------------------------------- 1 | Error'; 18 | echo '

The path to yii framework seems to be incorrect.

'; 19 | echo '

You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . '.

'; 20 | echo '

Please refer to the README on how to install Yii.

'; 21 | } 22 | 23 | require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); 24 | $requirementsChecker = new YiiRequirementChecker(); 25 | 26 | $gdMemo = $imagickMemo = 'Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required for image CAPTCHA.'; 27 | $gdOK = $imagickOK = false; 28 | 29 | if (extension_loaded('imagick')) { 30 | $imagick = new Imagick(); 31 | $imagickFormats = $imagick->queryFormats('PNG'); 32 | if (in_array('PNG', $imagickFormats)) { 33 | $imagickOK = true; 34 | } else { 35 | $imagickMemo = 'Imagick extension should be installed with PNG support in order to be used for image CAPTCHA.'; 36 | } 37 | } 38 | 39 | if (extension_loaded('gd')) { 40 | $gdInfo = gd_info(); 41 | if (!empty($gdInfo['FreeType Support'])) { 42 | $gdOK = true; 43 | } else { 44 | $gdMemo = 'GD extension should be installed with FreeType support in order to be used for image CAPTCHA.'; 45 | } 46 | } 47 | 48 | /** 49 | * Adjust requirements according to your application specifics. 50 | */ 51 | $requirements = array( 52 | // Database : 53 | array( 54 | 'name' => 'PDO extension', 55 | 'mandatory' => true, 56 | 'condition' => extension_loaded('pdo'), 57 | 'by' => 'All DB-related classes', 58 | ), 59 | array( 60 | 'name' => 'PDO SQLite extension', 61 | 'mandatory' => false, 62 | 'condition' => extension_loaded('pdo_sqlite'), 63 | 'by' => 'All DB-related classes', 64 | 'memo' => 'Required for SQLite database.', 65 | ), 66 | array( 67 | 'name' => 'PDO MySQL extension', 68 | 'mandatory' => false, 69 | 'condition' => extension_loaded('pdo_mysql'), 70 | 'by' => 'All DB-related classes', 71 | 'memo' => 'Required for MySQL database.', 72 | ), 73 | array( 74 | 'name' => 'PDO PostgreSQL extension', 75 | 'mandatory' => false, 76 | 'condition' => extension_loaded('pdo_pgsql'), 77 | 'by' => 'All DB-related classes', 78 | 'memo' => 'Required for PostgreSQL database.', 79 | ), 80 | array( 81 | 'name' => 'MongoDB extension', 82 | 'mandatory' => false, 83 | 'condition' => extension_loaded('mongodb'), 84 | 'by' => 'MongoDB', 85 | 'memo' => 'Required for MongoDB database.', 86 | ), 87 | // Cache : 88 | /*array( 89 | 'name' => 'Memcache extension', 90 | 'mandatory' => false, 91 | 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), 92 | 'by' => 'MemCache', 93 | 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : '' 94 | ),*/ 95 | // CAPTCHA: 96 | array( 97 | 'name' => 'GD PHP extension with FreeType support', 98 | 'mandatory' => false, 99 | 'condition' => $gdOK, 100 | 'by' => 'Captcha', 101 | 'memo' => $gdMemo, 102 | ), 103 | array( 104 | 'name' => 'ImageMagick PHP extension with PNG support', 105 | 'mandatory' => false, 106 | 'condition' => $imagickOK, 107 | 'by' => 'Captcha', 108 | 'memo' => $imagickMemo, 109 | ), 110 | // PHP ini : 111 | 'phpExposePhp' => array( 112 | 'name' => 'Expose PHP', 113 | 'mandatory' => false, 114 | 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), 115 | 'by' => 'Security reasons', 116 | 'memo' => '"expose_php" should be disabled at php.ini', 117 | ), 118 | 'phpAllowUrlInclude' => array( 119 | 'name' => 'PHP allow url include', 120 | 'mandatory' => false, 121 | 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), 122 | 'by' => 'Security reasons', 123 | 'memo' => '"allow_url_include" should be disabled at php.ini', 124 | ), 125 | 'phpSmtp' => array( 126 | 'name' => 'PHP mail SMTP', 127 | 'mandatory' => false, 128 | 'condition' => strlen(ini_get('SMTP')) > 0, 129 | 'by' => 'Email sending', 130 | 'memo' => 'PHP mail SMTP server required', 131 | ), 132 | 'phpPcntl' => array( 133 | 'name' => 'PHP Process Control', 134 | 'mandatory' => false, 135 | 'condition' => extension_loaded('pcntl'), 136 | 'by' => 'Queue', 137 | 'memo' => '', 138 | ), 139 | ); 140 | 141 | // OPcache check 142 | if (!version_compare(phpversion(), '5.5', '>=')) { 143 | $requirements[] = array( 144 | 'name' => 'APC extension', 145 | 'mandatory' => false, 146 | 'condition' => extension_loaded('apc'), 147 | 'by' => 'ApcCache', 148 | ); 149 | } 150 | 151 | $requirementsChecker->checkYii()->check($requirements)->render(); 152 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | pull_request: 7 | schedule: 8 | - cron: '0 3 * * 0' 9 | 10 | jobs: 11 | 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | continue-on-error: ${{ matrix.experimental }} 16 | strategy: 17 | matrix: 18 | # Example 19 | # 20 | # php-base-image-version: "x.y-type" 21 | # dockerfile-flavour: "debian" 22 | # legacy-gd-lib: "" 23 | # pecl-xdebug-suffix: "" 24 | # pecl-mongodb-suffix: "" 25 | # compose-file: "docker-compose.yml:..." 26 | include: 27 | - php-base-image-version: "8.1-fpm" 28 | dockerfile-flavour: "debian" 29 | compose-file: "docker-compose.yml,docker-compose.fpm-nginx.yml" 30 | experimental: false 31 | - php-base-image-version: "8.2-fpm" 32 | dockerfile-flavour: "debian" 33 | compose-file: "docker-compose.yml,docker-compose.fpm-nginx.yml" 34 | experimental: false 35 | - php-base-image-version: "8.3-fpm" 36 | dockerfile-flavour: "debian" 37 | compose-file: "docker-compose.yml,docker-compose.fpm-nginx.yml" 38 | pecl-imagick-suffix: "/imagick@master" 39 | experimental: false 40 | - php-base-image-version: "8.4-fpm" 41 | dockerfile-flavour: "debian" 42 | compose-file: "docker-compose.yml,docker-compose.fpm-nginx.yml" 43 | experimental: false 44 | - php-base-image-version: "8.5-fpm" 45 | dockerfile-flavour: "debian" 46 | compose-file: "docker-compose.yml,docker-compose.fpm-nginx.yml" 47 | pecl-xdebug-suffix: "-beta" 48 | pecl-imagick-suffix: "-beta" 49 | experimental: true 50 | - php-base-image-version: "8.1-apache" 51 | dockerfile-flavour: "debian" 52 | compose-file: "docker-compose.yml,docker-compose.apache.yml" 53 | experimental: false 54 | - php-base-image-version: "8.2-apache" 55 | dockerfile-flavour: "debian" 56 | compose-file: "docker-compose.yml,docker-compose.apache.yml" 57 | experimental: false 58 | - php-base-image-version: "8.3-apache" 59 | dockerfile-flavour: "debian" 60 | compose-file: "docker-compose.yml,docker-compose.apache.yml" 61 | pecl-imagick-suffix: "/imagick@master" 62 | experimental: false 63 | - php-base-image-version: "8.4-apache" 64 | dockerfile-flavour: "debian" 65 | compose-file: "docker-compose.yml,docker-compose.apache.yml" 66 | experimental: false 67 | - php-base-image-version: "8.5-apache" 68 | dockerfile-flavour: "debian" 69 | compose-file: "docker-compose.yml,docker-compose.apache.yml" 70 | pecl-xdebug-suffix: "-beta" 71 | pecl-imagick-suffix: "-beta" 72 | experimental: true 73 | fail-fast: false 74 | env: 75 | COMPOSE_FILE: ${{ matrix.compose-file }} 76 | COMPOSE_PATH_SEPARATOR: "," 77 | PHP_IMAGE_NAME: "yiisoftware/yii2-php" 78 | PHP_BASE_IMAGE_VERSION: ${{ matrix.php-base-image-version }} 79 | DOCKERFILE_FLAVOUR: ${{ matrix.dockerfile-flavour }} 80 | X_LEGACY_GD_LIB: ${{ matrix.legacy-gd-lib }} 81 | PECL_XDEBUG_INSTALL_SUFFIX: ${{ matrix.pecl-xdebug-suffix }} 82 | PECL_MONGODB_INSTALL_SUFFIX: ${{ matrix.pecl-mongodb-suffix }} 83 | PECL_IMAGICK_INSTALL_SUFFIX: ${{ matrix.pecl-imagick-suffix }} 84 | steps: 85 | - name: Checkout 86 | uses: actions/checkout@v3 87 | - name: Set up QEMU 88 | uses: docker/setup-qemu-action@v2 89 | with: 90 | platforms: 'amd64,arm64' 91 | - name: Set up Docker Buildx 92 | uses: docker/setup-buildx-action@v2 93 | - name: Set version suffix (tags) 94 | if: startsWith(github.ref, 'refs/tags/') 95 | run: echo "PHP_IMAGE_VERSION_SUFFIX=-${GITHUB_REF:10}" >> $GITHUB_ENV 96 | - name: Set version suffix (master/latest) 97 | if: (github.ref == 'refs/heads/master' && (github.event_name != 'workflow_dispatch' && github.event_name != 'schedule')) 98 | run: echo "PHP_IMAGE_VERSION_SUFFIX=-latest" >> $GITHUB_ENV 99 | - name: Install 100 | env: 101 | TEST_YII_VERSION: "5df412df2c9b4e60e7ec84008693b94ae472eae6" 102 | run: | 103 | git clone https://github.com/yiisoft/yii2 _host-volumes/yii2 104 | pushd _host-volumes/yii2 && git checkout ${TEST_YII_VERSION} && popd 105 | cp .env-dist .env 106 | - name: Print Info 107 | run: | 108 | pwd 109 | docker version 110 | docker info 111 | docker compose version 112 | - name: Build Image 113 | uses: docker/bake-action@v2 114 | with: 115 | files: ${{ matrix.compose-file }} 116 | load: true 117 | set: | 118 | *.platform=linux/amd64 119 | *.args.X_LEGACY_GD_LIB=${{ matrix.legacy-gd-lib }} 120 | - name: Test 121 | run: | 122 | docker compose run --rm php-min php -v 123 | docker compose run --rm php-min php -v | grep "Xdebug" && exit 1 124 | docker compose run --rm php-min php /tests/requirements.php 125 | docker compose run --rm php-dev php /tests/requirements.php 126 | docker compose run --rm -w /yii2 php-dev composer update --prefer-dist 127 | docker compose run --rm -w /yii2 php-dev php -d error_reporting="E_ALL ^ E_DEPRECATED" vendor/bin/phpunit tests/framework/ --exclude db 128 | docker compose run --rm -e PHP_ENABLE_XDEBUG=1 php-dev php -v | grep "Xdebug" || exit 1 129 | docker images 130 | - name: Login to Docker 131 | if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' 132 | uses: docker/login-action@v2 133 | with: 134 | username: ${{ secrets.DOCKERHUB_USER }} 135 | password: ${{ secrets.DOCKERHUB_PASS }} 136 | - name: Build and push docker image (amd64 master/latest) 137 | if: (github.ref == 'refs/heads/master' && (github.event_name != 'workflow_dispatch' && github.event_name != 'schedule')) 138 | uses: docker/bake-action@v2 139 | with: 140 | files: ${{ matrix.compose-file }} 141 | push: true 142 | set: | 143 | *.platform=linux/amd64 144 | - name: Build and push docker image (amd64,arm64) 145 | if: | 146 | startsWith(github.ref, 'refs/tags/') || 147 | (github.ref == 'refs/heads/master' && (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule')) 148 | uses: docker/bake-action@v2 149 | with: 150 | files: ${{ matrix.compose-file }} 151 | push: true 152 | set: | 153 | *.platform=linux/amd64,linux/arm64 154 | -------------------------------------------------------------------------------- /tests/requirements/YiiRequirementChecker.php: -------------------------------------------------------------------------------- 1 | 'PHP Some Extension', 25 | * 'mandatory' => true, 26 | * 'condition' => extension_loaded('some_extension'), 27 | * 'by' => 'Some application feature', 28 | * 'memo' => 'PHP extension "some_extension" required', 29 | * ), 30 | * ); 31 | * $requirementsChecker->checkYii()->check($requirements)->render(); 32 | * ``` 33 | * 34 | * If you wish to render the report with your own representation, use [[getResult()]] instead of [[render()]] 35 | * 36 | * Requirement condition could be in format "eval:PHP expression". 37 | * In this case specified PHP expression will be evaluated in the context of this class instance. 38 | * For example: 39 | * 40 | * ```php 41 | * $requirements = array( 42 | * array( 43 | * 'name' => 'Upload max file size', 44 | * 'condition' => 'eval:$this->checkUploadMaxFileSize("5M")', 45 | * ), 46 | * ); 47 | * ``` 48 | * 49 | * Note: this class definition does not match ordinary Yii style, because it should match PHP 4.3 50 | * and should not use features from newer PHP versions! 51 | * 52 | * @property array|null $result the check results, this property is for internal usage only. 53 | * 54 | * @author Paul Klimov 55 | * @since 2.0 56 | */ 57 | class YiiRequirementChecker 58 | { 59 | /** 60 | * @var Check result 61 | */ 62 | public $result; 63 | 64 | /** 65 | * Check the given requirements, collecting results into internal field. 66 | * This method can be invoked several times checking different requirement sets. 67 | * Use [[getResult()]] or [[render()]] to get the results. 68 | * @param array|string $requirements requirements to be checked. 69 | * If an array, it is treated as the set of requirements; 70 | * If a string, it is treated as the path of the file, which contains the requirements; 71 | * @return $this self instance. 72 | */ 73 | function check($requirements) 74 | { 75 | if (is_string($requirements)) { 76 | $requirements = require($requirements); 77 | } 78 | if (!is_array($requirements)) { 79 | $this->usageError('Requirements must be an array, "' . gettype($requirements) . '" has been given!'); 80 | } 81 | if (!isset($this->result) || !is_array($this->result)) { 82 | $this->result = array( 83 | 'summary' => array( 84 | 'total' => 0, 85 | 'errors' => 0, 86 | 'warnings' => 0, 87 | ), 88 | 'requirements' => array(), 89 | ); 90 | } 91 | foreach ($requirements as $key => $rawRequirement) { 92 | $requirement = $this->normalizeRequirement($rawRequirement, $key); 93 | $this->result['summary']['total']++; 94 | if (!$requirement['condition']) { 95 | if ($requirement['mandatory']) { 96 | $requirement['error'] = true; 97 | $requirement['warning'] = true; 98 | $this->result['summary']['errors']++; 99 | } else { 100 | $requirement['error'] = false; 101 | $requirement['warning'] = true; 102 | $this->result['summary']['warnings']++; 103 | } 104 | } else { 105 | $requirement['error'] = false; 106 | $requirement['warning'] = false; 107 | } 108 | $this->result['requirements'][] = $requirement; 109 | } 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * Performs the check for the Yii core requirements. 116 | * @return YiiRequirementChecker self instance. 117 | */ 118 | function checkYii() 119 | { 120 | return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php'); 121 | } 122 | 123 | /** 124 | * Return the check results. 125 | * @return array|null check results in format: 126 | * 127 | * ```php 128 | * array( 129 | * 'summary' => array( 130 | * 'total' => total number of checks, 131 | * 'errors' => number of errors, 132 | * 'warnings' => number of warnings, 133 | * ), 134 | * 'requirements' => array( 135 | * array( 136 | * ... 137 | * 'error' => is there an error, 138 | * 'warning' => is there a warning, 139 | * ), 140 | * ... 141 | * ), 142 | * ) 143 | * ``` 144 | */ 145 | function getResult() 146 | { 147 | if (isset($this->result)) { 148 | return $this->result; 149 | } else { 150 | return null; 151 | } 152 | } 153 | 154 | /** 155 | * Renders the requirements check result. 156 | * The output will vary depending is a script running from web or from console. 157 | */ 158 | function render() 159 | { 160 | if (!isset($this->result)) { 161 | $this->usageError('Nothing to render!'); 162 | } 163 | $baseViewFilePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views'; 164 | if (!empty($_SERVER['argv'])) { 165 | $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'index.php'; 166 | } else { 167 | $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'web' . DIRECTORY_SEPARATOR . 'index.php'; 168 | } 169 | $this->renderViewFile($viewFileName, $this->result); 170 | } 171 | 172 | /** 173 | * Checks if the given PHP extension is available and its version matches the given one. 174 | * @param string $extensionName PHP extension name. 175 | * @param string $version required PHP extension version. 176 | * @param string $compare comparison operator, by default '>=' 177 | * @return boolean if PHP extension version matches. 178 | */ 179 | function checkPhpExtensionVersion($extensionName, $version, $compare = '>=') 180 | { 181 | if (!extension_loaded($extensionName)) { 182 | return false; 183 | } 184 | $extensionVersion = phpversion($extensionName); 185 | if (empty($extensionVersion)) { 186 | return false; 187 | } 188 | if (strncasecmp($extensionVersion, 'PECL-', 5) === 0) { 189 | $extensionVersion = substr($extensionVersion, 5); 190 | } 191 | 192 | return version_compare($extensionVersion, $version, $compare); 193 | } 194 | 195 | /** 196 | * Checks if PHP configuration option (from php.ini) is on. 197 | * @param string $name configuration option name. 198 | * @return boolean option is on. 199 | */ 200 | function checkPhpIniOn($name) 201 | { 202 | $value = ini_get($name); 203 | if (empty($value)) { 204 | return false; 205 | } 206 | 207 | return ((int) $value === 1 || strtolower($value) === 'on'); 208 | } 209 | 210 | /** 211 | * Checks if PHP configuration option (from php.ini) is off. 212 | * @param string $name configuration option name. 213 | * @return boolean option is off. 214 | */ 215 | function checkPhpIniOff($name) 216 | { 217 | $value = ini_get($name); 218 | if (empty($value)) { 219 | return true; 220 | } 221 | 222 | return (strtolower($value) === 'off'); 223 | } 224 | 225 | /** 226 | * Compare byte sizes of values given in the verbose representation, 227 | * like '5M', '15K' etc. 228 | * @param string $a first value. 229 | * @param string $b second value. 230 | * @param string $compare comparison operator, by default '>='. 231 | * @return boolean comparison result. 232 | */ 233 | function compareByteSize($a, $b, $compare = '>=') 234 | { 235 | $compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')'; 236 | 237 | return $this->evaluateExpression($compareExpression); 238 | } 239 | 240 | /** 241 | * Gets the size in bytes from verbose size representation. 242 | * For example: '5K' => 5*1024 243 | * @param string $verboseSize verbose size representation. 244 | * @return integer actual size in bytes. 245 | */ 246 | function getByteSize($verboseSize) 247 | { 248 | if (empty($verboseSize)) { 249 | return 0; 250 | } 251 | if (is_numeric($verboseSize)) { 252 | return (int) $verboseSize; 253 | } 254 | $sizeUnit = trim($verboseSize, '0123456789'); 255 | $size = str_replace($sizeUnit, '', $verboseSize); 256 | $size = trim($size); 257 | if (!is_numeric($size)) { 258 | return 0; 259 | } 260 | switch (strtolower($sizeUnit)) { 261 | case 'kb': 262 | case 'k': 263 | return $size * 1024; 264 | case 'mb': 265 | case 'm': 266 | return $size * 1024 * 1024; 267 | case 'gb': 268 | case 'g': 269 | return $size * 1024 * 1024 * 1024; 270 | default: 271 | return 0; 272 | } 273 | } 274 | 275 | /** 276 | * Checks if upload max file size matches the given range. 277 | * @param string|null $min verbose file size minimum required value, pass null to skip minimum check. 278 | * @param string|null $max verbose file size maximum required value, pass null to skip maximum check. 279 | * @return boolean success. 280 | */ 281 | function checkUploadMaxFileSize($min = null, $max = null) 282 | { 283 | $postMaxSize = ini_get('post_max_size'); 284 | $uploadMaxFileSize = ini_get('upload_max_filesize'); 285 | if ($min !== null) { 286 | $minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>='); 287 | } else { 288 | $minCheckResult = true; 289 | } 290 | if ($max !== null) { 291 | $maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<='); 292 | } else { 293 | $maxCheckResult = true; 294 | } 295 | 296 | return ($minCheckResult && $maxCheckResult); 297 | } 298 | 299 | /** 300 | * Renders a view file. 301 | * This method includes the view file as a PHP script 302 | * and captures the display result if required. 303 | * @param string $_viewFile_ view file 304 | * @param array $_data_ data to be extracted and made available to the view file 305 | * @param boolean $_return_ whether the rendering result should be returned as a string 306 | * @return string the rendering result. Null if the rendering result is not required. 307 | */ 308 | function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false) 309 | { 310 | // we use special variable names here to avoid conflict when extracting data 311 | if (is_array($_data_)) { 312 | extract($_data_, EXTR_PREFIX_SAME, 'data'); 313 | } else { 314 | $data = $_data_; 315 | } 316 | if ($_return_) { 317 | ob_start(); 318 | ob_implicit_flush(false); 319 | require($_viewFile_); 320 | 321 | return ob_get_clean(); 322 | } else { 323 | require($_viewFile_); 324 | } 325 | } 326 | 327 | /** 328 | * Normalizes requirement ensuring it has correct format. 329 | * @param array $requirement raw requirement. 330 | * @param integer $requirementKey requirement key in the list. 331 | * @return array normalized requirement. 332 | */ 333 | function normalizeRequirement($requirement, $requirementKey = 0) 334 | { 335 | if (!is_array($requirement)) { 336 | $this->usageError('Requirement must be an array!'); 337 | } 338 | if (!array_key_exists('condition', $requirement)) { 339 | $this->usageError("Requirement '{$requirementKey}' has no condition!"); 340 | } else { 341 | $evalPrefix = 'eval:'; 342 | if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) { 343 | $expression = substr($requirement['condition'], strlen($evalPrefix)); 344 | $requirement['condition'] = $this->evaluateExpression($expression); 345 | } 346 | } 347 | if (!array_key_exists('name', $requirement)) { 348 | $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey; 349 | } 350 | if (!array_key_exists('mandatory', $requirement)) { 351 | if (array_key_exists('required', $requirement)) { 352 | $requirement['mandatory'] = $requirement['required']; 353 | } else { 354 | $requirement['mandatory'] = false; 355 | } 356 | } 357 | if (!array_key_exists('by', $requirement)) { 358 | $requirement['by'] = 'Unknown'; 359 | } 360 | if (!array_key_exists('memo', $requirement)) { 361 | $requirement['memo'] = ''; 362 | } 363 | 364 | return $requirement; 365 | } 366 | 367 | /** 368 | * Displays a usage error. 369 | * This method will then terminate the execution of the current application. 370 | * @param string $message the error message 371 | */ 372 | function usageError($message) 373 | { 374 | echo "Error: $message\n\n"; 375 | exit(1); 376 | } 377 | 378 | /** 379 | * Evaluates a PHP expression under the context of this class. 380 | * @param string $expression a PHP expression to be evaluated. 381 | * @return mixed the expression result. 382 | */ 383 | function evaluateExpression($expression) 384 | { 385 | return eval('return ' . $expression . ';'); 386 | } 387 | 388 | /** 389 | * Returns the server information. 390 | * @return string server information. 391 | */ 392 | function getServerInfo() 393 | { 394 | return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';; 395 | } 396 | 397 | /** 398 | * Returns the now date if possible in string representation. 399 | * @return string now date. 400 | */ 401 | function getNowDate() 402 | { 403 | return @strftime('%Y-%m-%d %H:%M', time());; 404 | } 405 | } 406 | --------------------------------------------------------------------------------