├── src ├── Template │ ├── .github │ │ ├── dependabot.yml │ │ └── workflows │ │ │ ├── phpcpd.yml │ │ │ ├── phpcsfixer.yml │ │ │ ├── unused.yml │ │ │ ├── infection.yml │ │ │ ├── psalm.yml │ │ │ ├── deptrac.yml │ │ │ ├── rector.yml │ │ │ ├── phpstan.yml │ │ │ └── phpunit.yml │ ├── .editorconfig │ ├── infection.json.dist │ ├── phpstan.neon.dist │ ├── psalm_autoload.php │ ├── composer-unused.php │ ├── psalm.xml │ ├── .php-cs-fixer.dist.php │ ├── phpunit.xml.dist │ ├── phpunit9.xml.dist │ ├── deptrac.yaml │ ├── rector.php │ └── Vagrantfile.dist ├── BADGES.md ├── docker │ ├── docker-compose.yaml │ └── README.md └── ide │ └── phpstorm │ └── .phpstorm.meta.php ├── psalm_autoload.php ├── CONTRIBUTING.md ├── .php-cs-fixer.dist.php ├── psalm.xml ├── LICENSE ├── SECURITY.md ├── composer.json ├── rector.php └── README.md /src/Template/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: daily 13 | -------------------------------------------------------------------------------- /src/Template/.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /src/Template/infection.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "directories": [ 4 | "app/" 5 | ], 6 | "excludes": [ 7 | "Config", 8 | "Database/Migrations", 9 | "Views" 10 | ] 11 | }, 12 | "logs": { 13 | "text": "build/infection.log" 14 | }, 15 | "mutators": { 16 | "@default": true 17 | }, 18 | "bootstrap": "vendor/codeigniter4/framework/system/Test/bootstrap.php" 19 | } 20 | -------------------------------------------------------------------------------- /psalm_autoload.php: -------------------------------------------------------------------------------- 1 | files() 9 | ->in([ 10 | __DIR__ . '/src/', 11 | ]) 12 | ->exclude('build') 13 | ->append([ 14 | __FILE__, 15 | __DIR__ . '/rector.php', 16 | ]); 17 | 18 | $overrides = []; 19 | 20 | $options = [ 21 | 'finder' => $finder, 22 | 'cacheFile' => 'build/.php-cs-fixer.cache', 23 | ]; 24 | 25 | return Factory::create(new CodeIgniter4(), $overrides, $options)->forProjects(); 26 | -------------------------------------------------------------------------------- /src/Template/psalm_autoload.php: -------------------------------------------------------------------------------- 1 | $config 11 | // ->addNamedFilter(NamedFilter::fromString('symfony/config')) 12 | // ->addPatternFilter(PatternFilter::fromString('/symfony-.*/')) 13 | ->setAdditionalFilesFor('codeigniter4/framework', [ 14 | ...Glob::glob(__DIR__ . '/vendor/codeigniter4/framework/system/Helpers/*.php'), 15 | ]); 16 | -------------------------------------------------------------------------------- /src/Template/psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Template/.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | files() 9 | ->in([ 10 | __DIR__ . '/app/', 11 | __DIR__ . '/tests/', 12 | ]) 13 | ->exclude([ 14 | 'build', 15 | 'Views', 16 | ]) 17 | ->append([ 18 | __FILE__, 19 | __DIR__ . '/rector.php', 20 | ]); 21 | 22 | $overrides = [ 23 | // 'declare_strict_types' => true, 24 | // 'void_return' => true, 25 | ]; 26 | 27 | $options = [ 28 | 'finder' => $finder, 29 | 'cacheFile' => 'build/.php-cs-fixer.cache', 30 | ]; 31 | 32 | return Factory::create(new CodeIgniter4(), $overrides, $options)->forProjects(); 33 | -------------------------------------------------------------------------------- /src/BADGES.md: -------------------------------------------------------------------------------- 1 | # Badges 2 | 3 | GitHub Actions (and some third-party tools) provide nice badges you can put in your README! 4 | Here are some examples from the devkit workflows (replace "organization/project" with your path). 5 | 6 | [![](https://github.com/organization/project/workflows/PHPUnit/badge.svg)](https://github.com/organization/project/actions/workflows/phpunit.yml) 7 | [![](https://github.com/organization/project/workflows/PHPStan/badge.svg)](https://github.com/organization/project/actions/workflows/phpstan.yml) 8 | [![](https://github.com/organization/project/workflows/Deptrac/badge.svg)](https://github.com/organization/project/actions/workflows/deptrac.yml) 9 | [![Coverage Status](https://coveralls.io/repos/github/organization/project/badge.svg?branch=develop)](https://coveralls.io/github/organization/project?branch=develop) 10 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/phpcpd.yml: -------------------------------------------------------------------------------- 1 | name: PHPCPD 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - '.github/workflows/phpcpd.yml' 10 | push: 11 | branches: 12 | - develop 13 | paths: 14 | - '**.php' 15 | - '.github/workflows/phpcpd.yml' 16 | 17 | jobs: 18 | build: 19 | name: Code Copy-Paste Detection 20 | runs-on: ubuntu-latest 21 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup PHP 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: '8.1' 31 | tools: phpcpd 32 | extensions: dom, mbstring 33 | coverage: none 34 | 35 | - name: Detect duplicate code 36 | run: phpcpd app/ tests/ 37 | -------------------------------------------------------------------------------- /src/docker/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | postgres: 5 | image: postgres:13-alpine 6 | ports: 7 | - "5432:5432" 8 | environment: 9 | POSTGRES_DB: test 10 | POSTGRES_USER: postgres 11 | POSTGRES_PASSWORD: postgres 12 | 13 | mssql: 14 | image: mcr.microsoft.com/mssql/server:2019-latest 15 | ports: 16 | - "1433:1433" 17 | environment: 18 | SA_PASSWORD: 1Secure*Password1 19 | ACCEPT_EULA: Y 20 | MSSQL_PID: Developer 21 | 22 | mysql: 23 | image: mysql:8.0 24 | ports: 25 | - "3306:3306" 26 | environment: 27 | MYSQL_ROOT_PASSWORD: mysql 28 | MYSQL_DATABASE: test 29 | MYSQL_USER: mysql 30 | MYSQL_PASSWORD: mysql 31 | 32 | oracle: 33 | image: gvenzl/oracle-xe:21 34 | ports: 35 | - "1521:1521" 36 | environment: 37 | ORACLE_RANDOM_PASSWORD: true 38 | APP_USER: ORACLE 39 | APP_USER_PASSWORD: ORACLE 40 | 41 | mailhog: 42 | image: mailhog/mailhog 43 | ports: 44 | - 1025:1025 45 | - 8025:8025 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CodeIgniter Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The development team and community take all security issues seriously. **Please do not make public any uncovered flaws.** 4 | 5 | ## Reporting a Vulnerability 6 | 7 | Thank you for improving the security of our code! Any assistance in removing security flaws will be acknowledged. 8 | 9 | **Please report security flaws by emailing the development team directly: security@codeigniter.com**. 10 | 11 | The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating 12 | the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the 13 | progress towards a fix and full announcement, and may ask for additional information or guidance. 14 | 15 | ## Disclosure Policy 16 | 17 | When the security team receives a security bug report, they will assign it to a primary handler. 18 | This person will coordinate the fix and release process, involving the following steps: 19 | 20 | - Confirm the problem and determine the affected versions. 21 | - Audit code to find any potential similar problems. 22 | - Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible. 23 | 24 | ## Comments on this Policy 25 | 26 | If you have suggestions on how this process could be improved please submit a Pull Request. 27 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/phpcsfixer.yml: -------------------------------------------------------------------------------- 1 | name: PHPCSFixer 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - '.github/workflows/phpcsfixer.yml' 10 | push: 11 | branches: 12 | - develop 13 | paths: 14 | - '**.php' 15 | - '.github/workflows/phpcsfixer.yml' 16 | 17 | jobs: 18 | build: 19 | name: Coding Standards 20 | runs-on: ubuntu-latest 21 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Set up PHP 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: '8.1' 31 | extensions: json, tokenizer 32 | coverage: none 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - name: Get composer cache directory 37 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 38 | 39 | - name: Cache composer dependencies 40 | uses: actions/cache@v4 41 | with: 42 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 43 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 44 | restore-keys: ${{ runner.os }}-composer- 45 | 46 | - name: Install dependencies 47 | run: | 48 | if [ -f composer.lock ]; then 49 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 50 | else 51 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 52 | fi 53 | 54 | - name: Check code for standards compliance 55 | run: vendor/bin/php-cs-fixer fix --verbose --ansi --dry-run --using-cache=no --diff 56 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/unused.yml: -------------------------------------------------------------------------------- 1 | name: Unused 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - '.github/workflows/unused.yml' 11 | push: 12 | branches: 13 | - develop 14 | paths: 15 | - '**.php' 16 | - 'composer.*' 17 | - '.github/workflows/unused.yml' 18 | 19 | jobs: 20 | build: 21 | name: Unused Package Detection 22 | runs-on: ubuntu-latest 23 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | 29 | - name: Setup PHP 30 | uses: shivammathur/setup-php@v2 31 | with: 32 | php-version: '8.1' 33 | tools: composer, composer-unused 34 | extensions: intl, json, mbstring, xml 35 | coverage: none 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | - name: Get composer cache directory 40 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 41 | 42 | - name: Cache composer dependencies 43 | uses: actions/cache@v4 44 | with: 45 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 46 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 47 | restore-keys: ${{ runner.os }}-composer- 48 | 49 | - name: Install dependencies 50 | run: | 51 | if [ -f composer.lock ]; then 52 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 53 | else 54 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 55 | fi 56 | 57 | - name: Detect unused packages 58 | run: composer-unused -vvv --output-format=github --ansi --no-interaction --no-progress 59 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/infection.yml: -------------------------------------------------------------------------------- 1 | name: Infection 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - 'phpunit*' 11 | - '.github/workflows/infection.yml' 12 | 13 | jobs: 14 | main: 15 | name: Mutation Testing 16 | runs-on: ubuntu-latest 17 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Set up PHP 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: '8.2' 27 | tools: infection, phpunit 28 | extensions: intl, json, mbstring, gd, xml, sqlite3 29 | coverage: xdebug 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | - name: Set up problem matchers for PHPUnit 34 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 35 | 36 | - name: Configure matchers 37 | uses: mheap/phpunit-matcher-action@v1 38 | 39 | - name: Get composer cache directory 40 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 41 | 42 | - name: Cache composer dependencies 43 | uses: actions/cache@v4 44 | with: 45 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 46 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 47 | restore-keys: ${{ runner.os }}-composer- 48 | 49 | - name: Install dependencies 50 | run: | 51 | if [ -f composer.lock ]; then 52 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 53 | else 54 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 55 | fi 56 | 57 | - name: Run Infection for added files only 58 | run: | 59 | git fetch --depth=1 origin $GITHUB_BASE_REF 60 | infection --threads=max --git-diff-lines --git-diff-base=origin/$GITHUB_BASE_REF --ignore-msi-with-no-mutations --only-covered --logger-github 61 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/psalm.yml: -------------------------------------------------------------------------------- 1 | name: Psalm 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - 'psalm*' 11 | - '.github/workflows/psalm.yml' 12 | push: 13 | branches: 14 | - develop 15 | paths: 16 | - '**.php' 17 | - 'composer.*' 18 | - 'psalm*' 19 | - '.github/workflows/psalm.yml' 20 | 21 | jobs: 22 | build: 23 | name: Psalm Analysis 24 | runs-on: ubuntu-latest 25 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup PHP 32 | uses: shivammathur/setup-php@v2 33 | with: 34 | php-version: '8.1' 35 | tools: phpstan, phpunit 36 | extensions: intl, json, mbstring, xml 37 | coverage: none 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | 41 | - name: Get composer cache directory 42 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 43 | 44 | - name: Cache composer dependencies 45 | uses: actions/cache@v4 46 | with: 47 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 48 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 49 | restore-keys: ${{ runner.os }}-composer- 50 | 51 | - name: Create Psalm cache directory 52 | run: mkdir -p build/psalm 53 | 54 | - name: Cache Psalm results 55 | uses: actions/cache@v4 56 | with: 57 | path: build/psalm 58 | key: ${{ runner.os }}-psalm-${{ github.sha }} 59 | restore-keys: ${{ runner.os }}-psalm- 60 | 61 | - name: Install dependencies 62 | run: | 63 | if [ -f composer.lock ]; then 64 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 65 | else 66 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 67 | fi 68 | 69 | - name: Run Psalm analysis 70 | run: vendor/bin/psalm 71 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/deptrac.yml: -------------------------------------------------------------------------------- 1 | name: Deptrac 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - 'depfile.yaml' 11 | - '.github/workflows/deptrac.yml' 12 | push: 13 | branches: 14 | - develop 15 | paths: 16 | - '**.php' 17 | - 'composer.*' 18 | - 'depfile.yaml' 19 | - '.github/workflows/deptrac.yml' 20 | 21 | jobs: 22 | build: 23 | name: Dependency Tracing 24 | runs-on: ubuntu-latest 25 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | 31 | - name: Set up PHP 32 | uses: shivammathur/setup-php@v2 33 | with: 34 | php-version: '8.1' 35 | extensions: intl, json, mbstring, xml 36 | coverage: none 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Get composer cache directory 41 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 42 | 43 | - name: Cache composer dependencies 44 | uses: actions/cache@v4 45 | with: 46 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 47 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 48 | restore-keys: ${{ runner.os }}-composer- 49 | 50 | - name: Create Deptrac cache directory 51 | run: mkdir -p build/ 52 | 53 | - name: Cache Deptrac results 54 | uses: actions/cache@v4 55 | with: 56 | path: build 57 | key: ${{ runner.os }}-deptrac-${{ github.sha }} 58 | restore-keys: ${{ runner.os }}-deptrac- 59 | 60 | - name: Install dependencies 61 | run: | 62 | if [ -f composer.lock ]; then 63 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 64 | else 65 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 66 | fi 67 | 68 | - name: Trace dependencies 69 | run: | 70 | composer require --dev qossmic/deptrac-shim 71 | vendor/bin/deptrac analyze --cache-file=build/deptrac.cache 72 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/rector.yml: -------------------------------------------------------------------------------- 1 | name: Rector 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - 'rector.php' 11 | - '.github/workflows/rector.yml' 12 | push: 13 | branches: 14 | - develop 15 | paths: 16 | - '**.php' 17 | - 'composer.*' 18 | - 'rector.php' 19 | - '.github/workflows/rector.yml' 20 | 21 | jobs: 22 | build: 23 | name: PHP ${{ matrix.php-versions }} Rector Analysis 24 | runs-on: ubuntu-latest 25 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | php-versions: ['8.1', '8.2', '8.3', '8.4'] 30 | 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Set up PHP 36 | uses: shivammathur/setup-php@v2 37 | with: 38 | php-version: ${{ matrix.php-versions }} 39 | tools: phpstan 40 | extensions: intl, json, mbstring, xml 41 | coverage: none 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | - name: Get composer cache directory 46 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 47 | 48 | - name: Cache composer dependencies 49 | uses: actions/cache@v4 50 | with: 51 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 52 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 53 | restore-keys: ${{ runner.os }}-composer- 54 | 55 | - name: Install dependencies 56 | run: | 57 | if [ -f composer.lock ]; then 58 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 59 | else 60 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 61 | fi 62 | 63 | - name: Rector Cache 64 | uses: actions/cache@v4 65 | with: 66 | path: /tmp/rector 67 | key: ${{ runner.os }}-rector-${{ github.run_id }} 68 | restore-keys: ${{ runner.os }}-rector- 69 | 70 | - run: mkdir -p /tmp/rector 71 | 72 | - name: Analyze for refactoring 73 | run: vendor/bin/rector process --dry-run --no-progress-bar 74 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: PHPStan 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - 'phpstan*' 11 | - '.github/workflows/phpstan.yml' 12 | push: 13 | branches: 14 | - develop 15 | paths: 16 | - '**.php' 17 | - 'composer.*' 18 | - 'phpstan*' 19 | - '.github/workflows/phpstan.yml' 20 | 21 | jobs: 22 | build: 23 | name: PHP ${{ matrix.php-versions }} Static Analysis 24 | runs-on: ubuntu-latest 25 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | php-versions: ['8.1', '8.2', '8.3', '8.4'] 30 | 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Setup PHP 36 | uses: shivammathur/setup-php@v2 37 | with: 38 | php-version: ${{ matrix.php-versions }} 39 | tools: phpstan, phpunit 40 | extensions: intl, json, mbstring, xml 41 | coverage: none 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | - name: Get composer cache directory 46 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 47 | 48 | - name: Cache composer dependencies 49 | uses: actions/cache@v4 50 | with: 51 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 52 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 53 | restore-keys: ${{ runner.os }}-composer- 54 | 55 | - name: Create PHPStan cache directory 56 | run: mkdir -p build/phpstan 57 | 58 | - name: Cache PHPStan results 59 | uses: actions/cache@v4 60 | with: 61 | path: build/phpstan 62 | key: ${{ runner.os }}-phpstan-${{ github.sha }} 63 | restore-keys: ${{ runner.os }}-phpstan- 64 | 65 | - name: Install dependencies 66 | run: | 67 | if [ -f composer.lock ]; then 68 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 69 | else 70 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 71 | fi 72 | 73 | - name: Run static analysis 74 | run: vendor/bin/phpstan analyze 75 | -------------------------------------------------------------------------------- /src/docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker for development 2 | 3 | ## Included 4 | 5 | - PostgreSQL 13 6 | - SQL Server 2019 7 | - MySQL 8.0 8 | - Oracle Database 21c XE 9 | - MailHog 10 | 11 | ## Usage 12 | 13 | ``` 14 | $ docker-compose up -d 15 | ``` 16 | 17 | ``` 18 | $ docker-compose down 19 | ``` 20 | 21 | ## Config 22 | 23 | ### PostgreSQL 24 | 25 | #### .env 26 | 27 | ``` 28 | database.default.hostname = localhost 29 | database.default.database = test 30 | database.default.username = postgres 31 | database.default.password = postgres 32 | database.default.DBDriver = Postgre 33 | database.default.port = 5432 34 | ``` 35 | 36 | ### SQL Server 37 | 38 | #### Create Database 39 | 40 | ``` 41 | $ docker-compose exec mssql bash 42 | mssql@aaa8e9411491:/$ /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "1Secure*Password1" -Q "CREATE DATABASE test" 43 | ``` 44 | 45 | #### .env 46 | 47 | ``` 48 | database.default.hostname = localhost 49 | database.default.database = test 50 | database.default.username = sa 51 | database.default.password = 1Secure*Password1 52 | database.default.DBDriver = SQLSRV 53 | database.default.port = 1433 54 | ``` 55 | 56 | See https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-ver15&pivots=cs1-bash 57 | 58 | ### MySQL 59 | 60 | #### .env 61 | 62 | ``` 63 | database.default.hostname = localhost 64 | database.default.database = test 65 | database.default.username = mysql 66 | database.default.password = mysql 67 | database.default.DBDriver = MySQLi 68 | database.default.port = 3306 69 | ``` 70 | 71 | ### Oracle 72 | 73 | #### .env 74 | 75 | ``` 76 | NLS_LANG = 'AMERICAN_AMERICA.UTF8' 77 | NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' 78 | NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS' 79 | NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS' 80 | database.default.DSN = localhost:1521/XEPDB1 81 | database.default.hostname = 82 | database.default.database = 83 | database.default.username = ORACLE 84 | database.default.password = ORACLE 85 | database.default.DBDriver = OCI8 86 | database.default.charset = AL32UTF8 87 | database.default.port = 1521 88 | ``` 89 | 90 | ### MailHog 91 | 92 | #### .env 93 | 94 | ``` 95 | email.protocol = smtp 96 | email.SMTPHost = localhost 97 | email.SMTPPort = 1025 98 | email.SMTPCrypto = 99 | email.fromEmail = info@example.com 100 | ``` 101 | 102 | Navigate to http://localhost:8025/ to see sent mails. 103 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codeigniter4/devkit", 3 | "description": "Development toolkit for CodeIgniter libraries and projects", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "codeigniter", 8 | "codeigniter4", 9 | "devkit", 10 | "toolkit", 11 | "development", 12 | "tools", 13 | "static analysis" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Matthew Gatner", 18 | "email": "mgatner@tattersoftware.com", 19 | "homepage": "https://tattersoftware.com", 20 | "role": "Developer" 21 | } 22 | ], 23 | "homepage": "https://github.com/codeigniter4/devkit", 24 | "require": { 25 | "php": "^8.1", 26 | "codeigniter/coding-standard": "^1.5", 27 | "fakerphp/faker": "^1.9", 28 | "mikey179/vfsstream": "^1.6", 29 | "nexusphp/cs-config": "^3.6", 30 | "nexusphp/tachycardia": "^2.0", 31 | "phpstan/extension-installer": "^1.1", 32 | "phpstan/phpstan": "^2.0", 33 | "phpstan/phpstan-deprecation-rules": "^2.0", 34 | "phpstan/phpstan-phpunit": "^2.0", 35 | "phpunit/phpunit": "^10.5 || ^11.5", 36 | "rector/rector": "^2.0", 37 | "roave/security-advisories": "dev-latest", 38 | "vimeo/psalm": "^5.0 || ^6.0" 39 | }, 40 | "require-dev": { 41 | "codeigniter4/framework": "^4.1", 42 | "icanhazstring/composer-unused": "dev-main" 43 | }, 44 | "minimum-stability": "dev", 45 | "prefer-stable": true, 46 | "config": { 47 | "allow-plugins": { 48 | "phpstan/extension-installer": true 49 | } 50 | }, 51 | "scripts": { 52 | "analyze": [ 53 | "Composer\\Config::disableProcessTimeout", 54 | "phpstan analyze", 55 | "psalm", 56 | "rector process --dry-run" 57 | ], 58 | "ci": [ 59 | "Composer\\Config::disableProcessTimeout", 60 | "@cs", 61 | "@deduplicate", 62 | "@inspect", 63 | "@analyze", 64 | "@test" 65 | ], 66 | "cs": "php-cs-fixer fix --ansi --verbose --dry-run --diff", 67 | "cs-fix": "php-cs-fixer fix --ansi --verbose --diff", 68 | "deduplicate": "phpcpd app/ src/", 69 | "inspect": "deptrac analyze --cache-file=build/deptrac.cache", 70 | "mutate": "infection --threads=2 --skip-initial-tests --coverage=build/phpunit", 71 | "sa": "@analyze", 72 | "style": "@cs-fix", 73 | "test": "phpunit" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Template/.github/workflows/phpunit.yml: -------------------------------------------------------------------------------- 1 | name: PHPUnit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | paths: 8 | - '**.php' 9 | - 'composer.*' 10 | - 'phpunit*' 11 | - '.github/workflows/phpunit.yml' 12 | push: 13 | branches: 14 | - develop 15 | paths: 16 | - '**.php' 17 | - 'composer.*' 18 | - 'phpunit*' 19 | - '.github/workflows/phpunit.yml' 20 | 21 | jobs: 22 | main: 23 | name: PHP ${{ matrix.php-versions }} Unit Tests 24 | runs-on: ubuntu-latest 25 | if: (! contains(github.event.head_commit.message, '[ci skip]')) 26 | strategy: 27 | matrix: 28 | php-versions: ['8.1', '8.2', '8.3', '8.4'] 29 | 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | 34 | - name: Set up PHP 35 | uses: shivammathur/setup-php@v2 36 | with: 37 | php-version: ${{ matrix.php-versions }} 38 | tools: composer, phive, phpunit 39 | extensions: intl, json, mbstring, gd, xdebug, xml, sqlite3 40 | coverage: xdebug 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | 44 | - name: Get composer cache directory 45 | run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_ENV 46 | 47 | - name: Cache composer dependencies 48 | uses: actions/cache@v4 49 | with: 50 | path: ${{ env.COMPOSER_CACHE_FILES_DIR }} 51 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} 52 | restore-keys: ${{ runner.os }}-composer- 53 | 54 | - name: Install dependencies 55 | run: | 56 | if [ -f composer.lock ]; then 57 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 58 | else 59 | composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader 60 | fi 61 | 62 | - name: Test with PHPUnit 63 | run: vendor/bin/phpunit --verbose --coverage-text 64 | env: 65 | TERM: xterm-256color 66 | TACHYCARDIA_MONITOR_GA: enabled 67 | 68 | - if: matrix.php-versions == '8.1' 69 | name: Run Coveralls 70 | continue-on-error: true 71 | run: | 72 | sudo phive --no-progress install --global --trust-gpg-keys E82B2FB314E9906E php-coveralls 73 | php-coveralls --verbose --coverage_clover=build/phpunit/clover.xml --json_path build/phpunit/coveralls-upload.json 74 | env: 75 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 76 | COVERALLS_PARALLEL: true 77 | COVERALLS_FLAG_NAME: PHP ${{ matrix.php-versions }} 78 | 79 | coveralls: 80 | needs: [main] 81 | name: Coveralls Finished 82 | runs-on: ubuntu-latest 83 | steps: 84 | - name: Upload Coveralls results 85 | uses: coverallsapp/github-action@master 86 | continue-on-error: true 87 | with: 88 | github-token: ${{ secrets.GITHUB_TOKEN }} 89 | parallel-finished: true 90 | -------------------------------------------------------------------------------- /src/Template/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ./tests 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 64 | 65 | 66 | 67 | ./app/ 68 | 69 | 70 | ./app/Config 71 | ./app/Views 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/Template/phpunit9.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 23 | 24 | ./app/ 25 | 26 | 27 | ./app/Config 28 | ./app/Views 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ./tests 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 0.50 51 | 52 | 53 | 30 54 | 55 | 56 | 2 57 | 58 | 59 | true 60 | 61 | 62 | true 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/ide/phpstorm/.phpstorm.meta.php: -------------------------------------------------------------------------------- 1 | \CodeIgniter\Autoloader\Autoloader::class, 6 | 'cache' => \CodeIgniter\Cache\CacheInterface::class, 7 | 'clirequest' => \CodeIgniter\HTTP\CLIRequest::class, 8 | 'codeigniter' => \CodeIgniter\CodeIgniter::class, 9 | 'commands' => \CodeIgniter\CLI\Commands::class, 10 | 'csp' => \CodeIgniter\HTTP\ContentSecurityPolicy::class, 11 | 'curlrequest' => \CodeIgniter\HTTP\CURLRequest::class, 12 | 'email' => \CodeIgniter\Email\Email::class, 13 | 'encrypter' => \CodeIgniter\Encryption\EncrypterInterface::class, 14 | 'exceptions' => \CodeIgniter\Debug\Exceptions::class, 15 | 'filters' => \CodeIgniter\Filters\Filters::class, 16 | 'format' => \CodeIgniter\Format\Format::class, 17 | 'honeypot' => \CodeIgniter\Honeypot\Honeypot::class, 18 | 'image' => \CodeIgniter\Images\Handlers\BaseHandler::class, 19 | 'iterator' => \CodeIgniter\Debug\Iterator::class, 20 | 'language' => \CodeIgniter\Language\Language::class, 21 | 'locator' => \CodeIgniter\Autoloader\FileLocator::class, 22 | 'logger' => \CodeIgniter\Log\Logger::class, 23 | 'migrations' => \CodeIgniter\Database\MigrationRunner::class, 24 | 'negotiator' => \CodeIgniter\HTTP\Negotiate::class, 25 | 'pager' => \CodeIgniter\Pager\Pager::class, 26 | 'parser' => \CodeIgniter\View\Parser::class, 27 | 'redirectresponse' => \CodeIgniter\HTTP\RedirectResponse::class, 28 | 'renderer' => \CodeIgniter\View\View::class, 29 | 'request' => \CodeIgniter\HTTP\IncomingRequest::class, 30 | 'response' => \CodeIgniter\HTTP\Response::class, 31 | 'router' => \CodeIgniter\Router\Router::class, 32 | 'routes' => \CodeIgniter\Router\RouteCollection::class, 33 | 'security' => \CodeIgniter\Security\Security::class, 34 | 'session' => \CodeIgniter\Session\Session::class, 35 | 'throttler' => \CodeIgniter\Throttle\Throttler::class, 36 | 'timer' => \CodeIgniter\Debug\Timer::class, 37 | 'toolbar' => \CodeIgniter\Debug\Toolbar::class, 38 | 'typography' => \CodeIgniter\Typography\Typography::class, 39 | 'uri' => \CodeIgniter\HTTP\URI::class, 40 | 'validation' => \CodeIgniter\Validation\Validation::class, 41 | 'viewcell' => \CodeIgniter\View\Cell::class, 42 | ])); 43 | 44 | override(\config(), map([ 45 | 'App' => \Config\App::class, 46 | 'Autoload' => \Config\Autoload::class, 47 | 'Cache' => \Config\Cache::class, 48 | 'ContentSecurityPolicy' => \Config\ContentSecurityPolicy::class, 49 | 'Cookie' => \Config\Cookie::class, 50 | 'CURLRequest' => \Config\CURLRequest::class, 51 | 'Database' => \Config\Database::class, 52 | 'DocTypes' => \Config\DocTypes::class, 53 | 'Email' => \Config\Email::class, 54 | 'Encryption' => \Config\Encryption::class, 55 | 'Exceptions' => \Config\Exceptions::class, 56 | 'Feature' => \Config\Feature::class, 57 | 'Filters' => \Config\Filters::class, 58 | 'ForeignCharacters' => \Config\ForeignCharacters::class, 59 | 'Format' => \Config\Format::class, 60 | 'Generators' => \Config\Generators::class, 61 | 'Honeypot' => \Config\Honeypot::class, 62 | 'Images' => \Config\Images::class, 63 | 'Kint' => \Config\Kint::class, 64 | 'Logger' => \Config\Logger::class, 65 | 'Migrations' => \Config\Migrations::class, 66 | 'Mimes' => \Config\Mimes::class, 67 | 'Modules' => \Config\Modules::class, 68 | 'Pager' => \Config\Pager::class, 69 | 'Publisher' => \Config\Publisher::class, 70 | 'Security' => \Config\Security::class, 71 | 'Services' => \Config\Services::class, 72 | 'Toolbar' => \Config\Toolbar::class, 73 | 'UserAgents' => \Config\UserAgents::class, 74 | 'Validation' => \Config\Validation::class, 75 | 'View' => \Config\View::class, 76 | ])); 77 | } 78 | -------------------------------------------------------------------------------- /src/Template/deptrac.yaml: -------------------------------------------------------------------------------- 1 | deptrac: 2 | paths: 3 | - ./app/ 4 | - ./vendor/codeigniter4/framework/system/ 5 | exclude_files: 6 | - '#.*test.*#i' 7 | layers: 8 | - name: Model 9 | collectors: 10 | - type: bool 11 | must: 12 | - type: class 13 | value: .*[A-Za-z]+Model$ 14 | must_not: 15 | - type: directory 16 | value: vendor/.* 17 | - name: Vendor Model 18 | collectors: 19 | - type: bool 20 | must: 21 | - type: class 22 | value: .*[A-Za-z]+Model$ 23 | - type: directory 24 | value: vendor/.* 25 | - name: Controller 26 | collectors: 27 | - type: bool 28 | must: 29 | - type: class 30 | value: .*\/Controllers\/.* 31 | must_not: 32 | - type: directory 33 | value: vendor/.* 34 | - name: Vendor Controller 35 | collectors: 36 | - type: bool 37 | must: 38 | - type: class 39 | value: .*\/Controllers\/.* 40 | - type: directory 41 | value: vendor/.* 42 | - name: Config 43 | collectors: 44 | - type: bool 45 | must: 46 | - type: directory 47 | value: app/Config/.* 48 | must_not: 49 | - type: class 50 | value: .*Services 51 | - type: directory 52 | value: vendor/.* 53 | - name: Vendor Config 54 | collectors: 55 | - type: bool 56 | must: 57 | - type: directory 58 | value: vendor/.*/Config/.* 59 | must_not: 60 | - type: class 61 | value: .*Services 62 | - name: Entity 63 | collectors: 64 | - type: bool 65 | must: 66 | - type: directory 67 | value: app/Entities/.* 68 | must_not: 69 | - type: directory 70 | value: vendor/.* 71 | - name: Vendor Entity 72 | collectors: 73 | - type: bool 74 | must: 75 | - type: directory 76 | value: vendor/.*/Entities/.* 77 | - name: View 78 | collectors: 79 | - type: bool 80 | must: 81 | - type: directory 82 | value: app/Views/.* 83 | must_not: 84 | - type: directory 85 | value: vendor/.* 86 | - name: Vendor View 87 | collectors: 88 | - type: bool 89 | must: 90 | - type: directory 91 | value: vendor/.*/Views/.* 92 | - name: Service 93 | collectors: 94 | - type: class 95 | value: .*Services.* 96 | ruleset: 97 | Entity: 98 | - Config 99 | - Model 100 | - Service 101 | - Vendor Config 102 | - Vendor Entity 103 | - Vendor Model 104 | Config: 105 | - Service 106 | - Vendor Config 107 | Model: 108 | - Config 109 | - Entity 110 | - Service 111 | - Vendor Config 112 | - Vendor Entity 113 | - Vendor Model 114 | Service: 115 | - Config 116 | - Vendor Config 117 | 118 | # Ignore anything in the Vendor layers 119 | Vendor Model: 120 | - Config 121 | - Service 122 | - Vendor Config 123 | - Vendor Controller 124 | - Vendor Entity 125 | - Vendor Model 126 | - Vendor View 127 | Vendor Controller: 128 | - Service 129 | - Vendor Config 130 | - Vendor Controller 131 | - Vendor Entity 132 | - Vendor Model 133 | - Vendor View 134 | Vendor Config: 135 | - Config 136 | - Service 137 | - Vendor Config 138 | - Vendor Controller 139 | - Vendor Entity 140 | - Vendor Model 141 | - Vendor View 142 | Vendor Entity: 143 | - Service 144 | - Vendor Config 145 | - Vendor Controller 146 | - Vendor Entity 147 | - Vendor Model 148 | - Vendor View 149 | Vendor View: 150 | - Service 151 | - Vendor Config 152 | - Vendor Controller 153 | - Vendor Entity 154 | - Vendor Model 155 | - Vendor View 156 | skip_violations: [] 157 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | sets([ 49 | SetList::DEAD_CODE, 50 | LevelSetList::UP_TO_PHP_74, 51 | PHPUnitSetList::PHPUNIT_CODE_QUALITY, 52 | PHPUnitSetList::PHPUNIT_100, 53 | ]); 54 | 55 | $rectorConfig->parallel(); 56 | 57 | // Github action cache 58 | $rectorConfig->cacheClass(FileCacheStorage::class); 59 | if (is_dir('/tmp')) { 60 | $rectorConfig->cacheDirectory('/tmp/rector'); 61 | } 62 | 63 | // The paths to refactor (can also be supplied with CLI arguments) 64 | $rectorConfig->paths([ 65 | __DIR__ . '/src/', 66 | ]); 67 | 68 | // Include Composer's autoload - required for global execution, remove if running locally 69 | $rectorConfig->autoloadPaths([ 70 | __DIR__ . '/vendor/autoload.php', 71 | ]); 72 | 73 | // Do you need to include constants, class aliases, or a custom autoloader? 74 | $rectorConfig->bootstrapFiles([ 75 | realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php', 76 | ]); 77 | 78 | if (is_file(__DIR__ . '/phpstan.neon.dist')) { 79 | $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist'); 80 | } 81 | 82 | // Set the target version for refactoring 83 | $rectorConfig->phpVersion(PhpVersion::PHP_74); 84 | 85 | // Auto-import fully qualified class names 86 | $rectorConfig->importNames(); 87 | 88 | // Are there files or rules you need to skip? 89 | $rectorConfig->skip([ 90 | __DIR__ . '/src/Views', 91 | 92 | StringifyStrNeedlesRector::class, 93 | YieldDataProviderRector::class, 94 | 95 | // Note: requires php 8 96 | RemoveUnusedPromotedPropertyRector::class, 97 | AnnotationWithValueToAttributeRector::class, 98 | 99 | // May load view files directly when detecting classes 100 | StringClassNameToClassConstantRector::class, 101 | ]); 102 | 103 | // auto import fully qualified class names 104 | $rectorConfig->importNames(); 105 | 106 | $rectorConfig->rule(SimplifyUselessVariableRector::class); 107 | $rectorConfig->rule(RemoveAlwaysElseRector::class); 108 | $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class); 109 | $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class); 110 | $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class); 111 | $rectorConfig->rule(SimplifyStrposLowerRector::class); 112 | $rectorConfig->rule(CombineIfRector::class); 113 | $rectorConfig->rule(SimplifyIfReturnBoolRector::class); 114 | $rectorConfig->rule(InlineIfToExplicitIfRector::class); 115 | $rectorConfig->rule(PreparedValueToEarlyReturnRector::class); 116 | $rectorConfig->rule(ShortenElseIfRector::class); 117 | $rectorConfig->rule(SimplifyIfElseToTernaryRector::class); 118 | $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class); 119 | $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class); 120 | $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class); 121 | $rectorConfig->rule(SimplifyRegexPatternRector::class); 122 | $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class); 123 | $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); 124 | $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class); 125 | $rectorConfig->rule(SimplifyEmptyCheckOnEmptyArrayRector::class); 126 | $rectorConfig->rule(TernaryEmptyArrayArrayDimFetchToCoalesceRector::class); 127 | $rectorConfig->rule(EmptyOnNullableObjectToInstanceOfRector::class); 128 | $rectorConfig->rule(DisallowedEmptyRuleFixerRector::class); 129 | $rectorConfig 130 | ->ruleWithConfiguration(TypedPropertyFromAssignsRector::class, [ 131 | /** 132 | * The INLINE_PUBLIC value is default to false to avoid BC break, 133 | * if you use for libraries and want to preserve BC break, you don't 134 | * need to configure it, as it included in LevelSetList::UP_TO_PHP_74 135 | * Set to true for projects that allow BC break 136 | */ 137 | TypedPropertyFromAssignsRector::INLINE_PUBLIC => true, 138 | ]); 139 | $rectorConfig->rule(StringClassNameToClassConstantRector::class); 140 | $rectorConfig->rule(PrivatizeFinalClassPropertyRector::class); 141 | $rectorConfig->rule(CompleteDynamicPropertiesRector::class); 142 | $rectorConfig->rule(BooleanInIfConditionRuleFixerRector::class); 143 | $rectorConfig->rule(SingleInArrayToCompareRector::class); 144 | $rectorConfig->rule(VersionCompareFuncCallToConstantRector::class); 145 | $rectorConfig->rule(ExplicitBoolCompareRector::class); 146 | }; 147 | -------------------------------------------------------------------------------- /src/Template/rector.php: -------------------------------------------------------------------------------- 1 | sets([ 49 | SetList::DEAD_CODE, 50 | LevelSetList::UP_TO_PHP_81, 51 | PHPUnitSetList::PHPUNIT_CODE_QUALITY, 52 | PHPUnitSetList::PHPUNIT_100, 53 | ]); 54 | 55 | $rectorConfig->parallel(); 56 | 57 | // Github action cache 58 | $rectorConfig->cacheClass(FileCacheStorage::class); 59 | if (is_dir('/tmp')) { 60 | $rectorConfig->cacheDirectory('/tmp/rector'); 61 | } 62 | 63 | // The paths to refactor (can also be supplied with CLI arguments) 64 | $rectorConfig->paths([ 65 | __DIR__ . '/app/', 66 | __DIR__ . '/tests/', 67 | ]); 68 | 69 | // Include Composer's autoload - required for global execution, remove if running locally 70 | $rectorConfig->autoloadPaths([ 71 | __DIR__ . '/vendor/autoload.php', 72 | ]); 73 | 74 | // Do you need to include constants, class aliases, or a custom autoloader? 75 | $rectorConfig->bootstrapFiles([ 76 | realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php', 77 | ]); 78 | 79 | if (is_file(__DIR__ . '/phpstan.neon.dist')) { 80 | $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist'); 81 | } 82 | 83 | // Set the target version for refactoring 84 | $rectorConfig->phpVersion(PhpVersion::PHP_81); 85 | 86 | // Auto-import fully qualified class names 87 | $rectorConfig->importNames(); 88 | 89 | // Are there files or rules you need to skip? 90 | $rectorConfig->skip([ 91 | __DIR__ . '/app/Views', 92 | 93 | StringifyStrNeedlesRector::class, 94 | YieldDataProviderRector::class, 95 | 96 | // Note: requires php 8 97 | RemoveUnusedPromotedPropertyRector::class, 98 | AnnotationWithValueToAttributeRector::class, 99 | 100 | // May load view files directly when detecting classes 101 | StringClassNameToClassConstantRector::class, 102 | ]); 103 | 104 | // auto import fully qualified class names 105 | $rectorConfig->importNames(); 106 | 107 | $rectorConfig->rule(SimplifyUselessVariableRector::class); 108 | $rectorConfig->rule(RemoveAlwaysElseRector::class); 109 | $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class); 110 | $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class); 111 | $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class); 112 | $rectorConfig->rule(SimplifyStrposLowerRector::class); 113 | $rectorConfig->rule(CombineIfRector::class); 114 | $rectorConfig->rule(SimplifyIfReturnBoolRector::class); 115 | $rectorConfig->rule(InlineIfToExplicitIfRector::class); 116 | $rectorConfig->rule(PreparedValueToEarlyReturnRector::class); 117 | $rectorConfig->rule(ShortenElseIfRector::class); 118 | $rectorConfig->rule(SimplifyIfElseToTernaryRector::class); 119 | $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class); 120 | $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class); 121 | $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class); 122 | $rectorConfig->rule(SimplifyRegexPatternRector::class); 123 | $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class); 124 | $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); 125 | $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class); 126 | $rectorConfig->rule(SimplifyEmptyCheckOnEmptyArrayRector::class); 127 | $rectorConfig->rule(TernaryEmptyArrayArrayDimFetchToCoalesceRector::class); 128 | $rectorConfig->rule(EmptyOnNullableObjectToInstanceOfRector::class); 129 | $rectorConfig->rule(DisallowedEmptyRuleFixerRector::class); 130 | $rectorConfig 131 | ->ruleWithConfiguration(TypedPropertyFromAssignsRector::class, [ 132 | /** 133 | * The INLINE_PUBLIC value is default to false to avoid BC break, 134 | * if you use for libraries and want to preserve BC break, you don't 135 | * need to configure it, as it included in LevelSetList::UP_TO_PHP_74 136 | * Set to true for projects that allow BC break 137 | */ 138 | TypedPropertyFromAssignsRector::INLINE_PUBLIC => true, 139 | ]); 140 | $rectorConfig->rule(StringClassNameToClassConstantRector::class); 141 | $rectorConfig->rule(PrivatizeFinalClassPropertyRector::class); 142 | $rectorConfig->rule(CompleteDynamicPropertiesRector::class); 143 | $rectorConfig->rule(BooleanInIfConditionRuleFixerRector::class); 144 | $rectorConfig->rule(SingleInArrayToCompareRector::class); 145 | $rectorConfig->rule(VersionCompareFuncCallToConstantRector::class); 146 | $rectorConfig->rule(ExplicitBoolCompareRector::class); 147 | }; 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeIgniter DevKit 2 | 3 | Development toolkit for CodeIgniter libraries and projects 4 | 5 | ## Installation 6 | 7 | Install via Composer: 8 | 9 | ```console 10 | composer config minimum-stability dev 11 | composer config prefer-stable true 12 | composer require --dev codeigniter4/devkit 13 | ``` 14 | 15 | ## Included Dependencies 16 | 17 | ### Styles and Standards 18 | 19 | * [CodeIgniter Coding Standard](https://github.com/CodeIgniter/coding-standard) 20 | * [NexusPHP CS Config](https://github.com/NexusPHP/cs-config) 21 | 22 | ### Testing and Analysis 23 | 24 | * [NexusPHP Tachycardia](https://github.com/NexusPHP/tachycardia) 25 | * [PHPStan](https://phpstan.org/user-guide/getting-started) 26 | * [PHPUnit](https://phpunit.readthedocs.io) 27 | * [Psalm](https://psalm.dev) 28 | 29 | ### Mocking 30 | 31 | * [FakerPHP](https://fakerphp.github.io) 32 | * [VFS Stream](https://github.com/bovigo/vfsStream/wiki) 33 | 34 | ### Security 35 | 36 | * [Dependabot](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates) 37 | * [Roave Security Advisories](https://github.com/Roave/SecurityAdvisories) 38 | 39 | ### Additional Tools 40 | 41 | These are integrated into the workflows but not included via Composer. If you want to use them 42 | locally they will need to be installed. All of them (except Rector) are available via [Phive](https://phar.io/#Tools). 43 | 44 | * [Composer Normalize](https://github.com/ergebnis/composer-normalize) 45 | * [Composer Unused](https://github.com/composer-unused/composer-unused) 46 | * [Deptrac](https://github.com/deptrac/deptrac) 47 | * [Infection](https://infection.github.io/) 48 | * [PHP Coveralls](https://php-coveralls.github.io/php-coveralls/) 49 | * [PHP CS Fixer](https://cs.symfony.com/) 50 | * [Rector](https://github.com/rectorphp/rector/) 51 | 52 | ## Template Files 53 | 54 | The provided source files (in **Template/**) should be considered guidelines for your own use, 55 | as they may need changing to fit your environment. These are based on the following assumptions: 56 | 57 | 1. Your default repository branch is set to `develop` 58 | 2. You use Composer to manage all necessary dependencies 59 | 3. Your source code is located in **app/** (for projects) or **src/** (for libraries) 60 | 4. Your unit tests are located in **tests/** 61 | 5. Your CodeIgniter dependency is `codeigniter4/framework` (some paths need to be changed for `dev-develop`) 62 | 63 | ### Workflows 64 | 65 | This kit includes a number of workflow templates for integrating [GitHub Actions](https://docs.github.com/en/actions) 66 | into your library or project development process. To add these to your repo simply copy the 67 | workflows into a **Template/.github/workflows/** directory. 68 | 69 | > [!TIP] 70 | > The [source files](src/.github) also include a configuration for Dependabot which 71 | > will help keep your dependencies and workflows updated. 72 | 73 | Below is a brief description of each workflow; see the links above for help with each tool. 74 | 75 | #### Deptrac 76 | 77 | *Requires **depfile.yaml*** 78 | 79 | Deptrac is a "dependency tracing" tool that allows developers to define which components should 80 | be allowed to access each other. This helps keep your project architecture logical and concise 81 | by enforcing the rules you set. For example, you may want to impose an MVC-style architecture 82 | by allowing a `Controller` to use any `Model` but not vice-versa. 83 | 84 | #### Infection 85 | 86 | *Requires **infection.json.dist*** 87 | 88 | Just because your tests reach a high level of code coverage does not mean they are comprehensive. 89 | Mutation Testing is a way of gauging the *quality* of your unit tests. A silly example: your 90 | code has an increment function with a single unit test for 100% coverage: 91 | 92 | ```php 93 | function increment(int $num1, int $num2): int 94 | { 95 | return $num1 + $num2; 96 | } 97 | 98 | function testIncrementWithZero() 99 | { 100 | $result = increment(42, 0); 101 | $this->assertSame(42, $result); 102 | } 103 | ``` 104 | 105 | Infection will re-run your unit test against "mutated" versions of your code that *should* 106 | cause failures and report "escaped mutations" when they still pass. In this example, Infection 107 | mutates your `increment()` function to use `-` instead of `+`, but since your test case 108 | still asserts `42` as the result it is considered an "escape" and you should plan to add 109 | more tests. 110 | 111 | #### PHPCPD 112 | 113 | PHP Copy-Paste Detector analyzes your code and reports when there are blocks of duplicate code 114 | more than a certain number of lines long (default: 5). In most cases this is a sign of poor 115 | code structure and an opportunity to consolidate classes or functions. 116 | 117 | #### PHP CS Fixer 118 | 119 | PHP CS Fixer is used to enforce coding standards. Once the rules are defined in the config file 120 | the workflow will check your code against the definitions and fail for any deviance. 121 | 122 | #### PHPStan 123 | 124 | *Requires **phpstan.neon.dist*** 125 | 126 | Static analysis is a major factor in catching bugs and issues before they happen. PHPStan will 127 | analyze your code for mistakes based on the configuration supplied. 128 | 129 | #### PHPUnit 130 | 131 | *Requires **phpunit.xml.dist*** 132 | 133 | Unit testing automates running your code through all the possible scenarios before putting it 134 | into use in production. PHPUnit is a highly-configurable framework and suite for writing and 135 | running unit tests. This workflow also configures PHPUnit to report on code coverage and 136 | upload the results to [Coveralls.io](https://coveralls.io) (you will need a free account, 137 | but it is also fine to use this workflow without Coveralls). 138 | 139 | #### Rector 140 | 141 | *Requires **rector.php*** 142 | 143 | Rector provides automated refactoring of code, allowing you to make sweeping updates based on 144 | predefined rulesets. Rector can be highly opinionated based on its configuration file (**rector.php**) 145 | so be sure to read the documentation and figure out the best fit for you. This workflow performs 146 | a "dry run" to check for any changes that Rector would have made and fail if there are matches. 147 | 148 | > [!NOTE] 149 | > Rector updates rules all the time, so you may want to lock your repo to 150 | > the latest known working version of Rector to prevent unexpected failures. 151 | > Using pinned version in `composer.json` and update it with dependabot is the 152 | > best practice. 153 | 154 | #### Unused 155 | 156 | Composer Unused does one thing: checks that your code actually uses the dependencies you 157 | have included via Composer. It can be easy to forget to update your **composer.json** when 158 | your code drops a dependency, so this workflow will help track those down. 159 | 160 | ### Hosting with Vagrant 161 | 162 | > [!NOTE] 163 | > The `Vagrantfile.dist` is unmaintained. It might not work now. 164 | > Contributions are welcome. 165 | 166 | Virtualization is an effective way to test your webapp in the environment you 167 | plan to deploy on, even if you develop on a different one. 168 | Even if you are using the same platform for both, virtualization provides an 169 | isolated environment for testing. 170 | 171 | The codebase comes with a **src/Template/Vagrantfile.dist**, that can be copied to **Vagrantfile** 172 | and tailored for your system, for instance enabling access to specific database or caching engines. 173 | 174 | #### Setting Up 175 | 176 | It assumes that you have installed [VirtualBox](https://www.virtualbox.org/wiki/Downloads) and 177 | [Vagrant](https://www.vagrantup.com/downloads.html) 178 | for your platform. 179 | 180 | The Vagrant configuration file assumes you have set up a [ubuntu/bionic64 Vagrant box](https://app.vagrantup.com/ubuntu/boxes/bionic64) on your system: 181 | 182 | ```console 183 | > vagrant box add ubuntu/bionic64 184 | ``` 185 | 186 | #### Testing 187 | 188 | Once set up, you can then launch your webapp inside a VM, with the command: 189 | 190 | ```console 191 | > vagrant up 192 | ``` 193 | 194 | Your webapp will be accessible at http://localhost:8080, with the code coverage 195 | report for your build at http://localhost:8081 and the user guide for 196 | it at http://localhost:8082. 197 | 198 | ## Example Files 199 | 200 | Besides the template files, this repo includes some examples for integrating CodeIgniter 201 | with other third-party resources. These files (in **Examples/**) may change over time and 202 | should not be relied on for anything more than a reference for your own code. 203 | -------------------------------------------------------------------------------- /src/Template/Vagrantfile.dist: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # https://github.com/hashicorp/vagrant/issues/9442#issuecomment-374785457 5 | unless Vagrant::DEFAULT_SERVER_URL.frozen? 6 | Vagrant::DEFAULT_SERVER_URL.replace('https://vagrantcloud.com') 7 | end 8 | 9 | Vagrant.configure("2") do |config| 10 | # VM Box 11 | config.vm.box = "ubuntu/bionic64" 12 | # Automatic box update checking 13 | config.vm.box_check_update = true 14 | 15 | # CodeIgniter virtual host 16 | config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" 17 | # Code Coverage virtual host 18 | config.vm.network "forwarded_port", guest: 81, host: 8081, host_ip: "127.0.0.1" 19 | # User Guide virtual host 20 | config.vm.network "forwarded_port", guest: 82, host: 8082, host_ip: "127.0.0.1" 21 | # MySQL server 22 | #config.vm.network "forwarded_port", guest: 3306, host: 3307, host_ip: "127.0.0.1" 23 | # PostgreSQL server 24 | #config.vm.network "forwarded_port", guest: 5432, host: 5433, host_ip: "127.0.0.1" 25 | # Memcached server 26 | #config.vm.network "forwarded_port", guest: 11211, host: 11212, host_ip: "127.0.0.1" 27 | # Redis server 28 | #config.vm.network "forwarded_port", guest: 6379, host: 6380, host_ip: "127.0.0.1" 29 | 30 | # Add "192.168.10.10 ${VIRTUALHOST}" in your host file to access by domain 31 | #config.vm.network "private_network", ip: "192.168.10.10" 32 | 33 | # Same path set in the $CODEIGNITER_PATH Provision 34 | # "virtualbox" type allow auto-sync host to guest and guest to host 35 | # but chmod does not work... tests will fail. 36 | # Default rsync__args except "--copy-links", to allow phpunit correctly works by symlink 37 | config.vm.synced_folder ".", "/var/www/codeigniter", type: "rsync", rsync__args: ["--verbose", "--archive", "--delete", "-z"] 38 | 39 | # Provider-specific configuration 40 | config.vm.provider "virtualbox" do |vb| 41 | # Display the VirtualBox GUI when booting the machine 42 | vb.gui = false 43 | # Customize the amount of memory on the VM: 44 | vb.memory = "1024" 45 | end 46 | 47 | # Provision 48 | config.vm.provision "shell", inline: <<-SHELL 49 | MYSQL_ROOT_PASS="password" 50 | PGSQL_ROOT_PASS="password" 51 | VIRTUALHOST="localhost" 52 | CODEIGNITER_PATH="/var/www/codeigniter" 53 | PHP_VERSION=7.4 54 | PGSQL_VERSION=10 55 | #APT_PROXY="192.168.10.1:3142" 56 | 57 | grep -q "127.0.0.1 ${VIRTUALHOST}" /etc/hosts || echo "127.0.0.1 ${VIRTUALHOST}" >> /etc/hosts 58 | 59 | # Creates a swap file if necessary 60 | RAM=`awk '/MemTotal/ {print $2}' /proc/meminfo` 61 | if [ $RAM -lt 1000000 ] && [ ! -f /swap/swapfile ]; then 62 | echo "================================================================================" 63 | echo "Adding swap" 64 | echo "================================================================================" 65 | echo "This process may take a few minutes. Please wait..." 66 | mkdir /swap 67 | dd if=/dev/zero of=/swap/swapfile bs=1024 count=1000000 68 | chmod 600 /swap/swapfile 69 | mkswap /swap/swapfile 70 | swapon /swap/swapfile 71 | echo "/swap/swapfile swap swap defaults 0 0" >> /etc/fstab 72 | echo "Done." 73 | fi 74 | 75 | # Prepare to use APT Proxy 76 | if [ ! -z $APT_PROXY ]; then 77 | if [ ! -f /etc/apt/sources.list-origin ]; then 78 | cp /etc/apt/sources.list /etc/apt/sources.list-origin 79 | fi 80 | sed -i "s/archive.ubuntu.com/${APT_PROXY}/" /etc/apt/sources.list 81 | sed -i "s/security.ubuntu.com/${APT_PROXY}/" /etc/apt/sources.list 82 | fi 83 | 84 | export DEBIAN_FRONTEND=noninteractive 85 | 86 | echo "================================================================================" 87 | echo "Updating and Installing Required Packages" 88 | echo "================================================================================" 89 | 90 | add-apt-repository ppa:ondrej/php 91 | 92 | apt-get update 93 | 94 | debconf-set-selections <<< "mysql-server mysql-server/root_password password ${MYSQL_ROOT_PASS}" 95 | debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ${MYSQL_ROOT_PASS}" 96 | 97 | apt-get install -y \ 98 | php$PHP_VERSION apache2 composer \ 99 | php-intl php-mbstring php-xml php-zip php-xdebug \ 100 | php-mysql mysql-server mysql-client \ 101 | php-pgsql postgresql-$PGSQL_VERSION \ 102 | php-sqlite3 sqlite3 \ 103 | php-memcached memcached \ 104 | php-redis redis-server \ 105 | php-curl curl \ 106 | php-gd php-imagick \ 107 | python-pip 108 | 109 | pip install sphinx sphinxcontrib-phpdomain 110 | 111 | apt-get autoclean 112 | 113 | echo "================================================================================" 114 | echo "Preparing User Guide" 115 | echo "================================================================================" 116 | 117 | cd "${CODEIGNITER_PATH}/user_guide_src/cilexer" 118 | python setup.py install 119 | cd .. 120 | make html 121 | 122 | echo "================================================================================" 123 | echo "Configuring Databases" 124 | echo "================================================================================" 125 | 126 | sed -i "s/^bind-address/#bind-address/" /etc/mysql/mysql.conf.d/mysqld.cnf 127 | mysql -e "CREATE DATABASE IF NOT EXISTS codeigniter COLLATE 'utf8_general_ci'; 128 | UPDATE mysql.user SET Host='%' WHERE user='root'; 129 | GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; 130 | FLUSH PRIVILEGES;" -uroot -p$MYSQL_ROOT_PASS 131 | systemctl restart mysql 132 | 133 | sed -i "s/^#listen_addresses = 'localhost'/listen_addresses = '*'/" /etc/postgresql/$PGSQL_VERSION/main/postgresql.conf 134 | grep -q "host all root all md5" /etc/postgresql/$PGSQL_VERSION/main/pg_hba.conf || echo "host all root all md5" >> /etc/postgresql/$PGSQL_VERSION/main/pg_hba.conf 135 | sudo -u postgres psql -tc "SELECT 1 FROM pg_roles WHERE rolname='root'" | grep -q 1 || sudo -u postgres psql -c "CREATE ROLE root WITH SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN" 136 | sudo -u postgres psql -c "ALTER ROLE root WITH PASSWORD '${PGSQL_ROOT_PASS}'" 137 | sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='codeigniter'" | grep -q 1 ||sudo -u postgres psql -c "CREATE DATABASE codeigniter" 138 | sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE codeigniter TO root" 139 | systemctl restart postgresql 140 | 141 | echo "================================================================================" 142 | echo "Configuring Memcached and Redis" 143 | echo "================================================================================" 144 | 145 | sed -i "s/^bind 127.0.0.1/#bind 127.0.0.1/" /etc/redis/redis.conf 146 | sed -i "s/^protected-mode yes/protected-mode no/" /etc/redis/redis.conf 147 | sed -i "s/^-l 127.0.0.1/#-l 127.0.0.1/" /etc/memcached.conf 148 | systemctl restart redis 149 | systemctl restart memcached 150 | 151 | echo "================================================================================" 152 | echo "Configuring Virtual Hosts" 153 | echo "================================================================================" 154 | 155 | mkdir -p "${CODEIGNITER_PATH}/build/coverage-html" 156 | mkdir -p "${CODEIGNITER_PATH}/public" 157 | mkdir -p "${CODEIGNITER_PATH}/user_guide_src/build/html" 158 | mkdir -p "${CODEIGNITER_PATH}/writable/apache" 159 | chown -R vagrant:vagrant $CODEIGNITER_PATH 160 | 161 | # Creates a symlink in the user home 162 | if [ ! -d /home/vagrant/codeigniter ]; then 163 | ln -s $CODEIGNITER_PATH /home/vagrant/codeigniter 164 | fi 165 | 166 | sed -i "s/APACHE_RUN_USER=www-data/APACHE_RUN_USER=vagrant/" /etc/apache2/envvars 167 | sed -i "s/APACHE_RUN_GROUP=www-data/APACHE_RUN_GROUP=vagrant/" /etc/apache2/envvars 168 | grep -q "Listen 81" /etc/apache2/ports.conf || sed -i "s/^Listen 80/Listen 80\\nListen 81\\nListen 82/" /etc/apache2/ports.conf 169 | sed -i "s/^display_errors = Off/display_errors = On/" /etc/php/7.4/apache2/php.ini 170 | sed -i "s/^display_startup_errors = Off/display_startup_errors = On/" /etc/php/7.4/apache2/php.ini 171 | 172 | echo "ServerName ${VIRTUALHOST} 173 | 174 | DirectoryIndex index.html index.php 175 | Options All 176 | AllowOverride All 177 | 178 | 179 | ServerAdmin vagrant@localhost 180 | DocumentRoot ${CODEIGNITER_PATH}/public 181 | ErrorLog ${CODEIGNITER_PATH}/writable/apache/error.log 182 | CustomLog ${CODEIGNITER_PATH}/writable/apache/custom.log combined 183 | 184 | 185 | DocumentRoot ${CODEIGNITER_PATH}/build/coverage-html 186 | 187 | 188 | DocumentRoot ${CODEIGNITER_PATH}/user_guide_src/build/html 189 | 190 | " > /etc/apache2/sites-available/codeigniter.conf 191 | 192 | a2enmod rewrite 193 | a2dissite 000-default.conf 194 | a2ensite codeigniter.conf 195 | systemctl restart apache2 196 | 197 | echo "================================================================================" 198 | echo "Services Status" 199 | echo "================================================================================" 200 | service --status-all 201 | 202 | SHELL 203 | end 204 | --------------------------------------------------------------------------------