diff --git a/.gitattributes b/.gitattributes index c956eb4b9..4c3b0299d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -11,4 +11,9 @@ /ecs.php export-ignore /php_cs.php.dist export-ignore /phpbench.json export-ignore -/sonar-project.properties export-ignore \ No newline at end of file +/sonar-project.properties export-ignore +/ecs.php export-ignore +/CHANGELOG.md export-ignore +/release-commit.php export-ignore +/release-please-manifest.json export-ignore +/release-please-config.json export-ignore \ No newline at end of file diff --git a/.github/workflows/php70.yml b/.github/workflows/php70.yml deleted file mode 100644 index ae33f2f72..000000000 --- a/.github/workflows/php70.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Build PHP 7.0 - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - test: - runs-on: ${{ matrix.os }} - services: - sql.data: - image: mcr.microsoft.com/mssql/server:2019-latest - env: - SA_PASSWORD: 1234567890@Eu - ACCEPT_EULA: Y - MSSQL_PID: Express - ports: - - "1433:1433" - strategy: - fail-fast: true - matrix: - os: [ ubuntu-latest ] - php: [7.0] - - name: PHP${{matrix.php}} - ${{matrix.os}} - - steps: - - name: Clone Repo - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mysqli, mbstring, sqlsrv - tools: phpunit:5.7.27, composer - - - name: Shutdown Ubuntu MySQL - run: sudo service mysql stop - - - name: Set up MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql version: '5.7' - mysql database: 'testing_db' - mysql root password: 123456 - mysql user: 'root' - mysql password: 123456 - - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do - sleep 1 - done - - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - - - name: Install Dependencies - run: composer install --prefer-dist --no-interaction --no-dev - - - name: Execute Tests - run: phpunit - - - name: CodeCov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/php71.yml b/.github/workflows/php71.yml deleted file mode 100644 index cb20b2697..000000000 --- a/.github/workflows/php71.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Build PHP 7.1 - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - test: - runs-on: ${{ matrix.os }} - services: - sql.data: - image: mcr.microsoft.com/mssql/server:2019-latest - env: - SA_PASSWORD: 1234567890@Eu - ACCEPT_EULA: Y - MSSQL_PID: Express - ports: - - "1433:1433" - strategy: - fail-fast: true - matrix: - os: [ ubuntu-latest ] - php: [7.1] - - name: PHP${{matrix.php}} - ${{matrix.os}} - - steps: - - name: Clone Repo - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mysqli, mbstring, sqlsrv - tools: phpunit:5.7.27, composer - - - name: Shutdown Ubuntu MySQL - run: sudo service mysql stop - - - name: Set up MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql version: '5.7' - mysql database: 'testing_db' - mysql root password: 123456 - mysql user: 'root' - mysql password: 123456 - - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do - sleep 1 - done - - - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - - - name: Install Dependencies - run: composer install --prefer-dist --no-interaction --no-dev - - - name: Execute Tests - run: phpunit - - - name: CodeCov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/php72.yml b/.github/workflows/php72.yml deleted file mode 100644 index 7cfa8e832..000000000 --- a/.github/workflows/php72.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Build PHP 7.2 - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - test: - runs-on: ${{ matrix.os }} - services: - sql.data: - image: mcr.microsoft.com/mssql/server:2019-latest - env: - SA_PASSWORD: 1234567890@Eu - ACCEPT_EULA: Y - MSSQL_PID: Express - ports: - - "1433:1433" - strategy: - fail-fast: true - matrix: - os: [ ubuntu-latest ] - php: [7.2] - - name: PHP${{matrix.php}} - ${{matrix.os}} - - steps: - - name: Clone Repo - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mysqli, mbstring, sqlsrv - tools: phpunit:8.5.13 - - - name: Shutdown Ubuntu MySQL - run: sudo service mysql stop - - - name: Set up MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql version: '5.7' - mysql database: 'testing_db' - mysql root password: 123456 - mysql user: 'root' - mysql password: 123456 - - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do - sleep 1 - done - - - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - - - name: Install Dependencies - run: composer install --prefer-dist --no-interaction --no-dev - - - name: Execute Tests - run: phpunit - - - name: CodeCov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - - diff --git a/.github/workflows/php73.yml b/.github/workflows/php73.yml deleted file mode 100644 index 8bce6f656..000000000 --- a/.github/workflows/php73.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Build PHP 7.3 - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - test: - runs-on: ${{ matrix.os }} - services: - sql.data: - image: mcr.microsoft.com/mssql/server:2019-latest - env: - SA_PASSWORD: 1234567890@Eu - ACCEPT_EULA: Y - MSSQL_PID: Express - ports: - - "1433:1433" - strategy: - fail-fast: true - matrix: - os: [ ubuntu-latest ] - php: [7.3] - - name: PHP${{matrix.php}} - ${{matrix.os}} - - steps: - - name: Clone Repo - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mysqli, mbstring, sqlsrv - tools: phpunit:8.5.13 - - - name: Shutdown Ubuntu MySQL - run: sudo service mysql stop - - - name: Set up MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql version: '5.7' - mysql database: 'testing_db' - mysql root password: 123456 - mysql user: 'root' - mysql password: 123456 - - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do - sleep 1 - done - - - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - - - name: Install Dependencies - run: composer install --prefer-dist --no-interaction --no-dev - - - name: Execute Tests - run: phpunit - - - name: CodeCov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - - - name: SonarCloud - uses: SonarSource/sonarcloud-github-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - diff --git a/.github/workflows/php74.yml b/.github/workflows/php74.yml deleted file mode 100644 index 2761957fc..000000000 --- a/.github/workflows/php74.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Build PHP 7.4 - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - test: - runs-on: ${{ matrix.os }} - services: - sql.data: - image: mcr.microsoft.com/mssql/server:2019-latest - env: - SA_PASSWORD: 1234567890@Eu - ACCEPT_EULA: Y - MSSQL_PID: Express - ports: - - "1433:1433" - strategy: - fail-fast: true - matrix: - os: [ ubuntu-latest ] - php: [7.4] - - name: PHP${{matrix.php}} - ${{matrix.os}} - - steps: - - name: Clone Repo - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mysqli, mbstring, sqlsrv - tools: phpunit:8.5.13 - - - name: Shutdown Ubuntu MySQL - run: sudo service mysql stop - - - name: Set up MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql version: '5.7' - mysql database: 'testing_db' - mysql root password: 123456 - mysql user: 'root' - mysql password: 123456 - - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do - sleep 1 - done - - - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - - - name: Install Dependencies - run: composer install --prefer-dist --no-interaction --no-dev - - - name: Execute Tests - run: phpunit - - - name: CodeCov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - - - diff --git a/.github/workflows/php80.yml b/.github/workflows/php80.yml index 4bb63d38e..20c072522 100644 --- a/.github/workflows/php80.yml +++ b/.github/workflows/php80.yml @@ -2,9 +2,9 @@ name: Build PHP 8.0 on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: test: @@ -13,7 +13,7 @@ jobs: sql.data: image: mcr.microsoft.com/mssql/server:2019-latest env: - SA_PASSWORD: 1234567890@Eu + SA_PASSWORD: ${{ secrets.SA_PASSWORD }} ACCEPT_EULA: Y MSSQL_PID: Express ports: @@ -45,24 +45,29 @@ jobs: with: mysql version: '5.7' mysql database: 'testing_db' - mysql root password: 123456 + mysql root password: ${{ secrets.MYSQL_ROOT_PASSWORD }} mysql user: 'root' - mysql password: 123456 + mysql password: ${{ secrets.MYSQL_ROOT_PASSWORD }} - name: Wait for MySQL run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do + while ! mysqladmin ping --host=127.0.0.1 --password=${{ secrets.MYSQL_ROOT_PASSWORD }} --silent; do sleep 1 done - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc + curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt update + sudo apt install mssql-tools18 + /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P ${{ secrets.SA_PASSWORD }} -Q 'create database testing_db' -C - name: Install Dependencies run: composer install --prefer-dist --no-interaction --no-dev - name: Execute Tests - run: phpunit + run: phpunit --configuration tests/phpunit.xml - name: CodeCov uses: codecov/codecov-action@v4 diff --git a/.github/workflows/php81.yml b/.github/workflows/php81.yml index 1d2db2034..dba9551d4 100644 --- a/.github/workflows/php81.yml +++ b/.github/workflows/php81.yml @@ -2,18 +2,18 @@ name: Build PHP 8.1 on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: test: - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }} services: sql.data: image: mcr.microsoft.com/mssql/server:2019-latest env: - SA_PASSWORD: 1234567890@Eu + SA_PASSWORD: ${{ secrets.SA_PASSWORD }} ACCEPT_EULA: Y MSSQL_PID: Express ports: @@ -45,24 +45,29 @@ jobs: with: mysql version: '5.7' mysql database: 'testing_db' - mysql root password: 123456 + mysql root password: ${{ secrets.MYSQL_ROOT_PASSWORD }} mysql user: 'root' - mysql password: 123456 + mysql password: ${{ secrets.MYSQL_ROOT_PASSWORD }} - name: Wait for MySQL run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do + while ! mysqladmin ping --host=127.0.0.1 --password=${{ secrets.MYSQL_ROOT_PASSWORD }} --silent; do sleep 1 done - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc + curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt update + sudo apt install mssql-tools18 + /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P ${{ secrets.SA_PASSWORD }} -Q 'create database testing_db' -C + - name: Install Dependencies run: composer install --prefer-dist --no-interaction --no-dev - name: Execute Tests - run: phpunit + run: phpunit --configuration tests/phpunit.xml - name: CodeCov uses: codecov/codecov-action@v4 diff --git a/.github/workflows/php82.yml b/.github/workflows/php82.yml index 0cbde10be..d96db4b94 100644 --- a/.github/workflows/php82.yml +++ b/.github/workflows/php82.yml @@ -2,9 +2,9 @@ name: Build PHP 8.2 on: push: - branches: [ master, dev ] + branches: [ main, dev ] pull_request: - branches: [ master ] + branches: [ main ] jobs: @@ -14,7 +14,7 @@ jobs: sql.data: image: mcr.microsoft.com/mssql/server:2019-latest env: - SA_PASSWORD: 1234567890@Eu + SA_PASSWORD: ${{ secrets.SA_PASSWORD }} ACCEPT_EULA: Y MSSQL_PID: Express ports: @@ -28,8 +28,8 @@ jobs: name: PHP${{matrix.php}} - ${{matrix.os}} steps: - - name: Clone Repo - uses: actions/checkout@v3 + - name: Clone Repos + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -46,24 +46,29 @@ jobs: with: mysql version: '5.7' mysql database: 'testing_db' - mysql root password: 123456 + mysql root password: ${{ secrets.MYSQL_ROOT_PASSWORD }} mysql user: 'root' - mysql password: 123456 + mysql password: ${{ secrets.MYSQL_ROOT_PASSWORD }} - name: Wait for MySQL run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do + while ! mysqladmin ping --host=127.0.0.1 --password=${{ secrets.MYSQL_ROOT_PASSWORD }} --silent; do sleep 1 done - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc + curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt update + sudo apt install mssql-tools18 + /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P ${{ secrets.SA_PASSWORD }} -Q 'create database testing_db' -C + - name: Install Dependencies run: composer install --prefer-dist --no-interaction --no-dev - name: Execute Tests - run: phpunit + run: phpunit --configuration tests/phpunit.xml - name: CodeCov uses: codecov/codecov-action@v4 diff --git a/.github/workflows/php83.yml b/.github/workflows/php83.yml index 20b2f47e2..f4c23a0cf 100644 --- a/.github/workflows/php83.yml +++ b/.github/workflows/php83.yml @@ -2,9 +2,9 @@ name: Build PHP 8.3 on: push: - branches: [ master, dev ] + branches: [ main, dev ] pull_request: - branches: [ master ] + branches: [ main, dev ] jobs: @@ -14,7 +14,7 @@ jobs: sql.data: image: mcr.microsoft.com/mssql/server:2019-latest env: - SA_PASSWORD: 1234567890@Eu + SA_PASSWORD: ${{ secrets.SA_PASSWORD }} ACCEPT_EULA: Y MSSQL_PID: Express ports: @@ -46,25 +46,30 @@ jobs: with: mysql version: '5.7' mysql database: 'testing_db' - mysql root password: 123456 + mysql root password: ${{ secrets.MYSQL_ROOT_PASSWORD }} mysql user: 'root' - mysql password: 123456 + mysql password: ${{ secrets.MYSQL_ROOT_PASSWORD }} - name: Wait for MySQL run: | - while ! mysqladmin ping --host=127.0.0.1 --password=123456 --silent; do + while ! mysqladmin ping --host=127.0.0.1 --password=${{ secrets.MYSQL_ROOT_PASSWORD }} --silent; do sleep 1 done - name: Setup MSSQL - run: sqlcmd -S localhost -U SA -P 1234567890@Eu -Q 'create database testing_db' - + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc + curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt update + sudo apt install mssql-tools18 + /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P ${{ secrets.SA_PASSWORD }} -Q 'create database testing_db' -C + - name: Install Dependencies run: composer install --prefer-dist --no-interaction --no-dev - - - name: Execute Tests - run: phpunit + - name: Execute Tests + run: phpunit --configuration tests/phpunit.xml + - name: CodeCov uses: codecov/codecov-action@v4 with: @@ -118,5 +123,36 @@ jobs: - name: Benchmarking run: phpbench run tests/webfiori/benchmark --report=default + release_staging: + name: Prepare Beta Release Branch / Publish Release + needs: + - "test" + - "coding_standards_check" + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev' + steps: + - uses: actions/checkout@v4 + - uses: google-github-actions/release-please-action@v4 + with: + release-type: php + target-branch: dev + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + token: ${{ secrets.GITHUB_TOKEN }} + release_prod: + name: Prepare Production Release Branch / Publish Release + needs: + - "test" + - "coding_standards_check" + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v4 + - uses: google-github-actions/release-please-action@v4 + with: + release-type: php + target-branch: master + config-file: release-please-config.json + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/php84.yml b/.github/workflows/php84.yml new file mode 100644 index 000000000..ddbb3e05e --- /dev/null +++ b/.github/workflows/php84.yml @@ -0,0 +1,127 @@ +name: Build PHP 8.4 + +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main ] + +jobs: + + test: + runs-on: ${{ matrix.os }} + services: + sql.data: + image: mcr.microsoft.com/mssql/server:2019-latest + env: + SA_PASSWORD: ${{ secrets.SA_PASSWORD }} + ACCEPT_EULA: Y + MSSQL_PID: Express + ports: + - "1433:1433" + strategy: + fail-fast: true + matrix: + os: [ ubuntu-latest ] + php: [8.4] + + name: PHP${{matrix.php}} - ${{matrix.os}} + + steps: + - name: Clone Repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mysqli, mbstring, sqlsrv + tools: phpunit:9.5.20, composer, symplify/easy-coding-standard:12.0.6, phpbench/phpbench:1.2.14 + + - name: Shutdown Ubuntu MySQL + run: sudo service mysql stop + + - name: Set up MySQL + uses: mirromutth/mysql-action@v1.1 + with: + mysql version: '5.7' + mysql database: 'testing_db' + mysql root password: ${{ secrets.MYSQL_ROOT_PASSWORD }} + mysql user: 'root' + mysql password: ${{ secrets.MYSQL_ROOT_PASSWORD }} + + - name: Wait for MySQL + run: | + while ! mysqladmin ping --host=127.0.0.1 --password=${{ secrets.MYSQL_ROOT_PASSWORD }} --silent; do + sleep 1 + done + + - name: Setup MSSQL + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc + curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt update + sudo apt install mssql-tools18 + /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P ${{ secrets.SA_PASSWORD }} -Q 'create database testing_db' -C + + - name: Install Dependencies + run: composer install --prefer-dist --no-interaction --no-dev + + - name: Execute Tests + run: phpunit --configuration tests/phpunit.xml + + - name: CodeCov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + coding_standards_check: + name: "Coding Standards Check" + needs: + - "test" + + runs-on: "ubuntu-latest" + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + extensions: "mbstring" + tools: composer, symplify/easy-coding-standard:12.0.6 + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: Install Dependencies + run: composer install --prefer-dist --no-interaction --no-dev + + - name: "Check Style" + run: "ecs check" + + benchmarking: + needs: + - "test" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ ubuntu-latest ] + php: [8.0,8.1,8.2] + steps: + - name: Clone Repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring + tools: composer, phpbench/phpbench + + - name: Install Dependencies + run: composer install --prefer-dist --no-interaction --no-dev + + - name: Benchmarking + run: phpbench run tests/webfiori/benchmark --report=default + + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..8fa8d739f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,958 @@ +# Changelog + +## [3.0.0-Beta.26](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.26...v3.0.0-Beta.26) (2025-04-07) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Fully Implemented Migrations Writer ([ac42d3f](https://github.com/WebFiori/framework/commit/ac42d3fe853fa259382bc05214483ac4146c341e)) +* Rollback of Migrations ([078c94f](https://github.com/WebFiori/framework/commit/078c94f98b090176b22e2354e326be7153258663)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* Buffer Theme Components as They Might be HTML ([d803352](https://github.com/WebFiori/framework/commit/d803352bb1d97436f242807879e665c24015845a)) +* Class Path ([98c6ee6](https://github.com/WebFiori/framework/commit/98c6ee64ca4164b04c9453ef6e16685ee5b8b176)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Correction to File Path ([df3eacf](https://github.com/WebFiori/framework/commit/df3eacfb150a43020794545f51ee1379256a46fc)) +* Created Class Path ([0592ed2](https://github.com/WebFiori/framework/commit/0592ed22403d5890f03279d3dec11f35ee968946)) +* Fix Assignment Issue ([34a522f](https://github.com/WebFiori/framework/commit/34a522ff53e8ad7bc8bc1287bc0c7595a4d7e254)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Create Migration with Defaults ([534845d](https://github.com/WebFiori/framework/commit/534845df19c2fe7f2bec8559b77e47576c04313a)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Registering Middleware ([6cc7ce1](https://github.com/WebFiori/framework/commit/6cc7ce1e39bf0aebfe0eb5ff91360e4c4ed04f05)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Undefined Constant ([d605a5b](https://github.com/WebFiori/framework/commit/d605a5be85cd8623a2114b8c0756372c82bd7c9b)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* Show Exception on Initialization ([2341841](https://github.com/WebFiori/framework/commit/2341841f9718beb99330f8529e01600d61acfecf)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([60aa746](https://github.com/WebFiori/framework/commit/60aa746bf39ccf7cbdda5bd9c24a6ed408d2732c)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* **dev:** release 3.0.0-Beta.17 ([a3e5983](https://github.com/WebFiori/framework/commit/a3e598305d75f2fa87d6148520d88f4235a53253)) +* **dev:** release 3.0.0-Beta.18 ([a994097](https://github.com/WebFiori/framework/commit/a994097c021b5575cab7f077f8d730736c3c1bbe)) +* **dev:** release 3.0.0-Beta.19 ([e917eb5](https://github.com/WebFiori/framework/commit/e917eb5ccd18ee8223a73ec9a2ac499a281f5764)) +* **dev:** release 3.0.0-Beta.19 ([5497825](https://github.com/WebFiori/framework/commit/549782509f23be3a90375debe0319b16e549a3aa)) +* **dev:** release 3.0.0-Beta.20 ([7afdb92](https://github.com/WebFiori/framework/commit/7afdb92618bdbc6e11adb29b001d9a9d0a8f4809)) +* **dev:** release 3.0.0-Beta.21 ([eef1270](https://github.com/WebFiori/framework/commit/eef1270f51089065f04e7f8843d9944d368a774e)) +* **dev:** release 3.0.0-Beta.22 ([0e428f0](https://github.com/WebFiori/framework/commit/0e428f03ef510cb9c33bbf940c92b12ff702c8dd)) +* **dev:** release 3.0.0-Beta.23 ([0f70d8b](https://github.com/WebFiori/framework/commit/0f70d8bd2bcfef80bd677d242c79be3cb23a122f)) +* **dev:** release 3.0.0-Beta.24 ([d77357f](https://github.com/WebFiori/framework/commit/d77357f5b220f66e34334703c83c86c66b7fb9ae)) +* **dev:** release 3.0.0-Beta.25 ([e94ea85](https://github.com/WebFiori/framework/commit/e94ea85e94e941bf670954873e06723bf26dc5f3)) +* **dev:** release 3.1.0-Beta.14 ([ba5a5e3](https://github.com/WebFiori/framework/commit/ba5a5e30b4033d3f2486cbab578545aadbed67b0)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* Fix target Branch ([a419a3e](https://github.com/WebFiori/framework/commit/a419a3e29bb416be328b19f1489788d51ebbcd4e)) +* Libraries Bump Up ([d79a44a](https://github.com/WebFiori/framework/commit/d79a44a7d2c3e062974031081c3f5dbc24812b56)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* release 3.0.0-Beta.20 ([ddcf6b0](https://github.com/WebFiori/framework/commit/ddcf6b03010a0f011112bfa789f446b1949daaae)) +* release 3.0.0-Beta.20 ([c4fc053](https://github.com/WebFiori/framework/commit/c4fc053d45a5c8b24e963a625d0954c33af2c884)) +* release 3.0.0-Beta.20 ([6e72830](https://github.com/WebFiori/framework/commit/6e72830f0ec1f6943d84ee3266632d5c62e02832)) +* release v3.0.0-Beta.17 ([3c0c639](https://github.com/WebFiori/framework/commit/3c0c639a72f9dd08bec7a150e33af2bb18e9728a)) +* release v3.0.0-Beta.18 ([5a588eb](https://github.com/WebFiori/framework/commit/5a588eb52815889a8409a07a30c4e6f0defe3269)) +* release v3.0.0-Beta.19 ([e8d5314](https://github.com/WebFiori/framework/commit/e8d531433f6afada6684050013b4169b3d8d547b)) +* release v3.0.0-Beta.20 ([630f512](https://github.com/WebFiori/framework/commit/630f512c6a036e111a7b146cacbd7d72dfe0da06)) +* release v3.0.0-Beta.21 ([56f0bdc](https://github.com/WebFiori/framework/commit/56f0bdc7917faa97c8d3e4b73076d91213b85366)) +* release v3.0.0-Beta.22 ([4271914](https://github.com/WebFiori/framework/commit/4271914182564a0c917a185010933f7f76b86f5d)) +* release v3.0.0-Beta.23 ([3f74229](https://github.com/WebFiori/framework/commit/3f74229b2b38449330763f40883a6ce383eda504)) +* release v3.0.0-Beta.24 ([2870002](https://github.com/WebFiori/framework/commit/28700026d50339f1b3be07f21d801594d682b7b4)) +* release v3.0.0-Beta.25 ([7609472](https://github.com/WebFiori/framework/commit/76094722f597e943bf159b57368af5650d265af0)) +* release v3.0.0-Beta.25 ([77671f5](https://github.com/WebFiori/framework/commit/77671f5735902aef7fd25cf1dd6c5fa601ca7eca)) +* release v3.0.0-Beta.26 ([ab08f87](https://github.com/WebFiori/framework/commit/ab08f870c53cc8fc47212b579e077e8eef2fdc65)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Import ([4cd7cf3](https://github.com/WebFiori/framework/commit/4cd7cf313f836231c76b4533bacf7f7283589052)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Skeleton of Database Migrations Writer ([3b53f8e](https://github.com/WebFiori/framework/commit/3b53f8e33a89667df0479d146e7902db3b8d4d90)) +* Update composer.json ([08c60a8](https://github.com/WebFiori/framework/commit/08c60a878c48198f61b2149127113c359ca5f635)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Update Libraries Versions ([46f4d56](https://github.com/WebFiori/framework/commit/46f4d56aa8bc911393ed80d4e57368472dbcdd24)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated .gitattributes ([3b2334c](https://github.com/WebFiori/framework/commit/3b2334ce9b29d1be3b71049b3caf759a00c84724)) +* Updated App Version ([099d631](https://github.com/WebFiori/framework/commit/099d631e4c65de00fea4242cb8b5814dc6047113)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependences ([a160f0f](https://github.com/WebFiori/framework/commit/a160f0fcc7cb0b2570c9487090b2bcc3e0ad658e)) +* Updated Dependencies ([6936c18](https://github.com/WebFiori/framework/commit/6936c18cd1895df3ba101aaacfe4e599d39d59c4)) +* Updated Dependencies ([e48f333](https://github.com/WebFiori/framework/commit/e48f3336d8c0b1910e18b8baa5ea40be28c9e50d)) +* Updated Dependencies ([8284dc6](https://github.com/WebFiori/framework/commit/8284dc655e8e92aafc3fb6a1bd88861254da0fe1)) +* Updated Dependencies ([97bb7a2](https://github.com/WebFiori/framework/commit/97bb7a220a9c8a81fd70a4c2d80d891e4f4c7eb2)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies + Framework Version ([9f5dd93](https://github.com/WebFiori/framework/commit/9f5dd9374ebf818605b6ae4acff3d5b95237d1ff)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([ddaeca0](https://github.com/WebFiori/framework/commit/ddaeca02a7a192b49340fa15b4bd88b3d1f2dfab)) +* Updated Framework Version ([36b0f55](https://github.com/WebFiori/framework/commit/36b0f5514329db80bae9102094e402e240987260)) +* Updated Framework Version ([834782c](https://github.com/WebFiori/framework/commit/834782c8fd1ae846ffbaa72b8ee76e5fc7796f56)) +* Updated Framework Version ([7f84cf6](https://github.com/WebFiori/framework/commit/7f84cf65da991a63daccc2cb0896b78b98d578c5)) +* Updated Framework Version ([361e5d5](https://github.com/WebFiori/framework/commit/361e5d545a274043efceab4087d4db7769990d60)) +* Updated Framework Version ([783f4be](https://github.com/WebFiori/framework/commit/783f4be57869ae93eab8c0b49fe2ede5cc7fbba8)) +* Updated Framework Version ([0403027](https://github.com/WebFiori/framework/commit/0403027fc02bfbba13dd1c899ae073f43c925cd8)) +* Updated Framework Version ([f7c0f7f](https://github.com/WebFiori/framework/commit/f7c0f7f0dad2900d988b3866c2a035b0b1c10e7b)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Libraries Versions ([248d1da](https://github.com/WebFiori/framework/commit/248d1dab9efffd44ac5728d005dde58470d2fef1)) +* Updated Library Version ([c681042](https://github.com/WebFiori/framework/commit/c68104267d7abf876d2be93b3f7f2ea96697ca17)) +* Updated Release Please Config ([1a8b4e5](https://github.com/WebFiori/framework/commit/1a8b4e55f9a5496aac47e30228613d8a24068914)) +* Updated Version Number ([280f418](https://github.com/WebFiori/framework/commit/280f418df38a2eb019b6f2a5d0c1b8b8d00133d3)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.0.0-Beta.25](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.24...v3.0.0-Beta.25) (2025-02-04) + + +### Miscellaneous Chores + +* release v3.0.0-Beta.25 ([77671f5](https://github.com/WebFiori/framework/commit/77671f5735902aef7fd25cf1dd6c5fa601ca7eca)) +* Updated App Version ([099d631](https://github.com/WebFiori/framework/commit/099d631e4c65de00fea4242cb8b5814dc6047113)) + +## [3.0.0-Beta.24](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.23...v3.0.0-Beta.24) (2025-01-29) + + +### Bug Fixes + +* Show Exception on Initialization ([2341841](https://github.com/WebFiori/framework/commit/2341841f9718beb99330f8529e01600d61acfecf)) + + +### Miscellaneous Chores + +* release v3.0.0-Beta.24 ([2870002](https://github.com/WebFiori/framework/commit/28700026d50339f1b3be07f21d801594d682b7b4)) +* Update composer.json ([08c60a8](https://github.com/WebFiori/framework/commit/08c60a878c48198f61b2149127113c359ca5f635)) +* Updated Framework Version ([36b0f55](https://github.com/WebFiori/framework/commit/36b0f5514329db80bae9102094e402e240987260)) + +## [3.0.0-Beta.23](https://github.com/WebFiori/framework/compare/v3.0.1-Beta.22...v3.0.0-Beta.23) (2025-01-07) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* Buffer Theme Components as They Might be HTML ([d803352](https://github.com/WebFiori/framework/commit/d803352bb1d97436f242807879e665c24015845a)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Correction to File Path ([df3eacf](https://github.com/WebFiori/framework/commit/df3eacfb150a43020794545f51ee1379256a46fc)) +* Fix Assignment Issue ([34a522f](https://github.com/WebFiori/framework/commit/34a522ff53e8ad7bc8bc1287bc0c7595a4d7e254)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Registering Middleware ([6cc7ce1](https://github.com/WebFiori/framework/commit/6cc7ce1e39bf0aebfe0eb5ff91360e4c4ed04f05)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Undefined Constant ([d605a5b](https://github.com/WebFiori/framework/commit/d605a5be85cd8623a2114b8c0756372c82bd7c9b)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([60aa746](https://github.com/WebFiori/framework/commit/60aa746bf39ccf7cbdda5bd9c24a6ed408d2732c)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* **dev:** release 3.0.0-Beta.17 ([a3e5983](https://github.com/WebFiori/framework/commit/a3e598305d75f2fa87d6148520d88f4235a53253)) +* **dev:** release 3.0.0-Beta.18 ([a994097](https://github.com/WebFiori/framework/commit/a994097c021b5575cab7f077f8d730736c3c1bbe)) +* **dev:** release 3.0.0-Beta.19 ([e917eb5](https://github.com/WebFiori/framework/commit/e917eb5ccd18ee8223a73ec9a2ac499a281f5764)) +* **dev:** release 3.0.0-Beta.19 ([5497825](https://github.com/WebFiori/framework/commit/549782509f23be3a90375debe0319b16e549a3aa)) +* **dev:** release 3.0.0-Beta.20 ([7afdb92](https://github.com/WebFiori/framework/commit/7afdb92618bdbc6e11adb29b001d9a9d0a8f4809)) +* **dev:** release 3.0.0-Beta.21 ([eef1270](https://github.com/WebFiori/framework/commit/eef1270f51089065f04e7f8843d9944d368a774e)) +* **dev:** release 3.0.0-Beta.22 ([0e428f0](https://github.com/WebFiori/framework/commit/0e428f03ef510cb9c33bbf940c92b12ff702c8dd)) +* **dev:** release 3.1.0-Beta.14 ([ba5a5e3](https://github.com/WebFiori/framework/commit/ba5a5e30b4033d3f2486cbab578545aadbed67b0)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* Fix target Branch ([a419a3e](https://github.com/WebFiori/framework/commit/a419a3e29bb416be328b19f1489788d51ebbcd4e)) +* Libraries Bump Up ([d79a44a](https://github.com/WebFiori/framework/commit/d79a44a7d2c3e062974031081c3f5dbc24812b56)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* release 3.0.0-Beta.20 ([ddcf6b0](https://github.com/WebFiori/framework/commit/ddcf6b03010a0f011112bfa789f446b1949daaae)) +* release 3.0.0-Beta.20 ([c4fc053](https://github.com/WebFiori/framework/commit/c4fc053d45a5c8b24e963a625d0954c33af2c884)) +* release 3.0.0-Beta.20 ([6e72830](https://github.com/WebFiori/framework/commit/6e72830f0ec1f6943d84ee3266632d5c62e02832)) +* release v3.0.0-Beta.17 ([3c0c639](https://github.com/WebFiori/framework/commit/3c0c639a72f9dd08bec7a150e33af2bb18e9728a)) +* release v3.0.0-Beta.18 ([5a588eb](https://github.com/WebFiori/framework/commit/5a588eb52815889a8409a07a30c4e6f0defe3269)) +* release v3.0.0-Beta.19 ([e8d5314](https://github.com/WebFiori/framework/commit/e8d531433f6afada6684050013b4169b3d8d547b)) +* release v3.0.0-Beta.20 ([630f512](https://github.com/WebFiori/framework/commit/630f512c6a036e111a7b146cacbd7d72dfe0da06)) +* release v3.0.0-Beta.21 ([56f0bdc](https://github.com/WebFiori/framework/commit/56f0bdc7917faa97c8d3e4b73076d91213b85366)) +* release v3.0.0-Beta.22 ([4271914](https://github.com/WebFiori/framework/commit/4271914182564a0c917a185010933f7f76b86f5d)) +* release v3.0.0-Beta.23 ([3f74229](https://github.com/WebFiori/framework/commit/3f74229b2b38449330763f40883a6ce383eda504)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Import ([4cd7cf3](https://github.com/WebFiori/framework/commit/4cd7cf313f836231c76b4533bacf7f7283589052)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Update Libraries Versions ([46f4d56](https://github.com/WebFiori/framework/commit/46f4d56aa8bc911393ed80d4e57368472dbcdd24)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated .gitattributes ([3b2334c](https://github.com/WebFiori/framework/commit/3b2334ce9b29d1be3b71049b3caf759a00c84724)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependences ([a160f0f](https://github.com/WebFiori/framework/commit/a160f0fcc7cb0b2570c9487090b2bcc3e0ad658e)) +* Updated Dependencies ([e48f333](https://github.com/WebFiori/framework/commit/e48f3336d8c0b1910e18b8baa5ea40be28c9e50d)) +* Updated Dependencies ([8284dc6](https://github.com/WebFiori/framework/commit/8284dc655e8e92aafc3fb6a1bd88861254da0fe1)) +* Updated Dependencies ([97bb7a2](https://github.com/WebFiori/framework/commit/97bb7a220a9c8a81fd70a4c2d80d891e4f4c7eb2)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies + Framework Version ([9f5dd93](https://github.com/WebFiori/framework/commit/9f5dd9374ebf818605b6ae4acff3d5b95237d1ff)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([834782c](https://github.com/WebFiori/framework/commit/834782c8fd1ae846ffbaa72b8ee76e5fc7796f56)) +* Updated Framework Version ([7f84cf6](https://github.com/WebFiori/framework/commit/7f84cf65da991a63daccc2cb0896b78b98d578c5)) +* Updated Framework Version ([361e5d5](https://github.com/WebFiori/framework/commit/361e5d545a274043efceab4087d4db7769990d60)) +* Updated Framework Version ([783f4be](https://github.com/WebFiori/framework/commit/783f4be57869ae93eab8c0b49fe2ede5cc7fbba8)) +* Updated Framework Version ([0403027](https://github.com/WebFiori/framework/commit/0403027fc02bfbba13dd1c899ae073f43c925cd8)) +* Updated Framework Version ([f7c0f7f](https://github.com/WebFiori/framework/commit/f7c0f7f0dad2900d988b3866c2a035b0b1c10e7b)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Libraries Versions ([248d1da](https://github.com/WebFiori/framework/commit/248d1dab9efffd44ac5728d005dde58470d2fef1)) +* Updated Release Please Config ([1a8b4e5](https://github.com/WebFiori/framework/commit/1a8b4e55f9a5496aac47e30228613d8a24068914)) +* Updated Version Number ([280f418](https://github.com/WebFiori/framework/commit/280f418df38a2eb019b6f2a5d0c1b8b8d00133d3)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.0.0-Beta.22](https://github.com/WebFiori/framework/compare/v3.0.1-Beta.21...v3.0.0-Beta.22) (2024-12-24) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Correction to File Path ([df3eacf](https://github.com/WebFiori/framework/commit/df3eacfb150a43020794545f51ee1379256a46fc)) +* Fix Assignment Issue ([34a522f](https://github.com/WebFiori/framework/commit/34a522ff53e8ad7bc8bc1287bc0c7595a4d7e254)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Registering Middleware ([6cc7ce1](https://github.com/WebFiori/framework/commit/6cc7ce1e39bf0aebfe0eb5ff91360e4c4ed04f05)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Undefined Constant ([d605a5b](https://github.com/WebFiori/framework/commit/d605a5be85cd8623a2114b8c0756372c82bd7c9b)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([60aa746](https://github.com/WebFiori/framework/commit/60aa746bf39ccf7cbdda5bd9c24a6ed408d2732c)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* **dev:** release 3.0.0-Beta.17 ([a3e5983](https://github.com/WebFiori/framework/commit/a3e598305d75f2fa87d6148520d88f4235a53253)) +* **dev:** release 3.0.0-Beta.18 ([a994097](https://github.com/WebFiori/framework/commit/a994097c021b5575cab7f077f8d730736c3c1bbe)) +* **dev:** release 3.0.0-Beta.19 ([e917eb5](https://github.com/WebFiori/framework/commit/e917eb5ccd18ee8223a73ec9a2ac499a281f5764)) +* **dev:** release 3.0.0-Beta.19 ([5497825](https://github.com/WebFiori/framework/commit/549782509f23be3a90375debe0319b16e549a3aa)) +* **dev:** release 3.0.0-Beta.20 ([7afdb92](https://github.com/WebFiori/framework/commit/7afdb92618bdbc6e11adb29b001d9a9d0a8f4809)) +* **dev:** release 3.0.0-Beta.21 ([eef1270](https://github.com/WebFiori/framework/commit/eef1270f51089065f04e7f8843d9944d368a774e)) +* **dev:** release 3.1.0-Beta.14 ([ba5a5e3](https://github.com/WebFiori/framework/commit/ba5a5e30b4033d3f2486cbab578545aadbed67b0)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* Fix target Branch ([a419a3e](https://github.com/WebFiori/framework/commit/a419a3e29bb416be328b19f1489788d51ebbcd4e)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* release 3.0.0-Beta.20 ([ddcf6b0](https://github.com/WebFiori/framework/commit/ddcf6b03010a0f011112bfa789f446b1949daaae)) +* release 3.0.0-Beta.20 ([c4fc053](https://github.com/WebFiori/framework/commit/c4fc053d45a5c8b24e963a625d0954c33af2c884)) +* release 3.0.0-Beta.20 ([6e72830](https://github.com/WebFiori/framework/commit/6e72830f0ec1f6943d84ee3266632d5c62e02832)) +* release v3.0.0-Beta.17 ([3c0c639](https://github.com/WebFiori/framework/commit/3c0c639a72f9dd08bec7a150e33af2bb18e9728a)) +* release v3.0.0-Beta.18 ([5a588eb](https://github.com/WebFiori/framework/commit/5a588eb52815889a8409a07a30c4e6f0defe3269)) +* release v3.0.0-Beta.19 ([e8d5314](https://github.com/WebFiori/framework/commit/e8d531433f6afada6684050013b4169b3d8d547b)) +* release v3.0.0-Beta.20 ([630f512](https://github.com/WebFiori/framework/commit/630f512c6a036e111a7b146cacbd7d72dfe0da06)) +* release v3.0.0-Beta.21 ([56f0bdc](https://github.com/WebFiori/framework/commit/56f0bdc7917faa97c8d3e4b73076d91213b85366)) +* release v3.0.0-Beta.22 ([4271914](https://github.com/WebFiori/framework/commit/4271914182564a0c917a185010933f7f76b86f5d)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Update Libraries Versions ([46f4d56](https://github.com/WebFiori/framework/commit/46f4d56aa8bc911393ed80d4e57368472dbcdd24)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated .gitattributes ([3b2334c](https://github.com/WebFiori/framework/commit/3b2334ce9b29d1be3b71049b3caf759a00c84724)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependences ([a160f0f](https://github.com/WebFiori/framework/commit/a160f0fcc7cb0b2570c9487090b2bcc3e0ad658e)) +* Updated Dependencies ([e48f333](https://github.com/WebFiori/framework/commit/e48f3336d8c0b1910e18b8baa5ea40be28c9e50d)) +* Updated Dependencies ([8284dc6](https://github.com/WebFiori/framework/commit/8284dc655e8e92aafc3fb6a1bd88861254da0fe1)) +* Updated Dependencies ([97bb7a2](https://github.com/WebFiori/framework/commit/97bb7a220a9c8a81fd70a4c2d80d891e4f4c7eb2)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies + Framework Version ([9f5dd93](https://github.com/WebFiori/framework/commit/9f5dd9374ebf818605b6ae4acff3d5b95237d1ff)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([7f84cf6](https://github.com/WebFiori/framework/commit/7f84cf65da991a63daccc2cb0896b78b98d578c5)) +* Updated Framework Version ([361e5d5](https://github.com/WebFiori/framework/commit/361e5d545a274043efceab4087d4db7769990d60)) +* Updated Framework Version ([783f4be](https://github.com/WebFiori/framework/commit/783f4be57869ae93eab8c0b49fe2ede5cc7fbba8)) +* Updated Framework Version ([0403027](https://github.com/WebFiori/framework/commit/0403027fc02bfbba13dd1c899ae073f43c925cd8)) +* Updated Framework Version ([f7c0f7f](https://github.com/WebFiori/framework/commit/f7c0f7f0dad2900d988b3866c2a035b0b1c10e7b)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Libraries Versions ([248d1da](https://github.com/WebFiori/framework/commit/248d1dab9efffd44ac5728d005dde58470d2fef1)) +* Updated Release Please Config ([1a8b4e5](https://github.com/WebFiori/framework/commit/1a8b4e55f9a5496aac47e30228613d8a24068914)) +* Updated Version Number ([280f418](https://github.com/WebFiori/framework/commit/280f418df38a2eb019b6f2a5d0c1b8b8d00133d3)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.0.0-Beta.21](https://github.com/WebFiori/framework/compare/v3.0.1-Beta.20...v3.0.0-Beta.21) (2024-12-24) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Correction to File Path ([df3eacf](https://github.com/WebFiori/framework/commit/df3eacfb150a43020794545f51ee1379256a46fc)) +* Fix Assignment Issue ([34a522f](https://github.com/WebFiori/framework/commit/34a522ff53e8ad7bc8bc1287bc0c7595a4d7e254)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Registering Middleware ([6cc7ce1](https://github.com/WebFiori/framework/commit/6cc7ce1e39bf0aebfe0eb5ff91360e4c4ed04f05)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Undefined Constant ([d605a5b](https://github.com/WebFiori/framework/commit/d605a5be85cd8623a2114b8c0756372c82bd7c9b)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([60aa746](https://github.com/WebFiori/framework/commit/60aa746bf39ccf7cbdda5bd9c24a6ed408d2732c)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* **dev:** release 3.0.0-Beta.17 ([a3e5983](https://github.com/WebFiori/framework/commit/a3e598305d75f2fa87d6148520d88f4235a53253)) +* **dev:** release 3.0.0-Beta.18 ([a994097](https://github.com/WebFiori/framework/commit/a994097c021b5575cab7f077f8d730736c3c1bbe)) +* **dev:** release 3.0.0-Beta.19 ([e917eb5](https://github.com/WebFiori/framework/commit/e917eb5ccd18ee8223a73ec9a2ac499a281f5764)) +* **dev:** release 3.0.0-Beta.19 ([5497825](https://github.com/WebFiori/framework/commit/549782509f23be3a90375debe0319b16e549a3aa)) +* **dev:** release 3.0.0-Beta.20 ([7afdb92](https://github.com/WebFiori/framework/commit/7afdb92618bdbc6e11adb29b001d9a9d0a8f4809)) +* **dev:** release 3.1.0-Beta.14 ([ba5a5e3](https://github.com/WebFiori/framework/commit/ba5a5e30b4033d3f2486cbab578545aadbed67b0)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* Fix target Branch ([a419a3e](https://github.com/WebFiori/framework/commit/a419a3e29bb416be328b19f1489788d51ebbcd4e)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* release 3.0.0-Beta.20 ([ddcf6b0](https://github.com/WebFiori/framework/commit/ddcf6b03010a0f011112bfa789f446b1949daaae)) +* release 3.0.0-Beta.20 ([c4fc053](https://github.com/WebFiori/framework/commit/c4fc053d45a5c8b24e963a625d0954c33af2c884)) +* release 3.0.0-Beta.20 ([6e72830](https://github.com/WebFiori/framework/commit/6e72830f0ec1f6943d84ee3266632d5c62e02832)) +* release v3.0.0-Beta.17 ([3c0c639](https://github.com/WebFiori/framework/commit/3c0c639a72f9dd08bec7a150e33af2bb18e9728a)) +* release v3.0.0-Beta.18 ([5a588eb](https://github.com/WebFiori/framework/commit/5a588eb52815889a8409a07a30c4e6f0defe3269)) +* release v3.0.0-Beta.19 ([e8d5314](https://github.com/WebFiori/framework/commit/e8d531433f6afada6684050013b4169b3d8d547b)) +* release v3.0.0-Beta.20 ([630f512](https://github.com/WebFiori/framework/commit/630f512c6a036e111a7b146cacbd7d72dfe0da06)) +* release v3.0.0-Beta.21 ([56f0bdc](https://github.com/WebFiori/framework/commit/56f0bdc7917faa97c8d3e4b73076d91213b85366)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Update Libraries Versions ([46f4d56](https://github.com/WebFiori/framework/commit/46f4d56aa8bc911393ed80d4e57368472dbcdd24)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated .gitattributes ([3b2334c](https://github.com/WebFiori/framework/commit/3b2334ce9b29d1be3b71049b3caf759a00c84724)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependences ([a160f0f](https://github.com/WebFiori/framework/commit/a160f0fcc7cb0b2570c9487090b2bcc3e0ad658e)) +* Updated Dependencies ([e48f333](https://github.com/WebFiori/framework/commit/e48f3336d8c0b1910e18b8baa5ea40be28c9e50d)) +* Updated Dependencies ([8284dc6](https://github.com/WebFiori/framework/commit/8284dc655e8e92aafc3fb6a1bd88861254da0fe1)) +* Updated Dependencies ([97bb7a2](https://github.com/WebFiori/framework/commit/97bb7a220a9c8a81fd70a4c2d80d891e4f4c7eb2)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([7f84cf6](https://github.com/WebFiori/framework/commit/7f84cf65da991a63daccc2cb0896b78b98d578c5)) +* Updated Framework Version ([361e5d5](https://github.com/WebFiori/framework/commit/361e5d545a274043efceab4087d4db7769990d60)) +* Updated Framework Version ([783f4be](https://github.com/WebFiori/framework/commit/783f4be57869ae93eab8c0b49fe2ede5cc7fbba8)) +* Updated Framework Version ([0403027](https://github.com/WebFiori/framework/commit/0403027fc02bfbba13dd1c899ae073f43c925cd8)) +* Updated Framework Version ([f7c0f7f](https://github.com/WebFiori/framework/commit/f7c0f7f0dad2900d988b3866c2a035b0b1c10e7b)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Libraries Versions ([248d1da](https://github.com/WebFiori/framework/commit/248d1dab9efffd44ac5728d005dde58470d2fef1)) +* Updated Release Please Config ([1a8b4e5](https://github.com/WebFiori/framework/commit/1a8b4e55f9a5496aac47e30228613d8a24068914)) +* Updated Version Number ([280f418](https://github.com/WebFiori/framework/commit/280f418df38a2eb019b6f2a5d0c1b8b8d00133d3)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.0.0-Beta.20](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.19...v3.0.0-Beta.20) (2024-12-16) + + +### Miscellaneous Chores + +* Fix target Branch ([a419a3e](https://github.com/WebFiori/framework/commit/a419a3e29bb416be328b19f1489788d51ebbcd4e)) +* release 3.0.0-Beta.20 ([ddcf6b0](https://github.com/WebFiori/framework/commit/ddcf6b03010a0f011112bfa789f446b1949daaae)) +* release 3.0.0-Beta.20 ([c4fc053](https://github.com/WebFiori/framework/commit/c4fc053d45a5c8b24e963a625d0954c33af2c884)) +* release 3.0.0-Beta.20 ([6e72830](https://github.com/WebFiori/framework/commit/6e72830f0ec1f6943d84ee3266632d5c62e02832)) +* release v3.0.0-Beta.20 ([630f512](https://github.com/WebFiori/framework/commit/630f512c6a036e111a7b146cacbd7d72dfe0da06)) +* Updated Framework Version ([361e5d5](https://github.com/WebFiori/framework/commit/361e5d545a274043efceab4087d4db7769990d60)) + +## [3.0.0-Beta.19](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.19...v3.0.0-Beta.19) (2024-12-09) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Correction to File Path ([df3eacf](https://github.com/WebFiori/framework/commit/df3eacfb150a43020794545f51ee1379256a46fc)) +* Fix Assignment Issue ([34a522f](https://github.com/WebFiori/framework/commit/34a522ff53e8ad7bc8bc1287bc0c7595a4d7e254)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Registering Middleware ([6cc7ce1](https://github.com/WebFiori/framework/commit/6cc7ce1e39bf0aebfe0eb5ff91360e4c4ed04f05)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Undefined Constant ([d605a5b](https://github.com/WebFiori/framework/commit/d605a5be85cd8623a2114b8c0756372c82bd7c9b)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([60aa746](https://github.com/WebFiori/framework/commit/60aa746bf39ccf7cbdda5bd9c24a6ed408d2732c)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* **dev:** release 3.0.0-Beta.17 ([a3e5983](https://github.com/WebFiori/framework/commit/a3e598305d75f2fa87d6148520d88f4235a53253)) +* **dev:** release 3.0.0-Beta.18 ([a994097](https://github.com/WebFiori/framework/commit/a994097c021b5575cab7f077f8d730736c3c1bbe)) +* **dev:** release 3.0.0-Beta.19 ([5497825](https://github.com/WebFiori/framework/commit/549782509f23be3a90375debe0319b16e549a3aa)) +* **dev:** release 3.1.0-Beta.14 ([ba5a5e3](https://github.com/WebFiori/framework/commit/ba5a5e30b4033d3f2486cbab578545aadbed67b0)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* release v3.0.0-Beta.17 ([3c0c639](https://github.com/WebFiori/framework/commit/3c0c639a72f9dd08bec7a150e33af2bb18e9728a)) +* release v3.0.0-Beta.18 ([5a588eb](https://github.com/WebFiori/framework/commit/5a588eb52815889a8409a07a30c4e6f0defe3269)) +* release v3.0.0-Beta.19 ([e8d5314](https://github.com/WebFiori/framework/commit/e8d531433f6afada6684050013b4169b3d8d547b)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Update Libraries Versions ([46f4d56](https://github.com/WebFiori/framework/commit/46f4d56aa8bc911393ed80d4e57368472dbcdd24)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependences ([a160f0f](https://github.com/WebFiori/framework/commit/a160f0fcc7cb0b2570c9487090b2bcc3e0ad658e)) +* Updated Dependencies ([e48f333](https://github.com/WebFiori/framework/commit/e48f3336d8c0b1910e18b8baa5ea40be28c9e50d)) +* Updated Dependencies ([8284dc6](https://github.com/WebFiori/framework/commit/8284dc655e8e92aafc3fb6a1bd88861254da0fe1)) +* Updated Dependencies ([97bb7a2](https://github.com/WebFiori/framework/commit/97bb7a220a9c8a81fd70a4c2d80d891e4f4c7eb2)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([783f4be](https://github.com/WebFiori/framework/commit/783f4be57869ae93eab8c0b49fe2ede5cc7fbba8)) +* Updated Framework Version ([0403027](https://github.com/WebFiori/framework/commit/0403027fc02bfbba13dd1c899ae073f43c925cd8)) +* Updated Framework Version ([f7c0f7f](https://github.com/WebFiori/framework/commit/f7c0f7f0dad2900d988b3866c2a035b0b1c10e7b)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Release Please Config ([1a8b4e5](https://github.com/WebFiori/framework/commit/1a8b4e55f9a5496aac47e30228613d8a24068914)) +* Updated Version Number ([280f418](https://github.com/WebFiori/framework/commit/280f418df38a2eb019b6f2a5d0c1b8b8d00133d3)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.0.0-Beta.19](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.18...v3.0.0-Beta.19) (2024-12-09) + + +### Miscellaneous Chores + +* release v3.0.0-Beta.19 ([e8d5314](https://github.com/WebFiori/framework/commit/e8d531433f6afada6684050013b4169b3d8d547b)) +* Updated Dependencies ([8284dc6](https://github.com/WebFiori/framework/commit/8284dc655e8e92aafc3fb6a1bd88861254da0fe1)) +* Updated Version Number ([280f418](https://github.com/WebFiori/framework/commit/280f418df38a2eb019b6f2a5d0c1b8b8d00133d3)) + +## [3.0.0-Beta.18](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.17...v3.0.0-Beta.18) (2024-12-04) + + +### Bug Fixes + +* Correction to File Path ([df3eacf](https://github.com/WebFiori/framework/commit/df3eacfb150a43020794545f51ee1379256a46fc)) +* Fix to Undefined Constant ([d605a5b](https://github.com/WebFiori/framework/commit/d605a5be85cd8623a2114b8c0756372c82bd7c9b)) + + +### Miscellaneous Chores + +* release v3.0.0-Beta.18 ([5a588eb](https://github.com/WebFiori/framework/commit/5a588eb52815889a8409a07a30c4e6f0defe3269)) +* Updated Framework Version ([783f4be](https://github.com/WebFiori/framework/commit/783f4be57869ae93eab8c0b49fe2ede5cc7fbba8)) +* Updated Release Please Config ([1a8b4e5](https://github.com/WebFiori/framework/commit/1a8b4e55f9a5496aac47e30228613d8a24068914)) + +## [3.0.0-Beta.17](https://github.com/WebFiori/framework/compare/v3.1.0-Beta.14...v3.0.0-Beta.17) (2024-12-03) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([60aa746](https://github.com/WebFiori/framework/commit/60aa746bf39ccf7cbdda5bd9c24a6ed408d2732c)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* **dev:** release 3.1.0-Beta.14 ([ba5a5e3](https://github.com/WebFiori/framework/commit/ba5a5e30b4033d3f2486cbab578545aadbed67b0)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* release v3.0.0-Beta.17 ([3c0c639](https://github.com/WebFiori/framework/commit/3c0c639a72f9dd08bec7a150e33af2bb18e9728a)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Update Libraries Versions ([46f4d56](https://github.com/WebFiori/framework/commit/46f4d56aa8bc911393ed80d4e57368472dbcdd24)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependences ([a160f0f](https://github.com/WebFiori/framework/commit/a160f0fcc7cb0b2570c9487090b2bcc3e0ad658e)) +* Updated Dependencies ([97bb7a2](https://github.com/WebFiori/framework/commit/97bb7a220a9c8a81fd70a4c2d80d891e4f4c7eb2)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([0403027](https://github.com/WebFiori/framework/commit/0403027fc02bfbba13dd1c899ae073f43c925cd8)) +* Updated Framework Version ([f7c0f7f](https://github.com/WebFiori/framework/commit/f7c0f7f0dad2900d988b3866c2a035b0b1c10e7b)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.1.0-Beta.14](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.14...v3.1.0-Beta.14) (2024-11-27) + + +### Features + +* Added Ability to Enable or Disable Cache ([434fd72](https://github.com/WebFiori/framework/commit/434fd726657d7e4967681933bd718d60f68f2a76)) + + +### Miscellaneous Chores + +* Updated Dependencies Versions ([07252d0](https://github.com/WebFiori/framework/commit/07252d09f40af27cc04494ea7081a41fe0fe2ede)) +* Updated Framework Version ([d44cedc](https://github.com/WebFiori/framework/commit/d44cedc29ce9097403bdb263c279812f78d7581b)) + +## [3.0.0-Beta.14](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.14...v3.0.0-Beta.14) (2024-11-21) + + +### Features + +* Added a Method to Load Multiple Files ([89d0363](https://github.com/WebFiori/framework/commit/89d0363bb81a32032e938da71a19ec959c48e2bf)) +* Added a Way to Handle Configuration Errors ([76f1539](https://github.com/WebFiori/framework/commit/76f153933680c4ae4d7b067e8fca95273412ab2d)) +* Added Additional Logging Methods to Tasks Manager ([afc9b46](https://github.com/WebFiori/framework/commit/afc9b4697b58dbeb0cb7df26e9303fc1e720ecce)) +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Added Support for Loading Non-PSR-4 Compliant Classes ([a9772b4](https://github.com/WebFiori/framework/commit/a9772b49fc94b1a524e4555a18135461e1ef88ac)) +* Added Support for Setting Env Vars Using `putenv()` ([2895d6f](https://github.com/WebFiori/framework/commit/2895d6fd7df6060ebae227867dba719864b3578a)) +* Added Support for Writing Unit Test Classes for APIs ([baefa85](https://github.com/WebFiori/framework/commit/baefa855b76a7f42fb2ca0323888d0bc7d7d1f96)) +* **autoloader:** Added a Method to Check Validity of Namespace ([e749a3a](https://github.com/WebFiori/framework/commit/e749a3aafa0d6c1a11da7d486cb68ad8b048b4b7)) +* Automation of Writing Unit Test Cases for APIs ([5bab349](https://github.com/WebFiori/framework/commit/5bab349082328e668f13c5192dd5555b99201fa9)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Bug Fixes + +* Add Missing Returns ([9dcd9bf](https://github.com/WebFiori/framework/commit/9dcd9bf2670116a514169abcfdd5af72d4b12d11)) +* Added Check for Empty File Path ([b046fdf](https://github.com/WebFiori/framework/commit/b046fdf98768a63d882d102c1d20cc01b4f8a288)) +* Added Handling Code for Session Serialization Errors ([a2c7955](https://github.com/WebFiori/framework/commit/a2c7955888483c4eb8e446c1b5bd8794331a174a)) +* Added Missing Namespace ([069364a](https://github.com/WebFiori/framework/commit/069364a4566dc15f917ae0469fb8548ae5411771)) +* **autoload:** Add File Name an NS ([eb4d5b9](https://github.com/WebFiori/framework/commit/eb4d5b93f6ea4dc12e5809a6fde63c9f2d4fa928)) +* **autoload:** Check NS with Path ([a3d4c6e](https://github.com/WebFiori/framework/commit/a3d4c6e2e52eae4f6c421f533b7316b8562b1bf8)) +* **cli:** Rename of Class `CommandArgument` to `Argument` ([7f67a0f](https://github.com/WebFiori/framework/commit/7f67a0f61886159261c4749955d34a4187e76cbc)) +* **config:** Fix to JSON Configuration Style ([4dda36c](https://github.com/WebFiori/framework/commit/4dda36c14c8f8a77479bebb24b7b504e4bf02817)) +* Fix to `RunSQLQueryCommand` ([87dc2e3](https://github.com/WebFiori/framework/commit/87dc2e3a2dbf9f25dc81db2e9af9123aef198d4c)) +* Fix to a Bug in Creating Test Case ([0e4b8e5](https://github.com/WebFiori/framework/commit/0e4b8e5ff0c307e45bd5fb3a2acbfacc82f9d373)) +* Fix to Bug in Loading Themes ([ce67490](https://github.com/WebFiori/framework/commit/ce674903b6358824c61360a2ae335b8399c38309)) +* Fix to Create CLI Command ([82c7a88](https://github.com/WebFiori/framework/commit/82c7a888a0a5140d1381b9855ecf2762eb52659b)) +* Fix to Initial Namespace ([6e0e08a](https://github.com/WebFiori/framework/commit/6e0e08ace2b63c2cb959a236342014962c6a3b01)) +* Fix to Line Numbers in Exception Logging ([781a233](https://github.com/WebFiori/framework/commit/781a233a9e0bdb95c4d1a40b96358d608e07de0e)) +* Fix to Reading Extra Connection Props ([a6c5b92](https://github.com/WebFiori/framework/commit/a6c5b9269ac6f7a354f944f0bbc9557f6a73dd1f)) +* Fix to Running SQL Query from File ([0c8bb61](https://github.com/WebFiori/framework/commit/0c8bb613dbdec50c06f80ee0e4d9850602d8a71b)) +* Fix to Setting Middleware Name ([3a02a60](https://github.com/WebFiori/framework/commit/3a02a60d0ed3a2decf0059ce889325fc02f64893)) +* Fix to Uninitialized Variable ([905c3c7](https://github.com/WebFiori/framework/commit/905c3c7b8232a8f1ee6171fa309c2489b1bdd141)) +* Made `init` Static ([e04233a](https://github.com/WebFiori/framework/commit/e04233a0b4b65b903d92029dcb80ec4814dd5a08)) +* Remove Unused Import ([ed43960](https://github.com/WebFiori/framework/commit/ed43960b90052084b7a95a9ac182619af1244a3f)) +* **themes:** Fix to Problems in Loading Theme ([7a331ff](https://github.com/WebFiori/framework/commit/7a331ff9484fe13fcbd7a7c653321b3e9d233fba)) +* **ui:** Fix to Bug In Web Page Initialization ([8645c2a](https://github.com/WebFiori/framework/commit/8645c2a024276c70a96b0ebc3b83480649bd09d7)) +* **ui:** Fix to Load Language After Page Initialization ([38b0843](https://github.com/WebFiori/framework/commit/38b084385251e8b5bfdae2c8ade3ab0219bba046)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Cleanup ([0d5f798](https://github.com/WebFiori/framework/commit/0d5f7983426f7d766e79e6932ec47cf9ac7853dd)) +* Code Quality Improvements ([80c7853](https://github.com/WebFiori/framework/commit/80c7853f737b16e605e11fd9bcf56f1ecc24223a)) +* Code Quality Improvments ([f8e9ed9](https://github.com/WebFiori/framework/commit/f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* **dev:** release 3.0.0-Beta.14 ([8c3dd76](https://github.com/WebFiori/framework/commit/8c3dd7651f604414c5e5ccfd8567d907545d5513)) +* Fix Imports ([7386f92](https://github.com/WebFiori/framework/commit/7386f9242351673588eaefe6c0de02c7e467f62a)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* **release-please:** Added Additional Sections ([40dcfa4](https://github.com/WebFiori/framework/commit/40dcfa4bad0f8b42a34e0541ef558cd78f37b2ce)) +* Remove Redeclaration ([f41549d](https://github.com/WebFiori/framework/commit/f41549da7a7570ec9984a53f16abf863a716e55d)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Run CS Fixer ([ca8e690](https://github.com/WebFiori/framework/commit/ca8e690d7e8dcc737d4fe125ea828ec4ef146035)) +* Update composer.json ([819c26d](https://github.com/WebFiori/framework/commit/819c26d8fd7f23a057a76fa923b62d0a2281721d)) +* Updated .gitattribute ([63ba6d8](https://github.com/WebFiori/framework/commit/63ba6d890b82280d87d002f8c3fcfee1493ea2ff)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated CI Config ([a7175a4](https://github.com/WebFiori/framework/commit/a7175a4442cb6d5d4031d03cf228fc43439b504b)) +* Updated Composer Config ([cf26913](https://github.com/WebFiori/framework/commit/cf2691382d6d883f2f06b25e789b9a30524758cd)) +* Updated Core Framework Libraries ([9220fa4](https://github.com/WebFiori/framework/commit/9220fa4c77c668793962afc427495adcd6c8ca55)) +* Updated Core Libraries ([c21a48f](https://github.com/WebFiori/framework/commit/c21a48f6586068b4cab3c223465e4b3c60849752)) +* Updated Core Libraries ([fda39a9](https://github.com/WebFiori/framework/commit/fda39a9168b6e8cebda1408e0de4f0f3815845f5)) +* Updated Core Libraries ([4aa9670](https://github.com/WebFiori/framework/commit/4aa96707feabd9518a788d4393442b0287f0a375)) +* Updated Core Libraries Versions ([dcb1a15](https://github.com/WebFiori/framework/commit/dcb1a15882cac069cb7439101b8e096018c1cfc1)) +* Updated Core Library Version ([db4d223](https://github.com/WebFiori/framework/commit/db4d223a4cb2b8bc40a45fa69e4d67b358d1f29a)) +* Updated Dependencies ([aef4319](https://github.com/WebFiori/framework/commit/aef4319b1dd10cd4f208e943e638b4b364b04cd0)) +* Updated Dependencies Version ([0d3ead5](https://github.com/WebFiori/framework/commit/0d3ead5cad177efd50e4b285222fcdbaf8beab66)) +* Updated Errors Handling Library ([5cf44a9](https://github.com/WebFiori/framework/commit/5cf44a9b5ecae3ac5ed3888c18c33e5415055703)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) +* Updated Framework Version ([fb91c15](https://github.com/WebFiori/framework/commit/fb91c15587fb006f096b90a2f2b01e5b9ffb7c47)) +* Updated Framework Version ([a817e8f](https://github.com/WebFiori/framework/commit/a817e8feec235cec29e6c20e00732a25d1c80534)) +* Updated Framework Version ([1672faf](https://github.com/WebFiori/framework/commit/1672faf3f12ef217915d5ea62d6fae9e01c9db28)) +* Updated Framework Version ([8b013ae](https://github.com/WebFiori/framework/commit/8b013ae6d622823f4abd935cbda45d0a718030a1)) +* Updated Framework Version ([7841fd2](https://github.com/WebFiori/framework/commit/7841fd266b92146c4eb800adc1b859c9e022dd25)) +* Updated Framework Version Number ([2f8d814](https://github.com/WebFiori/framework/commit/2f8d814fd477b3c8699ada67c2c6b47a04f29b10)) +* Updated Framework Version Number ([84c7857](https://github.com/WebFiori/framework/commit/84c785722512e04c106412f3612f9ef59758ed40)) +* Updated Framework Version Number ([72cb62d](https://github.com/WebFiori/framework/commit/72cb62d198cef51275c282142edacb13b1a5bcd4)) +* Updated Framework Version Number ([bc89447](https://github.com/WebFiori/framework/commit/bc8944771dd1bd629f65a0df9e4067ea76e141da)) +* Updated Framework Version Number ([a5177bb](https://github.com/WebFiori/framework/commit/a5177bbb1de08d1cf6748ec09ec6d9e53fa20d10)) +* Updated Version Number ([d75c9d0](https://github.com/WebFiori/framework/commit/d75c9d0c9547d2e4ce3edbac839a1f712a9f90a4)) + +## [3.0.0-Beta.14](https://github.com/WebFiori/framework/compare/v3.0.0-Beta.13...v3.0.0-Beta.14) (2024-11-21) + + +### Features + +* Added More Abstraction to Cache Feature ([f51b7b9](https://github.com/WebFiori/framework/commit/f51b7b9d74ef992625a697faa09e71e1c7873f22)) +* Caching (Initial Prototype) ([4a063f3](https://github.com/WebFiori/framework/commit/4a063f3b1070b04bf81adf1ac2ea2089002adf84)) +* Routes Caching ([bbbacff](https://github.com/WebFiori/framework/commit/bbbacffd93174662a6359dc3b6c51a3e1db74dd6)) + + +### Miscellaneous Chores + +* Added Documentation ([697155f](https://github.com/WebFiori/framework/commit/697155f3904a7fbaac37421bc0b75e31d1fd932a)) +* Added Please Release Manifest and Config ([25970da](https://github.com/WebFiori/framework/commit/25970da8ea98c77a3bf9dd44ae443e8fc5cbb7c6)) +* Added Release Please Config & Manifest ([3b6273c](https://github.com/WebFiori/framework/commit/3b6273c644189f8e52a22b38041921eeab15c7f3)) +* Added Release Please to Workflow ([6da66a3](https://github.com/WebFiori/framework/commit/6da66a3eed187878aaa5557765537e65a9f00853)) +* Change Target Branch for Release Please ([452b9ff](https://github.com/WebFiori/framework/commit/452b9ff4f3919d6416c4ce55316a5b1325482437)) +* Configuration for Please Release ([33caa13](https://github.com/WebFiori/framework/commit/33caa13908911242236e7f22e7ce603f41c63207)) +* release 3.0.0-Beta.14 ([872a0ec](https://github.com/WebFiori/framework/commit/872a0ec0cf732dbe1e2ef3e11d51d79d68b2fb8b)) +* Remove Unused Imports ([53288a9](https://github.com/WebFiori/framework/commit/53288a9063a672bb37da06e6d6e15a492d57b45b)) +* Run CS Fixer ([13f2dde](https://github.com/WebFiori/framework/commit/13f2dde9bc289ea682a045a8c8ab10c7edaf8891)) +* Updated CI Config ([2f14e35](https://github.com/WebFiori/framework/commit/2f14e354fb6d0017197def88049e71e7a3f46f95)) +* Updated Framework Version ([f27a583](https://github.com/WebFiori/framework/commit/f27a583ffa12f4d8aecb5682a2e58c78f191c095)) diff --git a/README.md b/README.md index f37fc3572..0b18fa9a9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- + @@ -29,11 +29,11 @@ WebFiori Framework is a mini web development framework which is built using PHP ## Supported PHP Versions | Build Status | |:-----------:| -|| -|| -|| -|| -|
| +|| +|| +|| +|| +|
| ## Key Features diff --git a/app/apis/RoutingTestClass.php b/app/apis/RoutingTestClass.php index 6b9f7822c..3bbee5eb7 100644 --- a/app/apis/RoutingTestClass.php +++ b/app/apis/RoutingTestClass.php @@ -4,14 +4,14 @@ use webfiori\http\Response; class RoutingTestClass { - public function __construct(string $param = null, string $second = null) { + public function __construct(?string $param = null, ?string $second = null) { Response::write("I'm inside the class."); } public function doSomething() { Response::clear(); Response::write("I'm doing something."); } - public function doSomethingExtra(string $p1 = null, string $p2 = null) { + public function doSomethingExtra(?string $p1 = null, ?string $p2 = null) { Response::clear(); Response::write("I'm doing something."); } diff --git a/app/database/migrations/.gitkeep b/app/database/migrations/.gitkeep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/app/database/migrations/.gitkeep @@ -0,0 +1 @@ + diff --git a/app/database/migrations/commands/.gitkeep b/app/database/migrations/commands/.gitkeep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/app/database/migrations/commands/.gitkeep @@ -0,0 +1 @@ + diff --git a/app/database/migrations/emptyRunner/XRunner.php b/app/database/migrations/emptyRunner/XRunner.php new file mode 100644 index 000000000..32894978b --- /dev/null +++ b/app/database/migrations/emptyRunner/XRunner.php @@ -0,0 +1,12 @@ + 'true' + ]); + parent::__construct(APP_PATH.'database'.DS.'migrations'.DS.'multi', '\\app\\database\\migrations\\multi', $conn); + } +} diff --git a/app/database/migrations/multiDownErr/Migration000.php b/app/database/migrations/multiDownErr/Migration000.php new file mode 100644 index 000000000..9aca3997b --- /dev/null +++ b/app/database/migrations/multiDownErr/Migration000.php @@ -0,0 +1,32 @@ +do(); + } +} diff --git a/app/database/migrations/multiDownErr/Migration002.php b/app/database/migrations/multiDownErr/Migration002.php new file mode 100644 index 000000000..65f799a2e --- /dev/null +++ b/app/database/migrations/multiDownErr/Migration002.php @@ -0,0 +1,32 @@ + 'true' + ]); + parent::__construct(APP_PATH.'database'.DS.'migrations'.DS.'multiDownErr', '\\app\\database\\migrations\\multiDownErr', $conn); + } +} diff --git a/app/database/migrations/multiErr/Migration000.php b/app/database/migrations/multiErr/Migration000.php new file mode 100644 index 000000000..47046d8ee --- /dev/null +++ b/app/database/migrations/multiErr/Migration000.php @@ -0,0 +1,32 @@ +x(); + } + /** + * Performs the action that will revert back the migration. + * + * @param Database $schema The database at which the migration will be applied to. + */ + public function down(Database $schema) { + $schema->y(); + } +} diff --git a/app/database/migrations/multiErr/Migration001.php b/app/database/migrations/multiErr/Migration001.php new file mode 100644 index 000000000..100a4aa69 --- /dev/null +++ b/app/database/migrations/multiErr/Migration001.php @@ -0,0 +1,32 @@ + 'true' + ]); + parent::__construct(APP_PATH.'database'.DS.'migrations'.DS.'multiErr', '\\app\\database\\migrations\\multiErr', $conn); + } +} diff --git a/app/database/migrations/noConn/Migration000.php b/app/database/migrations/noConn/Migration000.php new file mode 100644 index 000000000..4c08a292a --- /dev/null +++ b/app/database/migrations/noConn/Migration000.php @@ -0,0 +1,32 @@ +x(); + } + /** + * Performs the action that will revert back the migration. + * + * @param Database $schema The database at which the migration will be applied to. + */ + public function down(Database $schema) { + $schema->y(); + } +} diff --git a/app/database/migrations/noConn/XRunner.php b/app/database/migrations/noConn/XRunner.php new file mode 100644 index 000000000..786bacd29 --- /dev/null +++ b/app/database/migrations/noConn/XRunner.php @@ -0,0 +1,12 @@ + - - - - - - - ./webfiori/framework/Access.php - ./webfiori/framework/DB.php - ./webfiori/framework/Language.php - - ./webfiori/framework/config/JsonDriver.php - ./webfiori/framework/config/ClassDriver.php - - ./webfiori/framework/Privilege.php - ./webfiori/framework/PrivilegesGroup.php - ./webfiori/framework/User.php - ./webfiori/framework/Theme.php - - ./webfiori/framework/ui/BeforeRenderCallback.php - ./webfiori/framework/ui/WebPage.php - - ./webfiori/framework/scheduler/BaseTask.php - ./webfiori/framework/scheduler/AbstractTask.php - ./webfiori/framework/scheduler/TasksManager.php - ./webfiori/framework/scheduler/TaskArgument.php - - ./webfiori/framework/router/RouterUri.php - ./webfiori/framework/router/Router.php - - ./webfiori/framework/session/Session.php - ./webfiori/framework/session/SessionsManager.php - ./webfiori/framework/session/DefaultSessionStorage.php - ./webfiori/framework/session/DatabaseSessionStorage.php - ./webfiori/framework/session/SessionDB.php - - ./webfiori/framework/cli/commands/AddCommand.php - ./webfiori/framework/cli/commands/CreateCommand.php - ./webfiori/framework/cli/commands/ListRoutesCommand.php - ./webfiori/framework/cli/commands/ListThemesCommand.php - ./webfiori/framework/cli/commands/RunSQLQueryCommand.php - ./webfiori/framework/cli/commands/SchedulerCommand.php - ./webfiori/framework/cli/commands/SettingsCommand.php - ./webfiori/framework/cli/commands/UpdateSettingsCommand.php - ./webfiori/framework/cli/commands/UpdateTableCommand.php - ./webfiori/framework/cli/commands/VersionCommand.php - ./webfiori/framework/cli/commands/WHelpCommand.php - ./webfiori/framework/cli/CLITestCase.php - ./webfiori/framework/cli/CLIUtils.php - - ./webfiori/framework/cli/helpers/ClassInfoReader.php - ./webfiori/framework/cli/helpers/CreateBackgroundTask.php - ./webfiori/framework/cli/helpers/CreateCLIClassHelper.php - ./webfiori/framework/cli/helpers/CreateClassHelper.php - ./webfiori/framework/cli/helpers/CreateDBAccessHelper.php - ./webfiori/framework/cli/helpers/CreateFullRESTHelper.php - ./webfiori/framework/cli/helpers/CreateMiddleware.php - ./webfiori/framework/cli/helpers/CreateTableObj.php - ./webfiori/framework/cli/helpers/CreateThemeHelper.php - ./webfiori/framework/cli/helpers/CreateWebService.php - ./webfiori/framework/cli/helpers/TableObjHelper.php - ./webfiori/framework/cli/helpers/CreateAPITestCase.php - - ./webfiori/framework/writers/CLICommandClassWriter.php - ./webfiori/framework/writers/ClassWriter.php - ./webfiori/framework/writers/DBClassWriter.php - ./webfiori/framework/writers/LangClassWriter.php - ./webfiori/framework/writers/MiddlewareClassWriter.php - ./webfiori/framework/writers/SchedulerTaskClassWriter.php - ./webfiori/framework/writers/TableClassWriter.php - ./webfiori/framework/writers/ThemeClassWriter.php - ./webfiori/framework/writers/ThemeComponentWriter.php - ./webfiori/framework/writers/WebServiceWriter.php - ./webfiori/framework/writers/APITestCaseWriter.php - - - - - - - - - ./tests/webfiori/framework/test - - - ./tests/webfiori/framework/test/DBTest.php - - - ./tests/webfiori/framework/test/langs - - - ./tests/webfiori/framework/test/router - - - ./tests/webfiori/framework/test/mail - - - ./tests/webfiori/framework/test/writers - - - ./tests/webfiori/framework/test/cli - - - ./tests/webfiori/framework/test/theme - - - ./tests/webfiori/framework/test/scheduler - - - ./tests/webfiori/framework/test/session - - - - \ No newline at end of file diff --git a/release-commit.php b/release-commit.php new file mode 100644 index 000000000..3cb8f8f04 --- /dev/null +++ b/release-commit.php @@ -0,0 +1,9 @@ + true, - 'root' => __DIR__, + 'root' => $ROOT, 'on-load-failure' => 'do-nothing' ]); fprintf(STDOUT,'Autoloader Initialized.'."\n"); fprintf(STDOUT,"---------------------------------\n"); fprintf(STDOUT,"Initializing application...\n"); +App::initiate('app', 'public', $ROOT); App::start(); fprintf(STDOUT,'Done.'."\n"); -fprintf(STDOUT,'Root Directory: \''.ClassLoader::get()->root().'\'.'."\n"); +fprintf(STDOUT,'Autoload Root Directory: \''.ClassLoader::get()->root().'\'.'."\n"); define('TESTS_PATH', ClassLoader::get()->root().$DS.TESTS_DIRECTORY); fprintf(STDOUT,'Tests Path: '.TESTS_PATH."\n"); fprintf(STDOUT,'App Path: '.APP_PATH."\n"); @@ -95,7 +106,18 @@ App::getConfig()->remove(); JsonDriver::setConfigFileName('super-confx.json'); App::getConfig()->remove(); + $conn = new ConnectionInfo('mssql',SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $runner = new MigrationsRunner(APP_PATH, '', $conn); + try { + $runner->dropMigrationsTable(); + } catch (\Exception $exc) { + + } + }); fprintf(STDOUT, "Registering shutdown function completed.\n"); fprintf(STDOUT,"---------------------------------\n"); fprintf(STDOUT,"Starting to run tests...\n"); +fprintf(STDOUT,"---------------------------------\n"); \ No newline at end of file diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 000000000..ca225d2ad --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,122 @@ + + + + + + + + ../webfiori/framework/Access.php + ../webfiori/framework/DB.php + ../webfiori/framework/Language.php + + ../webfiori/framework/config/JsonDriver.php + ../webfiori/framework/config/ClassDriver.php + + ../webfiori/framework/Privilege.php + ../webfiori/framework/PrivilegesGroup.php + ../webfiori/framework/User.php + ../webfiori/framework/Theme.php + + ../webfiori/framework/ui/BeforeRenderCallback.php + ../webfiori/framework/ui/WebPage.php + + ../webfiori/framework/scheduler/BaseTask.php + ../webfiori/framework/scheduler/AbstractTask.php + ../webfiori/framework/scheduler/TasksManager.php + ../webfiori/framework/scheduler/TaskArgument.php + + ../webfiori/framework/router/RouterUri.php + ../webfiori/framework/router/Router.php + + ../webfiori/framework/middleware/AbstractMiddleware.php + ../webfiori/framework/middleware/MiddlewareManager.php + ../webfiori/framework/middleware/CacheMiddleware.php + ../webfiori/framework/middleware/StartSessionMiddleware.php + + ../webfiori/framework/session/Session.php + ../webfiori/framework/session/SessionsManager.php + ../webfiori/framework/session/DefaultSessionStorage.php + ../webfiori/framework/session/DatabaseSessionStorage.php + ../webfiori/framework/session/SessionDB.php + + ../webfiori/framework/cli/commands/AddCommand.php + ../webfiori/framework/cli/commands/CreateCommand.php + ../webfiori/framework/cli/commands/ListRoutesCommand.php + ../webfiori/framework/cli/commands/ListThemesCommand.php + ../webfiori/framework/cli/commands/RunSQLQueryCommand.php + ../webfiori/framework/cli/commands/SchedulerCommand.php + ../webfiori/framework/cli/commands/SettingsCommand.php + ../webfiori/framework/cli/commands/UpdateSettingsCommand.php + ../webfiori/framework/cli/commands/UpdateTableCommand.php + ../webfiori/framework/cli/commands/VersionCommand.php + ../webfiori/framework/cli/commands/WHelpCommand.php + ../webfiori/framework/cli/commands/RunMigrationsCommand.php + ../webfiori/framework/cli/CLITestCase.php + ../webfiori/framework/cli/CLIUtils.php + + ../webfiori/framework/cli/helpers/ClassInfoReader.php + ../webfiori/framework/cli/helpers/CreateBackgroundTask.php + ../webfiori/framework/cli/helpers/CreateCLIClassHelper.php + ../webfiori/framework/cli/helpers/CreateClassHelper.php + ../webfiori/framework/cli/helpers/CreateDBAccessHelper.php + ../webfiori/framework/cli/helpers/CreateFullRESTHelper.php + ../webfiori/framework/cli/helpers/CreateMiddleware.php + ../webfiori/framework/cli/helpers/CreateTableObj.php + ../webfiori/framework/cli/helpers/CreateThemeHelper.php + ../webfiori/framework/cli/helpers/CreateWebService.php + ../webfiori/framework/cli/helpers/TableObjHelper.php + ../webfiori/framework/cli/helpers/CreateAPITestCase.php + ../webfiori/framework/cli/helpers/CreateMigration.php + + + ../webfiori/framework/writers/CLICommandClassWriter.php + ../webfiori/framework/writers/ClassWriter.php + ../webfiori/framework/writers/DBClassWriter.php + ../webfiori/framework/writers/LangClassWriter.php + ../webfiori/framework/writers/MiddlewareClassWriter.php + ../webfiori/framework/writers/SchedulerTaskClassWriter.php + ../webfiori/framework/writers/TableClassWriter.php + ../webfiori/framework/writers/ThemeClassWriter.php + ../webfiori/framework/writers/ThemeComponentWriter.php + ../webfiori/framework/writers/WebServiceWriter.php + ../webfiori/framework/writers/APITestCaseWriter.php + ../webfiori/framework/writers/DatabaseMigrationWriter.php + + + + + + + + + ./webfiori/framework/test + + + ./webfiori/framework/test/DBTest.php + + + ./webfiori/framework/test/langs + + + ./webfiori/framework/test/router + + + ./webfiori/framework/test/mail + + + ./webfiori/framework/test/writers + + + ./webfiori/framework/test/cli + + + ./webfiori/framework/test/theme + + + ./webfiori/framework/test/scheduler + + + ./webfiori/framework/test/session + + + \ No newline at end of file diff --git a/tests/webfiori/framework/test/PageTest.php b/tests/webfiori/framework/test/PageTest.php index f499416a4..161aaf89c 100644 --- a/tests/webfiori/framework/test/PageTest.php +++ b/tests/webfiori/framework/test/PageTest.php @@ -320,7 +320,7 @@ public function testDefaults00() { $this->assertEquals('ltr',$page->getWritingDir()); $this->assertNotNull($page->getTranslation()); $this->assertEquals('https://127.0.0.1/',$page->getCanonical()); - $this->assertEquals('http://127.0.0.1',$page->getBase()); + $this->assertEquals('https://127.0.0.1',$page->getBase()); } /** * @test diff --git a/tests/webfiori/framework/test/cli/AddCommandTest.php b/tests/webfiori/framework/test/cli/AddCommandTest.php index 946eca911..50bd9b198 100644 --- a/tests/webfiori/framework/test/cli/AddCommandTest.php +++ b/tests/webfiori/framework/test/cli/AddCommandTest.php @@ -191,7 +191,7 @@ public function testAddLang00() { "Success: Language added. Also, a class for the language is created at \"".APP_DIR."\langs\" for that language.\n" ], $runner->getOutput()); $this->assertTrue(class_exists('\\app\\langs\\LangFK')); - $this->removeClass('\\app\\langs\\LanguageFK'); + $this->removeClass('\\app\\langs\\LangFK'); Controller::getDriver()->initialize(); } /** diff --git a/tests/webfiori/framework/test/cli/CreateCLICommandTest.php b/tests/webfiori/framework/test/cli/CreateCLICommandTest.php index 2aa0e0988..3c85f7386 100644 --- a/tests/webfiori/framework/test/cli/CreateCLICommandTest.php +++ b/tests/webfiori/framework/test/cli/CreateCLICommandTest.php @@ -3,10 +3,11 @@ use webfiori\cli\CLICommand; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; /** * @author Ibrahim */ -class CreateCLICommandTest extends CreateTestCase { +class CreateCLICommandTest extends CLITestCase { /** * @test */ @@ -39,7 +40,8 @@ public function testCreateCommand00() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\commands'\n", "Enter a name for the command:\n", diff --git a/tests/webfiori/framework/test/cli/CreateCommandTest.php b/tests/webfiori/framework/test/cli/CreateCommandTest.php index 3a1ee73af..7262db33a 100644 --- a/tests/webfiori/framework/test/cli/CreateCommandTest.php +++ b/tests/webfiori/framework/test/cli/CreateCommandTest.php @@ -2,19 +2,20 @@ namespace webfiori\framework\test\cli; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; /** * Description of TestCreateCommand * * @author Ibrahim */ -class CreateCommandTest extends CreateTestCase { +class CreateCommandTest extends CLITestCase { /** * @test */ public function testCreate00() { $runner = App::getRunner(); $runner->setInputs([ - '10', + '11', ]); $runner->setArgsVector([ 'webfiori', @@ -33,7 +34,8 @@ public function testCreate00() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", ], $runner->getOutput()); } /** @@ -61,7 +63,8 @@ public function testCreate01() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", ], $runner->getOutput()); } } diff --git a/tests/webfiori/framework/test/cli/CreateDBAccessTest.php b/tests/webfiori/framework/test/cli/CreateDBAccessTest.php index 1939052eb..fc90595a0 100644 --- a/tests/webfiori/framework/test/cli/CreateDBAccessTest.php +++ b/tests/webfiori/framework/test/cli/CreateDBAccessTest.php @@ -3,13 +3,14 @@ use webfiori\database\ConnectionInfo; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; /** * Description of CreateDBAccessTest * * @author Ibrahim */ -class CreateDBAccessTest extends CreateTestCase { +class CreateDBAccessTest extends CLITestCase { /** * @test */ @@ -41,7 +42,7 @@ public function test00() { "Entity class name:\n", "Entity namespace: Enter = 'app\\entity'\n", "Would you like to have update methods for every single column?(y/N)\n", - "Info: New class was created at \"app\database\".\n" + "Info: New class was created at \"". ROOT_PATH.DS."app".DS."database\".\n" ], $runner->getOutput()); $clazz = '\\app\\database\\EmployeeOperationsDB'; $this->assertTrue(class_exists($clazz)); @@ -76,7 +77,7 @@ public function test01() { "Entity class name:\n", "Entity namespace: Enter = 'app\\entity'\n", "Would you like to have update methods for every single column?(y/N)\n", - "Info: New class was created at \"app\database\\empl\".\n" + "Info: New class was created at \"". ROOT_PATH.DS."app".DS."database".DS."empl\".\n" ], $runner->getOutput()); $clazz = '\\app\\database\\empl\\EmployeeSDB'; $this->assertTrue(class_exists($clazz)); @@ -119,7 +120,7 @@ public function test02() { "Entity class name:\n", "Entity namespace: Enter = 'app\\entity'\n", "Would you like to have update methods for every single column?(y/N)\n", - "Info: New class was created at \"app\database\".\n" + "Info: New class was created at \"". ROOT_PATH.DS."app".DS."database\".\n" ], $runner->getOutput()); $clazz = '\\app\\database\\Position2xDB'; $this->assertTrue(class_exists($clazz)); diff --git a/tests/webfiori/framework/test/cli/CreateEntityTest.php b/tests/webfiori/framework/test/cli/CreateEntityTest.php index 3bad134dd..b04c372d0 100644 --- a/tests/webfiori/framework/test/cli/CreateEntityTest.php +++ b/tests/webfiori/framework/test/cli/CreateEntityTest.php @@ -3,8 +3,9 @@ use app\database\TestTable; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; -class CreateEntityTest extends CreateTestCase { +class CreateEntityTest extends CLITestCase { /** * @test */ @@ -89,7 +90,8 @@ public function testCreateEntity01() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "We need from you to give us entity class information.\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\\entity'\n", diff --git a/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php b/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php index 635f00871..d0bca45f3 100644 --- a/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php +++ b/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php @@ -2,6 +2,7 @@ namespace webfiori\framework\test\cli; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; use webfiori\framework\middleware\AbstractMiddleware; /** @@ -9,7 +10,7 @@ * * @author Ibrahim */ -class CreateMiddlewareTest extends CreateTestCase { +class CreateMiddlewareTest extends CLITestCase { /** * @test */ @@ -42,7 +43,8 @@ public function testCreateMiddleware00() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\middleware'\n", "Enter a name for the middleware:\n", diff --git a/tests/webfiori/framework/test/cli/CreateMigrationTest.php b/tests/webfiori/framework/test/cli/CreateMigrationTest.php new file mode 100644 index 000000000..ad35b7baa --- /dev/null +++ b/tests/webfiori/framework/test/cli/CreateMigrationTest.php @@ -0,0 +1,76 @@ +getMName(); + $clazz = '\\app\\database\\migrations\\'.$name; + $order = $this->getOrder(); + + $this->assertEquals([ + 'Info: New class was created at "'. APP_PATH .'database'.DS.'migrations".'."\n", + "Info: Migration Name: $name\n", + "Info: Migration Order: $order\n" + ], $this->executeMultiCommand([ + CreateCommand::class, + '--c' => 'migration', + '--defaults' + ])); + $this->assertEquals(0, $this->getExitCode()); + $this->assertTrue(class_exists($clazz)); + $this->removeClass($clazz); + } + /** + * @test + */ + public function testCreateMigration01() { + $name = 'CoolMigration'; + $defaultName = $this->getMName(); + $clazz = '\\app\\database\\migrations\\'.$name; + $order = $this->getOrder(); + + $this->assertEquals([ + "Migration namespace: Enter = 'app\database\migrations'\n", + "Provide an optional name for the class that will have migration logic:\n", + "Enter an optional name for the migration: Enter = '$defaultName'\n", + "Enter an optional execution order for the migration: Enter = '$order'\n", + 'Info: New class was created at "'. APP_PATH .'database'.DS.'migrations".'."\n", + "Info: Migration Name: Great One\n", + "Info: Migration Order: 11\n" + ], $this->executeMultiCommand([ + CreateCommand::class, + '--c' => 'migration', + ], [ + "\n", + $name, + "Great One", + "11" + ])); + $this->assertEquals(0, $this->getExitCode()); + $this->assertTrue(class_exists($clazz)); + $this->removeClass($clazz); + } + private function getOrder() { + $runner = new MigrationsRunner(APP_PATH.DS.'database'.DS.'migrations', '\\app\\database\\migrations', null); + return count($runner->getMigrations()); + } + private function getMName() { + $runner = new MigrationsRunner(APP_PATH.DS.'database'.DS.'migrations', '\\app\\database\\migrations', null); + $count = count($runner->getMigrations()); + if ($count < 10) { + return 'Migration00'.$count; + } else if ($count < 100) { + return 'Migration0'.$count; + } + return 'Migration'.$count; + } +} \ No newline at end of file diff --git a/tests/webfiori/framework/test/cli/CreateRESTTest.php b/tests/webfiori/framework/test/cli/CreateRESTTest.php index df2538ef3..8493fa928 100644 --- a/tests/webfiori/framework/test/cli/CreateRESTTest.php +++ b/tests/webfiori/framework/test/cli/CreateRESTTest.php @@ -2,14 +2,16 @@ namespace webfiori\framework\test\cli; use webfiori\database\ConnectionInfo; +use webfiori\file\File; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; /** * Description of CreateRESTTest * * @author Ibrahim */ -class CreateRESTTest extends CreateTestCase { +class CreateRESTTest extends CLITestCase { /** * @test */ @@ -59,7 +61,7 @@ public function test00() { ]); $this->assertEquals(0, $runner->start()); $this->assertEquals(array_merge([ - "Warning: No database connections found in the class \"app\AppConfig\"!\n", + "Warning: No database connections found in application configuration.\n", "Info: Run the command \"add\" to add connections.\n", "Database type:\n", "0: mysql\n", @@ -127,6 +129,7 @@ public function test00() { foreach ($apiClazzes as $clazz) { $this->assertTrue(class_exists($clazz)); + $this->assertTrue(File::isFileExist(ROOT_PATH.DS. str_replace('\\', DS, $clazz).'.php')); } $this->assertTrue(class_exists($tableClazz)); $this->assertTrue(class_exists($entityClazz)); @@ -190,7 +193,7 @@ public function test01() { ]); $this->assertEquals(0, $runner->start()); $this->assertEquals(array_merge([ - "Warning: No database connections found in the class \"app\AppConfig\"!\n", + "Warning: No database connections found in application configuration.\n", "Info: Run the command \"add\" to add connections.\n","Database type:\n", "0: mysql\n", "1: mssql\n", diff --git a/tests/webfiori/framework/test/cli/CreateTableTest.php b/tests/webfiori/framework/test/cli/CreateTableTest.php index 357aa3971..44e82fa9f 100644 --- a/tests/webfiori/framework/test/cli/CreateTableTest.php +++ b/tests/webfiori/framework/test/cli/CreateTableTest.php @@ -28,13 +28,14 @@ class CreateTableTest extends TestCase { "8: varbinary\n", "9: date\n", "10: datetime2\n", - "11: time\n", - "12: money\n", - "13: bit\n", - "14: decimal\n", - "15: float\n", - "16: boolean\n", - "17: bool\n", + "11: datetime\n", + "12: time\n", + "13: money\n", + "14: bit\n", + "15: decimal\n", + "16: float\n", + "17: boolean\n", + "18: bool\n", ]; const MYSQL_COLS_TYPES = [ "Enter a name for column key:\n", diff --git a/tests/webfiori/framework/test/cli/CreateTaskTest.php b/tests/webfiori/framework/test/cli/CreateTaskTest.php index 5db238627..b057f3adf 100644 --- a/tests/webfiori/framework/test/cli/CreateTaskTest.php +++ b/tests/webfiori/framework/test/cli/CreateTaskTest.php @@ -2,6 +2,7 @@ namespace webfiori\framework\test\cli; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; use webfiori\framework\scheduler\AbstractTask; /** @@ -9,7 +10,7 @@ * * @author Ibrahim */ -class CreateTaskTest extends CreateTestCase { +class CreateTaskTest extends CLITestCase { /** * @test */ @@ -42,7 +43,8 @@ public function test00() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\\tasks'\n", "Enter a name for the task:\n", @@ -91,7 +93,8 @@ public function test01() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\\tasks'\n", "Error: A class in the given namespace which has the given name was found.\n", @@ -144,7 +147,8 @@ public function test02() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\\tasks'\n", "Enter a name for the task:\n", diff --git a/tests/webfiori/framework/test/cli/CreateTestCase.php b/tests/webfiori/framework/test/cli/CreateTestCase.php deleted file mode 100644 index 21c51da13..000000000 --- a/tests/webfiori/framework/test/cli/CreateTestCase.php +++ /dev/null @@ -1,12 +0,0 @@ -remove(); - } -} diff --git a/tests/webfiori/framework/test/cli/CreateThemeTest.php b/tests/webfiori/framework/test/cli/CreateThemeTest.php index c5712d15b..322360fa8 100644 --- a/tests/webfiori/framework/test/cli/CreateThemeTest.php +++ b/tests/webfiori/framework/test/cli/CreateThemeTest.php @@ -2,6 +2,7 @@ namespace webfiori\framework\test\cli; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; use webfiori\framework\ThemeLoader; /** @@ -9,7 +10,7 @@ * * @author Ibrahim */ -class CreateThemeTest extends CreateTestCase { +class CreateThemeTest extends CLITestCase { /** * @test */ @@ -39,7 +40,8 @@ public function testCreateTheme00() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'themes'\n", 'Creating theme at "'.ROOT_PATH.DS.'themes'.DS."fiori\"...\n", diff --git a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php index 0c625f42d..3c03df069 100644 --- a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php +++ b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php @@ -2,16 +2,18 @@ namespace webfiori\framework\test\cli; use webfiori\framework\App; +use webfiori\framework\cli\CLITestCase; use webfiori\http\AbstractWebService; use webfiori\http\ParamOption; use webfiori\http\ParamType; use webfiori\http\RequestMethod; use webfiori\http\RequestParameter; +use const ROOT_PATH; /** * * @author Ibrahim */ -class CreateWebServiceTest extends CreateTestCase { +class CreateWebServiceTest extends CLITestCase { /** * @test */ @@ -52,7 +54,8 @@ public function test00() { "7: Database access class based on table.\n", "8: Complete REST backend (Database table, entity, database access and web services).\n", "9: Web service test case.\n", - "10: Quit. <--\n", + "10: Database migration.\n", + "11: Quit. <--\n", "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\apis'\n", "Enter a name for the new web service:\n", @@ -63,9 +66,10 @@ public function test00() { "2: GET <--\n", "3: HEAD\n", "4: OPTIONS\n", - "5: POST\n", - "6: PUT\n", - "7: TRACE\n", + "5: PATCH\n", + "6: POST\n", + "7: PUT\n", + "8: TRACE\n", "Would you like to add another request method?(y/N)\n", "Would you like to add request parameters to the service?(y/N)\n", "Enter a name for the request parameter:\n", @@ -123,7 +127,7 @@ public function test01() { 'Service\'s Desc', '', 'y', - '5', + '6', 'n', 'y', 'a-number', @@ -151,9 +155,10 @@ public function test01() { "2: GET <--\n", "3: HEAD\n", "4: OPTIONS\n", - "5: POST\n", - "6: PUT\n", - "7: TRACE\n", + "5: PATCH\n", + "6: POST\n", + "7: PUT\n", + "8: TRACE\n", "Would you like to add another request method?(y/N)\n", "Request method:\n", "0: CONNECT\n", @@ -161,9 +166,10 @@ public function test01() { "2: GET <--\n", "3: HEAD\n", "4: OPTIONS\n", - "5: POST\n", - "6: PUT\n", - "7: TRACE\n", + "5: PATCH\n", + "6: POST\n", + "7: PUT\n", + "8: TRACE\n", "Would you like to add another request method?(y/N)\n", "Would you like to add request parameters to the service?(y/N)\n", "Enter a name for the request parameter:\n", @@ -220,7 +226,7 @@ public function test02() { 'Service\'s Desc', '', 'y', - '5', + '6', 'n', 'y', 'a-number', @@ -248,9 +254,10 @@ public function test02() { "2: GET <--\n", "3: HEAD\n", "4: OPTIONS\n", - "5: POST\n", - "6: PUT\n", - "7: TRACE\n", + "5: PATCH\n", + "6: POST\n", + "7: PUT\n", + "8: TRACE\n", "Would you like to add another request method?(y/N)\n", "Request method:\n", "0: CONNECT\n", @@ -258,9 +265,10 @@ public function test02() { "2: GET <--\n", "3: HEAD\n", "4: OPTIONS\n", - "5: POST\n", - "6: PUT\n", - "7: TRACE\n", + "5: PATCH\n", + "6: POST\n", + "7: PUT\n", + "8: TRACE\n", "Would you like to add another request method?(y/N)\n", "Would you like to add request parameters to the service?(y/N)\n", "Enter a name for the request parameter:\n", diff --git a/tests/webfiori/framework/test/cli/DBClassWritterTest.php b/tests/webfiori/framework/test/cli/DBClassWritterTest.php index 02aec1a4b..a7a7e7817 100644 --- a/tests/webfiori/framework/test/cli/DBClassWritterTest.php +++ b/tests/webfiori/framework/test/cli/DBClassWritterTest.php @@ -4,6 +4,7 @@ use tables\EmployeeInfoTable; use tables\PositionInfoTable; use tables\UserInfoTable; +use webfiori\framework\cli\CLITestCase; use webfiori\framework\writers\DBClassWriter; /** @@ -11,7 +12,7 @@ * * @author Ibrahim */ -class DBClassWritterTest extends CreateTestCase { +class DBClassWritterTest extends CLITestCase { /** * @test */ diff --git a/tests/webfiori/framework/test/cli/HelpCommandTest.php b/tests/webfiori/framework/test/cli/HelpCommandTest.php new file mode 100644 index 000000000..7ae239616 --- /dev/null +++ b/tests/webfiori/framework/test/cli/HelpCommandTest.php @@ -0,0 +1,37 @@ +assertEquals([ + "WebFiori Framework (c) Version ". WF_VERSION." ".WF_VERSION_TYPE."\n\n\n", + "Usage:\n", + " command [arg1 arg2=\"val\" arg3...]\n\n", + "Global Arguments:\n", + " --ansi:[Optional] Force the use of ANSI output.\n", + "Available Commands:\n", + " help: Display CLI Help. To display help for specific command, use the argument \"--command-name\" with this command.\n", + " v: Display framework version info.\n", + " show-settings: Display application configuration.\n", + " scheduler: Run tasks scheduler.\n", + " create: Creates a system entity (middleware, web service, background process ...).\n", + " add: Add a database connection or SMTP account.\n", + " list-routes: List all created routes and which resource they point to.\n", + " list-themes: List all registered themes.\n", + " run-query: Execute SQL query on specific database.\n", + " update-settings: Update application settings which are stored in specific configuration driver.\n", + " update-table: Update a database table.\n", + " migrations: Execute database migrations.\n", + ], $this->executeMultiCommand([ + 'help', + ])); + $this->assertEquals(0, $this->getExitCode()); + } +} diff --git a/tests/webfiori/framework/test/cli/RunMigrationsCommantTest.php b/tests/webfiori/framework/test/cli/RunMigrationsCommantTest.php new file mode 100644 index 000000000..90a518d79 --- /dev/null +++ b/tests/webfiori/framework/test/cli/RunMigrationsCommantTest.php @@ -0,0 +1,809 @@ +assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: No migrations found in the namespace '\app\database\migrations'.\n", + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--connection' => 'ABC' + ])); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations01() { + App::getConfig()->removeAllDBConnections(); + $clazz = $this->createMigration(); + $this->assertTrue(class_exists($clazz)); + + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--connection' => 'ABC' + ]); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Info: No connections were found in application configuration.\n", + ], $output); + + $this->assertEquals(-1, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations02() { + $conn = new ConnectionInfo('mysql', 'root', '123456', 'testing_db'); + $conn->setName('default-conn'); + $clazz = $this->createMigration(); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--connection' => 'ABC' + ]); + $this->removeClass($clazz); + App::getConfig()->removeAllDBConnections(); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Error: No connection was found which has the name 'ABC'.\n", + ], $output); + + $this->assertEquals(-1, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations03() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, 'x123456', SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration('PO', 'MyPOMigration'); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + ], [ + '7', + '' + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Error: Invalid answer.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Error: Unable to connect to database: 18456 - [Microsoft][ODBC Driver ".ODBC_VERSION." for SQL Server][SQL Server]Login failed for user 'sa'.\n", + ], $output); + $this->assertEquals(-1, $this->getExitCode()); + + + } + /** + * @test + */ + public function testRunMigrations04() { + $this->assertEquals([ + "Error: The argument --runner has invalid value: Class \"\app\database\migrations\" does not exist.\n", + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations', + ])); + $this->assertEquals(-1, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations05() { + $this->assertEquals([ + "Error: The argument --runner has invalid value: Exception: \"Call to private webfiori\\framework\App::__construct() from scope webfiori\\framework\cli\commands\RunMigrationsCommand\".\n", + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\webfiori\\framework\\App', + ])); + $this->assertEquals(-1, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations06() { + $this->assertEquals([ + "Checking namespace '\app\database\migrations\\emptyRunner' for migrations...\n", + "Info: No migrations found in the namespace '\app\database\migrations\\emptyRunner'.\n", + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\emptyRunner\XRunner', + ])); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations07() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, 'testing_dbx', SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration('Cool', 'CoolOne'); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + ], [ + '7', + '' + ]); + $this->removeClass($clazz); + App::getConfig()->removeAllDBConnections(); + $err = ODBC_VERSION == 17 ? "Error: Unable to connect to database: 4060 - [Microsoft][ODBC Driver ".ODBC_VERSION." for SQL Server][SQL Server]Cannot open database \"testing_dbx\" requested by the login. The login failed.\n" + : "Error: Unable to connect to database: 18456 - [Microsoft][ODBC Driver ".ODBC_VERSION." for SQL Server][SQL Server]Login failed for user 'sa'.\n"; + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Error: Invalid answer.\n", + "Select database connection:\n", + "0: default-conn <--\n", + $err, + ], $output); + $this->assertEquals(-1, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations08() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $this->removeMigTable($conn); + $clazz = $this->createMigration('Super', 'SuperMigration'); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + ], [ + '7', + '' + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Error: Invalid answer.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Starting to execute migrations...\n", + "Error: Failed to execute migration due to following:\n", + "208 - [Microsoft][ODBC Driver ".ODBC_VERSION." for SQL Server][SQL Server]Invalid object name 'migrations'. (Line 361)\n", + "Warning: Execution stopped.\n", + "Info: No migrations were executed.\n" + ], $output); + $this->assertEquals(0, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations09() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration(); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--ini' + ], [ + '7', + '' + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeMigTable($conn); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Error: Invalid answer.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Initializing migrations table...\n", + "Success: Migrations table succesfully created.\n", + "Starting to execute migrations...\n", + "Success: Migration 'Migration000' applied successfuly.\n", + "Info: Number of applied migrations: 1\n", + "Names of applied migrations:\n", + "- Migration000\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations10() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration('Cool One', 'CLSOne'); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--ini', + '--connection' => 'default-conn' + ], [ + + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeClass($clazz); + $this->removeMigTable($conn); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Initializing migrations table...\n", + "Success: Migrations table succesfully created.\n", + "Starting to execute migrations...\n", + "Success: Migration 'Cool One' applied successfuly.\n", + "Info: Number of applied migrations: 1\n", + "Names of applied migrations:\n", + "- Cool One\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations11() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration('Cool One', 'ColOne'); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ini', + ], [ + '' + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeClass($clazz); + $this->removeMigTable($conn); + $this->assertEquals([ + "Info: Using default namespace for migrations.\n", + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Initializing migrations table...\n", + "Success: Migrations table succesfully created.\n", + "Starting to execute migrations...\n", + "Success: Migration 'Cool One' applied successfuly.\n", + "Info: Number of applied migrations: 1\n", + "Names of applied migrations:\n", + "- Cool One\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations12() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, '1234567890@Eux', SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration('Oh S', 'OhSuperMg'); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ini', + ], [ + '' + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeClass($clazz); + $this->assertEquals([ + "Info: Using default namespace for migrations.\n", + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Select database connection:\n", + "0: default-conn <--\n", + "Initializing migrations table...\n", + "Error: Unable to create migrations table due to following:\n", + "Unable to connect to database: 18456 - [Microsoft][ODBC Driver ".ODBC_VERSION." for SQL Server][SQL Server]Login failed for user 'sa'.\n", + ], $output); + $this->assertEquals(-1, $this->getExitCode()); + + } + /** + * @test + */ + public function testRunMigrations13() { + $clazz = $this->createMigration(); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ini', + ], [ + '' + ]); + $this->removeClass($clazz); + $this->assertEquals([ + "Info: Using default namespace for migrations.\n", + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Info: No connections were found in application configuration.\n", + ], $output); + $this->assertEquals(-1, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations14() { + //$clazz = $this->createMigration(); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\noConn\XRunner', + '--ini' + ]); + //$this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations\\noConn' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations\\noConn'.\n", + "Info: No connections were found in application configuration.\n", + ], $output); + $this->assertEquals(-1, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations15() { + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Initializing migrations table...\n", + "Success: Migrations table succesfully created.\n", + "Starting to execute migrations...\n", + "Success: Migration 'First One' applied successfuly.\n", + "Success: Migration 'Second one' applied successfuly.\n", + "Success: Migration 'Third One' applied successfuly.\n", + "Info: Number of applied migrations: 3\n", + "Names of applied migrations:\n", + "- First One\n", + "- Second one\n", + "- Third One\n" + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\MultiRunner', + '--ini' + ])); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + * @depends testRunMigrations15 + */ + public function testRunMigrations16() { + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Starting to execute migrations...\n", + "Info: No migrations were executed.\n", + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\MultiRunner', + ])); + $this->assertEquals(0, $this->getExitCode()); + $this->removeMigTable(); + } + /** + * @test + */ + public function testRunMigrations17() { + $this->assertEquals([ + "Error: The argument --runner has invalid value: \"\app\database\migrations\multi\Migration000\" is not an instance of \"MigrationsRunner\".\n", + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\Migration000', + ])); + $this->assertEquals(-1, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations18() { + $this->assertEquals([ + "Info: Using default namespace for migrations.\n", + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: No migrations found in the namespace '\app\database\migrations'.\n" + ], $this->executeMultiCommand([ + RunMigrationsCommand::class, + + ])); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + */ + public function testRunMigrations19() { + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multiErr\MultiErrRunner', + '--ini' + ]); + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multiErr' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multiErr'.\n", + "Initializing migrations table...\n", + "Success: Migrations table succesfully created.\n", + "Starting to execute migrations...\n", + "Success: Migration 'First One' applied successfuly.\n", + "Success: Migration 'Second one' applied successfuly.\n", + "Error: Failed to execute migration due to following:\n", + "Call to undefined method app\database\migrations\multiErr\Migration000::x() (Line 22)\n", + "Warning: Execution stopped.\n", + "Info: Number of applied migrations: 2\n", + "Names of applied migrations:\n", + "- First One\n", + "- Second one\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + $r = new MultiErrRunner(); + $this->removeMigTable($r->getConnectionInfo()); + } + /** + * @test + */ + public function testRollback00() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + $clazz = $this->createMigration('ABC', 'ABC'); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->addOrUpdateDBConnection($conn); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--rollback', + '--connection' => 'default-conn', + '--ini' + ], [ + '' + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeMigTable($conn); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Initializing migrations table...\n", + "Success: Migrations table succesfully created.\n", + "Rolling back last executed migration...\n", + "Info: No migration rolled back.\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + */ + public function testRollback01() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + + $ns = '\\app\\database\\migrations'; + $clazz = $this->createAndRunMigration($conn, $ns, 'ABCD Cool', 'ABCCool'); + App::getConfig()->addOrUpdateDBConnection($conn); + + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--rollback', + '--connection' => 'default-conn', + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeMigTable($conn); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Rolling back last executed migration...\n", + "Success: Migration 'ABCD Cool' was successfully rolled back.\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + */ + public function testRollback02() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + + $ns = '\\app\\database\\migrations'; + $clazz = $this->createAndRunMigration($conn, $ns, 'ABCD Cool', 'ABCCool'); + App::getConfig()->addOrUpdateDBConnection($conn); + $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--rollback', + '--connection' => 'default-conn', + ]); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => '\\app\\database\\migrations', + '--rollback', + '--connection' => 'default-conn', + ]); + App::getConfig()->removeAllDBConnections(); + $this->removeMigTable($conn); + $this->removeClass($clazz); + $this->assertEquals([ + "Checking namespace '\app\database\migrations' for migrations...\n", + "Info: Found 1 migration(s) in the namespace '\app\database\migrations'.\n", + "Rolling back last executed migration...\n", + "Info: No migration rolled back.\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + } + /** + * @test + */ + public function testRollback03() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + + App::getConfig()->addOrUpdateDBConnection($conn); + $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\MultiRunner', + '--ini' + ]); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\\MultiRunner', + '--rollback', + ]); + + App::getConfig()->removeAllDBConnections(); + + + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Rolling back last executed migration...\n", + "Success: Migration 'Third One' was successfully rolled back.\n", + ], $output); + + $this->assertEquals(0, $this->getExitCode()); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\\MultiRunner', + '--rollback', + ]); + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Rolling back last executed migration...\n", + "Success: Migration 'Second one' was successfully rolled back.\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\\MultiRunner', + '--rollback', + ]); + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Rolling back last executed migration...\n", + "Success: Migration 'First One' was successfully rolled back.\n", + ], $output); + $this->assertEquals(0, $this->getExitCode()); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\\MultiRunner', + '--rollback', + ]); + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Rolling back last executed migration...\n", + "Info: No migration rolled back.\n", + ], $output); + $this->removeMigTable($conn); + } + /** + * @test + */ + public function testRollback04() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + + App::getConfig()->addOrUpdateDBConnection($conn); + $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\MultiRunner', + '--ini' + ]); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\\MultiRunner', + '--rollback', + '--all' + ]); + + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Rolling back migrations...\n", + "Success: Migration 'Third One' was successfully rolled back.\n", + "Success: Migration 'Second one' was successfully rolled back.\n", + "Success: Migration 'First One' was successfully rolled back.\n", + ], $output); + + $this->assertEquals(0, $this->getExitCode()); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multi\\MultiRunner', + '--rollback', + '--all' + ]); + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multi' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multi'.\n", + "Rolling back migrations...\n", + "Info: No migration rolled back.\n", + ], $output); + $this->removeMigTable($conn); + } + /** + * @test + */ + public function testRollback05() { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + $conn->setName('default-conn'); + + App::getConfig()->addOrUpdateDBConnection($conn); + $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multiDownErr\MultiErrRunner', + '--ini' + ]); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multiDownErr\MultiErrRunner', + '--rollback', + '--all' + ]); + + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multiDownErr' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multiDownErr'.\n", + "Rolling back migrations...\n", + "Success: Migration 'Third One' was successfully rolled back.\n", + "Error: Failed to execute migration due to following:\n", + "Call to undefined method webfiori\database\migration\MigrationsRunner::do() (Line 30)\n", + "Warning: Execution stopped.\n", + ], $output); + + $this->assertEquals(-1, $this->getExitCode()); + $output = $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--runner' => '\\app\\database\\migrations\\multiDownErr\MultiErrRunner', + '--rollback', + '--all' + ]); + + $this->assertEquals([ + "Checking namespace '\app\database\migrations\multiDownErr' for migrations...\n", + "Info: Found 3 migration(s) in the namespace '\app\database\migrations\multiDownErr'.\n", + "Rolling back migrations...\n", + "Error: Failed to execute migration due to following:\n", + "Call to undefined method webfiori\database\migration\MigrationsRunner::do() (Line 30)\n", + "Warning: Execution stopped.\n", + ], $output); + $this->removeMigTable($conn); + } + private function createAndRunMigration(ConnectionInfo $connection, string $ns, ?string $name = null, ?string $className = null) : string { + $clazz = $this->createMigration($name, $className); + App::getConfig()->addOrUpdateDBConnection($connection); + $this->executeMultiCommand([ + RunMigrationsCommand::class, + '--ns' => $ns, + '--connection' => $connection->getName(), + '--ini' + ]); + $this->assertTrue(class_exists($clazz)); + App::getConfig()->removeDBConnection($connection->getName()); + return $clazz; + } + private function createMigration(?string $name = null, ?string $className = null) : string { + $runner = new MigrationsRunner(APP_PATH.DS.'database'.DS.'migrations'.DS.'commands', '\\app\\database\\migrations\\commands', null); + $writer = new DatabaseMigrationWriter($runner); + if ($name !== null) { + $writer->setMigrationName($name); + } + if ($className !== null) { + $writer->setClassName($className); + } + $writer->writeClass(); + return $writer->getName(true); + } + private function removeMigTable(?ConnectionInfo $conn = null) { + if ($conn === null) { + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); + } + $runner = new MigrationsRunner(APP_PATH.DS.'database'.DS.'migrations'.DS.'commands', '\\app\\database\\migrations\\commands', $conn); + try{ + $runner->dropMigrationsTable(); + } catch (DatabaseException $ex) { + + } + } +} diff --git a/tests/webfiori/framework/test/cli/SettingsCommandTest.php b/tests/webfiori/framework/test/cli/SettingsCommandTest.php index d82b38f16..3f8344780 100644 --- a/tests/webfiori/framework/test/cli/SettingsCommandTest.php +++ b/tests/webfiori/framework/test/cli/SettingsCommandTest.php @@ -3,12 +3,15 @@ use PHPUnit\Framework\TestCase; use webfiori\framework\App; +use webfiori\framework\config\ClassDriver; class SettingsCommandTest extends TestCase { /** * @test */ public function test00() { + App::setConfigDriver(ClassDriver::class); + App::getConfig()->remove(); App::getConfig()->initialize(true); $runner = App::getRunner(); $runner->setInputs(); diff --git a/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php b/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php index 32d3afe2e..340a0e205 100644 --- a/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php +++ b/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php @@ -17,7 +17,9 @@ class UpdateSettingsCommandTest extends TestCase { * @test */ public function test00() { + App::getConfig()->remove(); JsonDriver::setConfigFileName('app-config.json'); + App::getConfig()->initialize(true); $runner = App::getRunner(); $runner->setArgsVector([ 'webfiori', diff --git a/tests/webfiori/framework/test/config/JsonDriverTest.php b/tests/webfiori/framework/test/config/JsonDriverTest.php index c85e8ddfd..44476a302 100644 --- a/tests/webfiori/framework/test/config/JsonDriverTest.php +++ b/tests/webfiori/framework/test/config/JsonDriverTest.php @@ -38,7 +38,7 @@ public function test00() { 'description' => 'Configure the verbosity of error messsages at run-time. This should be set to true in testing and false in production.' ], 'CLI_HTTP_HOST' => [ - 'value' => 'example.com', + "value" => "127.0.0.1", 'description' => 'Host name that will be used when runing the application as command line utility.' ], ],$driver->getEnvVars()); @@ -147,7 +147,7 @@ public function testAddEnvVar00() { 'description' => 'Configure the verbosity of error messsages at run-time. This should be set to true in testing and false in production.' ], "CLI_HTTP_HOST" => [ - "value" => "example.com", + "value" => "127.0.0.1", "description" => "Host name that will be used when runing the application as command line utility." ] ], $driver->getEnvVars()); @@ -169,7 +169,7 @@ public function testAddEnvVar01() { 'description' => 'Configure the verbosity of error messsages at run-time. This should be set to true in testing and false in production.' ], "CLI_HTTP_HOST" => [ - "value" => "example.com", + "value" => "127.0.0.1", "description" => "Host name that will be used when runing the application as command line utility." ], "COOL_OR_NOT" => [ @@ -204,7 +204,7 @@ public function testAddEnvVar02() { 'description' => 'Configure the verbosity of error messsages at run-time. This should be set to true in testing and false in production.' ], "CLI_HTTP_HOST" => [ - "value" => "example.com", + "value" => "127.0.0.1", "description" => "Host name that will be used when runing the application as command line utility." ], "DO_IT" => [ diff --git a/tests/webfiori/framework/test/router/RouterUriTest.php b/tests/webfiori/framework/test/router/RouterUriTest.php index dd5443d23..2e261fdd2 100644 --- a/tests/webfiori/framework/test/router/RouterUriTest.php +++ b/tests/webfiori/framework/test/router/RouterUriTest.php @@ -19,9 +19,9 @@ public function testAddToMiddleware00() { $uri = new RouterUri('https://www3.programmingacademia.com:80/test', ''); MiddlewareManager::register(new TestMiddleware()); $uri->addMiddleware('global'); - $this->assertEquals(1, $uri->getMiddleware()->size()); + $this->assertEquals(1, count($uri->getMiddleware())); $uri->addMiddleware('Super Cool Middleware'); - $this->assertEquals(2, $uri->getMiddleware()->size()); + $this->assertEquals(2, count($uri->getMiddleware())); $this->assertFalse($uri->isDynamic()); } /** diff --git a/tests/webfiori/framework/test/session/SessionsManagerTest.php b/tests/webfiori/framework/test/session/SessionsManagerTest.php index 604fde3e5..c48fcd735 100644 --- a/tests/webfiori/framework/test/session/SessionsManagerTest.php +++ b/tests/webfiori/framework/test/session/SessionsManagerTest.php @@ -161,8 +161,10 @@ public function testDatabaseSession00() { */ public function testDatabaseSession01() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage("208 - [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Invalid object name 'session_data'."); - $conn = new ConnectionInfo('mssql', 'sa', '1234567890@Eu', 'testing_db', 'localhost'); + $this->expectExceptionMessage("208 - [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Invalid object name 'session_data'."); + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); $conn->setName('sessions-connection'); App::getConfig()->addOrUpdateDBConnection($conn); SessionsManager::setStorage(new DatabaseSessionStorage()); @@ -174,7 +176,9 @@ public function testDatabaseSession01() { * @depends testInitSessionsDb */ public function testDatabaseSession02() { - $conn = new ConnectionInfo('mssql', 'sa', '1234567890@Eu', 'testing_db', 'localhost'); + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); $conn->setName('sessions-connection'); App::getConfig()->addOrUpdateDBConnection($conn); SessionsManager::reset(); @@ -281,8 +285,10 @@ public function testDatabaseSession02() { */ public function testDropDbTables00() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage("208 - [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Invalid object name 'session_data'."); - $conn = new ConnectionInfo('mssql', 'sa', '1234567890@Eu', 'testing_db', 'localhost'); + $this->expectExceptionMessage("208 - [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Invalid object name 'session_data'."); + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); $conn->setName('sessions-connection'); App::getConfig()->addOrUpdateDBConnection($conn); SessionsManager::reset(); @@ -309,7 +315,9 @@ public function testGetSessionIDFromRequest() { * @depends testDatabaseSession01 */ public function testInitSessionsDb() { - $conn = new ConnectionInfo('mssql', 'sa', '1234567890@Eu', 'testing_db', 'localhost'); + $conn = new ConnectionInfo('mssql', SQL_SERVER_USER, SQL_SERVER_PASS, SQL_SERVER_DB, SQL_SERVER_HOST, 1433, [ + 'TrustServerCertificate' => 'true' + ]); $conn->setName('sessions-connection'); App::getConfig()->addOrUpdateDBConnection($conn); SessionsManager::reset(); diff --git a/tests/webfiori/framework/test/writers/DatabaseMigrationWriterTest.php b/tests/webfiori/framework/test/writers/DatabaseMigrationWriterTest.php new file mode 100644 index 000000000..19f3e0a30 --- /dev/null +++ b/tests/webfiori/framework/test/writers/DatabaseMigrationWriterTest.php @@ -0,0 +1,141 @@ +removeClass($clazz); + $runner = new MigrationsRunner($path, $ns, null); + $writter = new DatabaseMigrationWriter($runner); + $this->assertEquals('Migration000', $writter->getName()); + $this->assertEquals('app\\database\\migrations', $writter->getNamespace()); + $this->assertEquals('', $writter->getSuffix()); + $this->assertEquals([ + "webfiori\database\Database", + "webfiori\database\migration\AbstractMigration", + ], $writter->getUseStatements()); + $writter->writeClass(); + + $this->assertTrue(class_exists($clazz)); + $runner = new MigrationsRunner($path, $ns, null); + $migrations = $runner->getMigrations(); + $this->assertEquals(1, count($migrations)); + $m00 = $migrations[0]; + $this->assertTrue($m00 instanceof AbstractMigration); + $this->assertEquals('Migration000', $m00->getName()); + $this->assertEquals(0, $m00->getOrder()); + $this->removeClass($clazz); + } + /** + * @test + */ + public function test01() { + $path = APP_PATH.DS.'database'.DS.'migrations'; + $ns = '\\app\\database\\migrations'; + $runner = new MigrationsRunner($path, $ns, null); + $writter = new DatabaseMigrationWriter($runner); + $writter->setClassName('MyMigration'); + $writter->setMigrationName('A test migration.'); + $writter->setMigrationOrder(3); + $this->assertEquals('MyMigration', $writter->getName()); + $this->assertEquals('app\\database\\migrations', $writter->getNamespace()); + + $writter->writeClass(); + $clazz = "\\app\\database\\migrations\\MyMigration"; + $this->assertTrue(class_exists($clazz)); + $runner = new MigrationsRunner($path, $ns, null); + $migrations = $runner->getMigrations(); + $this->assertEquals(1, count($migrations)); + $m00 = $migrations[0]; + $this->assertTrue($m00 instanceof AbstractMigration); + $this->assertEquals('A test migration.', $m00->getName()); + $this->assertEquals(3, $m00->getOrder()); + $this->removeClass($clazz); + } + /** + * @test + */ + public function test02() { + $path = APP_PATH.DS.'database'.DS.'migrations'; + $ns = '\\app\\database\\migrations'; + $runner = new MigrationsRunner($path, $ns, null); + $writter = new DatabaseMigrationWriter($runner); + $this->assertEquals('Migration000', $writter->getName()); + $writter->writeClass(); + $clazz = "\\app\\database\\migrations\\Migration000"; + $this->assertTrue(class_exists($clazz)); + $runner2 = new MigrationsRunner($path, $ns, null); + $migrations = $runner2->getMigrations(); + $this->assertEquals(1, count($migrations)); + $m00 = $migrations[0]; + $this->assertTrue($m00 instanceof AbstractMigration); + $this->assertEquals('Migration000', $m00->getName()); + $this->assertEquals(0, $m00->getOrder()); + + $writter2 = new DatabaseMigrationWriter($runner2); + $this->assertEquals('Migration001', $writter2->getName()); + $writter2->writeClass(); + $clazz2 = "\\app\\database\\migrations\\Migration001"; + $this->assertTrue(class_exists($clazz)); + $runner3 = new MigrationsRunner($path, $ns, null); + $migrations2 = $runner3->getMigrations(); + $this->assertEquals(2, count($migrations2)); + $m01 = $migrations2[1]; + $this->assertTrue($m00 instanceof AbstractMigration); + $this->assertEquals('Migration001', $m01->getName()); + $this->assertEquals(1, $m01->getOrder()); + $this->removeClass($clazz); + $this->removeClass($clazz2); + } + /** + * @test + */ + public function test03() { + $path = APP_PATH.DS.'database'.DS.'migrations'; + $ns = '\\app\\database\\migrations'; + for ($x = 0 ; $x < 110 ; $x++) { + $runner = new MigrationsRunner($path, $ns, null); + $writter = new DatabaseMigrationWriter($runner); + if ($x < 10) { + $name = 'Migration00'.$x; + } else if ($x < 100) { + $name = 'Migration0'.$x; + } else { + $name = 'Migration'.$x; + } + $this->assertEquals($name, $writter->getName()); + $writter->writeClass(); + $clazz = "\\app\\database\\migrations\\".$name; + $this->assertTrue(class_exists($clazz)); + $xRunner = new MigrationsRunner($path, $ns, null); + + $migrations = $xRunner->getMigrations(); + $this->assertEquals($x + 1, count($migrations)); + $m = $migrations[$x]; + $this->assertTrue($m instanceof AbstractMigration); + $this->assertEquals($name, $m->getName()); + $this->assertEquals($x, $m->getOrder()); + } + foreach ($migrations as $m) { + $this->removeClass("\\app\\database\\migrations\\".$m->getName()); + } + } + private function removeClass($classPath) { + $file = new File(ROOT_PATH.$classPath.'.php'); + $file->remove(); + } +} diff --git a/tests/webfiori/framework/test/writers/MiddlewareWritterTest.php b/tests/webfiori/framework/test/writers/MiddlewareWritterTest.php index febbbdb0b..ca82771af 100644 --- a/tests/webfiori/framework/test/writers/MiddlewareWritterTest.php +++ b/tests/webfiori/framework/test/writers/MiddlewareWritterTest.php @@ -1,14 +1,14 @@ assertEquals('tests\\apis\\WebServiceTest', $w->getName(true)); $this->assertEquals(9, $w->getPhpUnitVersion()); + $this->assertEquals(ROOT_PATH.DS.'tests'.DS.'apis'.DS.'WebServiceTest.php', $w->getAbsolutePath()); $w->writeClass(); $this->assertTrue(class_exists('\\'.$w->getName(true))); - $this->removeClass($w->getAbsolutePath()); + unlink($w->getAbsolutePath()); } /** * @test @@ -31,9 +32,10 @@ public function test01() { $w->setPath(ROOT_PATH.DS.'tests'.DS.'cool'); $this->assertEquals('tests\\cool\\CoolTest', $w->getName(true)); $w->writeClass(); + $this->assertEquals(ROOT_PATH.DS.'tests'.DS.'cool'.DS.'CoolTest.php', $w->getAbsolutePath()); $this->assertTrue(file_exists($w->getAbsolutePath())); require_once $w->getAbsolutePath(); $this->assertTrue(class_exists('\\'.$w->getName(true))); - $this->removeClass($w->getAbsolutePath()); + unlink($w->getAbsolutePath()); } } diff --git a/webfiori/framework/Access.php b/webfiori/framework/Access.php index 22789d434..1ab19b434 100644 --- a/webfiori/framework/Access.php +++ b/webfiori/framework/Access.php @@ -183,7 +183,7 @@ public static function hasGroup(string $groupId): bool { * * @since 1.0 */ - public static function hasPrivilege(string $id,string $groupId = null): bool { + public static function hasPrivilege(string $id, ?string $groupId = null): bool { return Access::get()->hasPrivilegeHelper($id,$groupId); } /** @@ -204,7 +204,7 @@ public static function hasPrivilege(string $id,string $groupId = null): bool { * * @since 1.0 */ - public static function newGroup(string $groupId, $parentGroupId = null): bool { + public static function newGroup(string $groupId, ?string $parentGroupId = null): bool { return Access::get()->createGroupHelper($groupId,$parentGroupId); } /** @@ -269,7 +269,7 @@ public static function newPrivileges(string $groupId, array $prNamesArr): array * * @since 1.0 */ - public static function privileges(string $groupId = null): array { + public static function privileges(?string $groupId = null): array { return Access::get()->getPrivilegesHelper($groupId); } /** @@ -356,7 +356,7 @@ private function checkID(string $id, PrivilegesGroup $group): bool { return $bool; } - private function createGroupHelper($groupId, $parentGroupID = null): bool { + private function createGroupHelper($groupId, ?string $parentGroupID = null): bool { $trimmedId = trim($groupId); if ($this->validateId($trimmedId)) { @@ -585,7 +585,7 @@ private function getPrivilegeHelper1(string $privilegeId, PrivilegesGroup $group return null; } - private function getPrivilegesHelper($groupId = null): array { + private function getPrivilegesHelper(?string $groupId = null): array { $prArr = []; foreach ($this->userGroups as $group) { @@ -601,7 +601,7 @@ private function getPrivilegesHelper($groupId = null): array { * @param array $array * @param string|null $groupId */ - private function getPrivilegesHelper1(PrivilegesGroup $group, array &$array, string $groupId = null) { + private function getPrivilegesHelper1(PrivilegesGroup $group, array &$array, ?string $groupId = null) { if ($groupId === null) { foreach ($group->privileges() as $pr) { $array[] = $pr; @@ -688,7 +688,7 @@ private function hasPrivilegeHelper($privilegeId, $groupId) { * @param PrivilegesGroup $group * @return bool */ - private function hasPrivilegeHelper1(string $prId, PrivilegesGroup $group, string $groupId = null) : bool { + private function hasPrivilegeHelper1(string $prId, PrivilegesGroup $group, ?string $groupId = null) : bool { if ($groupId === null || $group->getID() != $groupId) { if ($groupId !== null) { return $this->isChildGroupHasPrivilege($prId, $groupId, $group); diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index 691ebddda..fac0232ad 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -26,10 +26,10 @@ use webfiori\framework\handlers\HTTPErrHandler; use webfiori\framework\middleware\AbstractMiddleware; use webfiori\framework\middleware\MiddlewareManager; +use webfiori\framework\middleware\StartSessionMiddleware; use webfiori\framework\router\Router; use webfiori\framework\router\RouterUri; use webfiori\framework\scheduler\TasksManager; -use webfiori\framework\session\SessionsManager; use webfiori\http\Request; use webfiori\http\Response; /** @@ -61,6 +61,10 @@ class App { * */ const STATUS_NONE = 'NONE'; + /** + * A constant that indicates that the status of the class is initiated. + */ + const STATUS_INITIATED = 'INITIATED'; /** * An instance of autoloader class. * @@ -87,7 +91,7 @@ class App { * * @var string */ - private static $ConfigDriver = 'webfiori\\framework\\config\\ClassDriver'; + private static $ConfigDriver = 'webfiori\\framework\\config\\JsonDriver'; /** * A single instance of the class. * @@ -105,19 +109,7 @@ class App { * @since 1.0 */ private function __construct() { - $this->checkStdInOut(); - $this->initFrameworkVersionInfo(); $this->checkAppDir(); - /** - * Change encoding of mb_ functions to UTF-8 - */ - if (function_exists('mb_internal_encoding')) { - $encoding = 'UTF-8'; - mb_internal_encoding($encoding); - mb_http_output($encoding); - mb_regex_encoding($encoding); - } - $this->initAutoLoader(); $this->setHandlers(); Controller::get()->updateEnv(); /** @@ -130,13 +122,11 @@ private function __construct() { */ date_default_timezone_set(defined('DATE_TIMEZONE') ? DATE_TIMEZONE : 'Asia/Riyadh'); - //Initialize CLI self::getRunner(); $this->initThemesPath(); - $this->checkStandardLibs(); - + if (!class_exists(APP_DIR.'\ini\InitPrivileges')) { Ini::get()->createIniClass('InitPrivileges', 'Initialize user groups and privileges.'); } @@ -144,8 +134,6 @@ private function __construct() { //This step must be done before initializing anything. self::call(APP_DIR.'\ini\InitPrivileges::init'); - - $this->initMiddleware(); $this->initRoutes(); $this->initScheduler(); @@ -153,31 +141,23 @@ private function __construct() { { register_shutdown_function(function() { - SessionsManager::validateStorage(); $uriObj = Router::getRouteUri(); - if ($uriObj !== null) { - foreach ($uriObj->getMiddleware() as $mw) { - $mw->afterSend(Request::get(), Response::get()); + $mdArr = $uriObj->getMiddleware(); + + for ($x = count($mdArr) - 1 ; $x > 0 ; $x--) { + $mdArr[$x]->afterSend(Request::get(), Response::get()); } } }); - try { - $sessionsCookiesHeaders = SessionsManager::getCookiesHeaders(); - - foreach ($sessionsCookiesHeaders as $headerVal) { - Response::addHeader('set-cookie', $headerVal); - } - } catch (Error $exc) { - } $uriObj = Router::getRouteUri(); if ($uriObj !== null) { - $uriObj->getMiddleware()->insertionSort(); + $mdArr = $uriObj->getMiddleware(); - foreach ($uriObj->getMiddleware() as $mw) { - $mw->after(Request::get(), Response::get()); + for ($x = count($mdArr) - 1 ; $x > 0 ; $x--) { + $mdArr[$x]->after(Request::get(), Response::get()); } } }); @@ -207,7 +187,7 @@ private function __construct() { * * @since 1.3.6 */ - public static function autoRegister(string $folder, callable $regCallback, string $suffix = null, array $constructorParams = [], array $otherParams = []) { + public static function autoRegister(string $folder, callable $regCallback, ?string $suffix = null, array $constructorParams = [], array $otherParams = []) { $dir = APP_PATH.$folder; if (!File::isDirectory($dir)) { @@ -264,10 +244,8 @@ public static function getClassLoader(): ClassLoader { * the constructor of the class is not called. 'INITIALIZING' if the execution * is happening inside the constructor of the class. 'INITIALIZED' once the * code in the constructor is executed. - * - * @since 1.0 */ - public static function getClassStatus() { + public static function getClassStatus() : string { return self::$ClassStatus; } /** @@ -294,7 +272,104 @@ public static function getConfig(): ConfigurationDriver { public static function getConfigDriver() : string { return self::$ConfigDriver; } - + private static function getRoot() { + //Following lines of code assumes that the class exist on the folder: + //\vendor\webfiori\framework\webfiori\framework + //Its used to construct the folder at which index file will exist at + $DS = DIRECTORY_SEPARATOR; + $vendorPath = $DS.'vendor'.$DS.'webfiori'.$DS.'framework'.$DS.'webfiori'.$DS.'framework'; + $rootPath = substr(__DIR__, 0, strlen(__DIR__) - strlen($vendorPath)); + return $rootPath; + } + /** + * Handel the request. + * + * This method should only be called after the application has been initialized. + * Its used to handle HTTP requests or start CLI processing. + */ + public static function handle() { + + if (self::$ClassStatus == self::STATUS_NONE) { + $publicFolderName = 'public'; + self::initiate('app', $publicFolderName, self::getRoot().DIRECTORY_SEPARATOR.$publicFolderName); + } + if (self::$ClassStatus == self::STATUS_INITIATED) { + self::start(); + } + if (self::$ClassStatus == self::STATUS_INITIALIZED) { + if (App::getRunner()->isCLI() === true) { + App::getRunner()->start(); + } else { + //route user request. + Router::route(Request::getRequestedURI()); + Response::send(); + } + } + } + /** + * Initiate main components of the application. + * + * This method is intended to be called in the index file of the project. + * It should be first thing to be called. + * + * @param string $appFolder The name of the folder at which the application + * is created at. + * + * @param string $publicFolder A string that represent the name of the public + * folder such as 'public'. + * + * @param string $indexDir The directory at which index file exist at. + * Usually, its the value of the constant __DIR__. + */ + public static function initiate(string $appFolder = 'app', string $publicFolder = 'public', string $indexDir = __DIR__) { + /** + * Change encoding of mb_ functions to UTF-8 + */ + if (function_exists('mb_internal_encoding')) { + $encoding = 'UTF-8'; + mb_internal_encoding($encoding); + mb_http_output($encoding); + mb_regex_encoding($encoding); + } + if (!defined('DS')) { + /** + * Directory separator. + */ + define('DS', DIRECTORY_SEPARATOR); + } + if (!defined('ROOT_PATH')) { + if ($indexDir == __DIR__) { + $indexDir = self::getRoot().DS.$publicFolder; + } + /** + * Path to source folder. + */ + define('ROOT_PATH', substr($indexDir,0, strlen($indexDir) - strlen(DS.$publicFolder))); + } + if (!defined('APP_DIR')) { + /** + * Name of application directory. + */ + define('APP_DIR', $appFolder); + } + if (!defined('APP_PATH')) { + /** + * Path to application directory. + */ + define('APP_PATH', ROOT_PATH.DIRECTORY_SEPARATOR.APP_DIR.DS); + } + if (!defined('WF_CORE_PATH')) { + /** + * Path to WebFiori's core library. + */ + define('WF_CORE_PATH', ROOT_PATH.DS.'vendor'.DS.'webfiori'.DS.'framework'.DS.'webfiori'.DS.'framework'); + } + self::initAutoLoader(); + self::checkStandardLibs(); + self::checkStdInOut(); + self::initFrameworkVersionInfo(); + self::$ClassStatus = self::STATUS_INITIATED; + } /** * Returns an instance which represents the class that is used to run the * terminal. @@ -347,6 +422,7 @@ public static function getRunner() : Runner { '\\webfiori\\framework\\cli\\commands\\RunSQLQueryCommand', '\\webfiori\\framework\\cli\\commands\\UpdateSettingsCommand', '\\webfiori\\framework\\cli\\commands\\UpdateTableCommand', + '\\webfiori\\framework\\cli\\commands\\RunMigrationsCommand', ]; foreach ($commands as $c) { @@ -380,12 +456,12 @@ public static function setConfigDriver(string $clazz) { * @since 1.0 */ public static function start(): App { - if (self::$ClassStatus == 'NONE') { + if (self::$ClassStatus == self::STATUS_NONE || self::$ClassStatus == self::STATUS_INITIATED) { if (self::$LC === null) { - self::$ClassStatus = 'INITIALIZING'; + self::$ClassStatus = self::STATUS_INITIALIZING; self::$LC = new App(); } - } else if (self::$ClassStatus == 'INITIALIZING') { + } else if (self::$ClassStatus == self::STATUS_INITIALIZING) { throw new InitializationException('Using the core class while it is not fully initialized.'); } @@ -423,17 +499,13 @@ private static function call($func) { } catch (Exception $ex) { if (self::getRunner()->isCLI()) { printf("WARNING: ".$ex->getMessage().' at '.$ex->getFile().':'.$ex->getLine()."\n"); + } else { + throw new InitializationException($ex->getMessage(), $ex->getCode(), $ex); } } } private function checkAppDir() { - if (!defined('DS')) { - /** - * Directory separator. - */ - define('DS', DIRECTORY_SEPARATOR); - } - + if (!defined('APP_DIR')) { /** * The name of the directory at which the developer will have his own application @@ -468,7 +540,7 @@ private function checkAppDir() { * @throws InitializationException * @since 1.3.5 */ - private function checkStandardLibs() { + private static function checkStandardLibs() { $standardLibsClasses = [ 'webfiori/collections' => 'webfiori\\collections\\Node', 'webfiori/ui' => 'webfiori\\ui\\HTMLNode', @@ -478,7 +550,8 @@ private function checkStandardLibs() { 'webfiori/file' => 'webfiori\\file\\File', 'webfiori/mailer' => 'webfiori\\email\\SMTPAccount', 'webfiori/cli' => 'webfiori\\cli\\CLICommand', - 'webfiori/err' => 'webfiori\\error\\ErrorHandlerException' + 'webfiori/err' => 'webfiori\\error\\ErrorHandlerException', + 'webfiori/cache' => 'webfiori\\cache\\Cache' ]; foreach ($standardLibsClasses as $lib => $class) { @@ -491,7 +564,7 @@ private function checkStandardLibs() { /** * Checks and initialize standard input and output streams. */ - private function checkStdInOut() { + private static function checkStdInOut() { /* * first, check for php streams if they are open or not. */ @@ -532,12 +605,13 @@ private function checkStdInOut() { * @throws FileException * @throws Exception */ - private function initAutoLoader() { + private static function initAutoLoader() { /** * Initialize autoloader. */ if (!class_exists('webfiori\framework\autoload\ClassLoader',false)) { - require_once WF_CORE_PATH.DIRECTORY_SEPARATOR.'autoload'.DIRECTORY_SEPARATOR.'ClassLoader.php'; + $autoloader = WF_CORE_PATH.DIRECTORY_SEPARATOR.'autoload'.DIRECTORY_SEPARATOR.'ClassLoader.php'; + require_once $autoloader; } self::$AU = ClassLoader::get(); @@ -547,13 +621,23 @@ private function initAutoLoader() { } self::call(APP_DIR.'\ini\InitAutoLoad::init'); } - private function initFrameworkVersionInfo() { + /** + * Initialize global constants which has information about framework version. + * + * The constants which are defined by this method include the following: + *

+ */ + public static function initFrameworkVersionInfo() { /** * A constant that represents version number of the framework. * * @since 2.1 */ - define('WF_VERSION', '3.0.0-Beta.13'); + define('WF_VERSION', '3.0.0-Beta.26'); /** * A constant that tells the type of framework version. * @@ -569,7 +653,7 @@ private function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_RELEASE_DATE', '2024-10-29'); + define('WF_RELEASE_DATE', '2025-04-07'); } /** @@ -584,6 +668,7 @@ private function initMiddleware() { if (!class_exists(APP_DIR.'\ini\InitMiddleware')) { Ini::get()->createIniClass('InitMiddleware', 'Register middleware which are created outside the folder \'[APP_DIR]/middleware\'.'); } + MiddlewareManager::register(new StartSessionMiddleware()); self::call(APP_DIR.'\ini\InitMiddleware::init'); } /** diff --git a/webfiori/framework/Ini.php b/webfiori/framework/Ini.php index e92a8b8a7..ef8fb62f7 100644 --- a/webfiori/framework/Ini.php +++ b/webfiori/framework/Ini.php @@ -13,6 +13,7 @@ use webfiori\file\exceptions\FileException; use webfiori\file\File; use webfiori\framework\config\ClassDriver; +use webfiori\json\Json; /** * A class which is used to create application initialization classes. * @@ -25,6 +26,7 @@ class Ini { private $docEmptyLine; private $docEnd; private $docStart; + private static $DIR_TO_CREATE; /** * An instance of the class. * @@ -150,11 +152,16 @@ public static function get(): Ini { return self::$singleton; } public static function mkdir($dir) { + self::$DIR_TO_CREATE = $dir; if (!is_dir($dir)) { - set_error_handler(function (int $errno, string $errstr) - { + set_error_handler(function (int $errno, string $errstr) { http_response_code(500); - die('Unable to create one or more of application directories due to an error: "Code: '.$errno.', Message: '.$errstr.'"'); + header('content-type:application/json'); + die('{' + . '"message":"Unable to create application directory due to an error: '.$errstr.'",' + . '"code":'.$errno.',' + . '"dir":"'.Json::escapeJSONSpecialChars(self::$DIR_TO_CREATE).'"' + . '}'); }); mkdir($dir); restore_error_handler(); diff --git a/webfiori/framework/Lang.php b/webfiori/framework/Lang.php index ba693dbfd..f925a616b 100644 --- a/webfiori/framework/Lang.php +++ b/webfiori/framework/Lang.php @@ -219,7 +219,7 @@ public function getCode() : string { * @param string $dir A directory to the language variable (such as 'pages/login/login-label'). * This also can be a string similar to 'pages.login.login-label'. * - * @param string $langCode An optional language code. If provided, the + * @param string|null $langCode An optional language code. If provided, the * method will attempt to replace active language with the provided * one. If not provided, the method * will attempt to load a translation based on the session or default @@ -233,7 +233,7 @@ public function getCode() : string { * @throws MissingLangException * @since 1.0 */ - public static function getLabel(string $dir, string $langCode = null) { + public static function getLabel(string $dir, ?string $langCode = null) { if ($langCode === null) { $session = SessionsManager::getActiveSession(); diff --git a/webfiori/framework/PrivilegesGroup.php b/webfiori/framework/PrivilegesGroup.php index 81248f9ea..b55d15318 100644 --- a/webfiori/framework/PrivilegesGroup.php +++ b/webfiori/framework/PrivilegesGroup.php @@ -311,7 +311,7 @@ public function setName(string $name) : bool { * * @since 1.1 */ - public function setParentGroup(PrivilegesGroup $group = null) : bool { + public function setParentGroup(?PrivilegesGroup $group = null) : bool { if ($group !== null) { if ($group !== $this && $group->getID() != $this->getID()) { $this->parentGroup = $group; diff --git a/webfiori/framework/ThemeLoader.php b/webfiori/framework/ThemeLoader.php index 7ae18ffd3..f03ca68f3 100644 --- a/webfiori/framework/ThemeLoader.php +++ b/webfiori/framework/ThemeLoader.php @@ -175,7 +175,7 @@ public static function resetLoaded() { * * @since 1.0 */ - public static function usingTheme(string $themeName = null) { + public static function usingTheme(?string $themeName = null) { $trimmedName = trim((string)$themeName); if (strlen($trimmedName) != 0) { @@ -253,7 +253,9 @@ private static function scanDir($filesInDir, $pathToScan, $dirName) { if ($fileExt == '.php') { $cName = str_replace('.php', '', $fileName); + ob_start(); $ns = require_once $pathToScan.DS.$fileName; + ob_end_clean(); $aNs = gettype($ns) == 'string' ? $ns.'\\' : '\\'; $aCName = $aNs.$cName; diff --git a/webfiori/framework/User.php b/webfiori/framework/User.php index 975e54087..be3d9d1de 100644 --- a/webfiori/framework/User.php +++ b/webfiori/framework/User.php @@ -453,7 +453,7 @@ public function setLastLogin(string $date) { * * @since 1.6 */ - public function setLastPasswordResetDate(string $date = null) { + public function setLastPasswordResetDate(?string $date = null) { $this->lastPasswordReset = $date; } /** diff --git a/webfiori/framework/autoload/ClassLoader.php b/webfiori/framework/autoload/ClassLoader.php index 3a55cc044..980e2f049 100644 --- a/webfiori/framework/autoload/ClassLoader.php +++ b/webfiori/framework/autoload/ClassLoader.php @@ -318,7 +318,7 @@ public static function getCachePath() : string { * loaded. * */ - public static function getClassPath(string $className, string $namespace = null, bool $load = false): array { + public static function getClassPath(string $className, ?string $namespace = null, bool $load = false): array { $retVal = []; if ($load === true) { @@ -397,7 +397,7 @@ public static function getLoadedClasses(): array { * * @throws Exception */ - public static function isLoaded(string $class, string $ns = null): bool { + public static function isLoaded(string $class, ?string $ns = null): bool { foreach (self::getLoadedClasses() as $classArr) { if ($ns !== null) { if ($class == $classArr[ClassInfo::NAME] diff --git a/webfiori/framework/cli/CLIUtils.php b/webfiori/framework/cli/CLIUtils.php index 70604e760..7e4e52f3f 100644 --- a/webfiori/framework/cli/CLIUtils.php +++ b/webfiori/framework/cli/CLIUtils.php @@ -33,7 +33,7 @@ public static function getConnectionName(CLICommand $c) { $dbConnectionsNames = array_keys($dbConnections); if (count($dbConnectionsNames) == 0) { - $c->warning('No database connections found in the class "'.APP_DIR.'\\AppConfig"!'); + $c->warning('No database connections found in application configuration.'); $c->info('Run the command "add" to add connections.'); return null; @@ -61,10 +61,9 @@ public static function getConnectionName(CLICommand $c) { * * @return string A string that represents a valid class name. */ - public static function readClassName(CLICommand $c, string $suffix = null, string $prompt = 'Enter class name:', string $errMsg = 'Invalid class name is given.') : string { + public static function readClassName(CLICommand $c, ?string $suffix = null, string $prompt = 'Enter class name:', string $errMsg = 'Invalid class name is given.') : string { do { - $c->readClassName($prompt, $suffix, $errMsg); - $className = trim($c->getInput($prompt)); + $className = $c->readClassName($prompt, $suffix, $errMsg); if ($suffix !== null) { $subSuffix = substr($className, strlen($className) - strlen($suffix)); @@ -149,4 +148,29 @@ public static function readTable(CLICommand $c) : Table { return $tableObj; } + /** + * Reads and returns the name of a database connection. + * + * This method will display a list of all stored connections in the configuration + * and returns one of them. + * + * @return string The name of selected connection. An empty string is returned + * if none is selected. + */ + public function getConnection() : string { + $dbConnections = array_keys(App::getConfig()->getDBConnections()); + + if (count($dbConnections) != 0) { + $dbConnections[] = 'None'; + $conn = $this->select('Select database connecion:', $dbConnections, count($dbConnections) - 1); + + if ($conn != 'None') { + return $conn; + } + } else { + $this->warning('No database connections were found.'); + } + + return ''; + } } diff --git a/webfiori/framework/cli/commands/CreateCommand.php b/webfiori/framework/cli/commands/CreateCommand.php index 10b5d6795..b1695e8ae 100644 --- a/webfiori/framework/cli/commands/CreateCommand.php +++ b/webfiori/framework/cli/commands/CreateCommand.php @@ -20,6 +20,7 @@ use webfiori\framework\cli\helpers\CreateDBAccessHelper; use webfiori\framework\cli\helpers\CreateFullRESTHelper; use webfiori\framework\cli\helpers\CreateMiddleware; +use webfiori\framework\cli\helpers\CreateMigration; use webfiori\framework\cli\helpers\CreateTableObj; use webfiori\framework\cli\helpers\CreateThemeHelper; use webfiori\framework\cli\helpers\CreateWebService; @@ -115,6 +116,17 @@ public function exec() : int { if (!$create->readClassInfo()) { return -1; } + } else if ($answer == 'Database migration.') { + $create = new CreateMigration($this); + if ($create->isConfigured()) { + $create->writeClass(); + $writer = $create->getWriter(); + $this->info("Migration Name: ".$writer->getMigrationName()); + $this->info("Migration Order: ".$writer->getMigrationOrder()); + return 0; + } else { + return -1; + } } return 0; @@ -131,6 +143,7 @@ private function getWhat() { $options['db'] = 'Database access class based on table.'; $options['rest'] = 'Complete REST backend (Database table, entity, database access and web services).'; $options['api-test'] = 'Web service test case.'; + $options['migration'] = 'Database migration.'; $options['q'] = 'Quit.'; $what = $this->getArgValue('--c'); $answer = null; diff --git a/webfiori/framework/cli/commands/RunMigrationsCommand.php b/webfiori/framework/cli/commands/RunMigrationsCommand.php new file mode 100644 index 000000000..88b7eea7e --- /dev/null +++ b/webfiori/framework/cli/commands/RunMigrationsCommand.php @@ -0,0 +1,303 @@ +isArgProvided('--ini')) { + return 0; + } + if ($conn === null) { + $conn = $this->getDBConnection($runner); + } + if ($conn !== null) { + + try { + $this->println("Initializing migrations table..."); + $temp = $runner !== null ? $runner : new MigrationsRunner(APP_PATH, '\\'.APP_DIR, $conn); + + $temp->createMigrationsTable(); + $this->success("Migrations table succesfully created."); + } catch (\Throwable $ex) { + $this->error('Unable to create migrations table due to following:'); + $this->println($ex->getMessage()); + return -1; + } + return 0; + } + return 0; + } + private function getNS(?MigrationsRunner $runner = null) { + if ($this->isArgProvided('--ns')) { + return $this->getArgValue('--ns'); + } else if ($runner !== null) { + return $runner->getMigrationsNamespace(); + } else { + $this->info("Using default namespace for migrations."); + return '\\'.APP_DIR.'\\database\\migrations'; + } + } + + /** + * Execute the command. + * + * @return int 0 in case of success. Other value if failed. + */ + public function exec() : int { + + $runner = $this->getRunnerArg(); + + if (!($runner instanceof MigrationsRunner) && $runner !== null) { + return -1; + } + $ns = $this->getNS($runner); + + if (!$this->hasMigrations($ns)) { + return 0; + } + + $connection = $this->getDBConnection($runner); + if (!($connection instanceof ConnectionInfo)) { + return -1; + } + + if ($this->checkMigrationsTable($runner, $connection) == -1) { + return -1; + } + + + try { + $runner = new MigrationsRunner(ROOT_PATH.DS. str_replace('\\', DS, $ns), $ns, $connection); + } catch (Throwable $ex) { + $this->error($ex->getMessage()); + return -1; + } + if ($this->isArgProvided("--rollback")) { + return $this->rollbackMigration($runner); + } else { + return $this->executeMigrations($runner); + } + } + private function rollbackMigration(MigrationsRunner $runner) { + $isAll = $this->isArgProvided('--all'); + $rolledCount = 0; + if ($isAll) { + $this->println("Rolling back migrations..."); + do { + $migration = $this->doRollback($runner); + if ($migration === false) { + return -1; + } + $this->printInfo($migration, $rolledCount); + } while ($migration !== null); + } else { + $this->println("Rolling back last executed migration..."); + $migration = $this->doRollback($runner); + if ($migration === false) { + return -1; + } + $this->printInfo($migration, $rolledCount); + } + if ($rolledCount == 0) { + $this->info("No migration rolled back."); + } + + + return 0; + } + private function doRollback(MigrationsRunner $runner) { + try { + return $runner->rollback(); + + } catch (Throwable $ex) { + $this->error('Failed to execute migration due to following:'); + $this->println($ex->getMessage().' (Line '.$ex->getLine().')'); + $this->warning('Execution stopped.'); + return false; + } + } + private function printInfo(?AbstractMigration $migration, &$rolledCount = 0) { + if ($migration !== null) { + $rolledCount++; + $this->success("Migration '".$migration->getName()."' was successfully rolled back."); + } + } + private function executeMigrations(MigrationsRunner $runner) { + $this->println("Starting to execute migrations..."); + $listOfApplied = []; + while ($this->applyNext($runner, $listOfApplied)){}; + + if (count($listOfApplied) != 0) { + $this->info("Number of applied migrations: ".count($listOfApplied)); + $this->println("Names of applied migrations:"); + $this->printList(array_map(function (AbstractMigration $migration) { + return $migration->getName(); + }, $listOfApplied)); + } else { + $this->info("No migrations were executed."); + } + return 0; + } + /** + * Returns the connection that will be used in running the migrations. + * + * The method will first check on the provided runner. If it has connection, + * it will be returned. Then it will check if the argument '--connection' is + * provided or not. If provided, the method will check if such connection + * exist in application configuration. If no connection was found, null + * is returned. If the argument '--connection' is not provided, the method will + * ask the user to select a connection from the connections which + * exist in application configuration. + * + * @param MigrationsRunner|null $runner If given and the connection is set + * on the instance, it will be returned. + * + * @return ConnectionInfo|null + */ + private function getDBConnection(?MigrationsRunner $runner = null) { + + if ($runner !== null) { + if ($runner->getConnectionInfo() !== null) { + return $runner->getConnectionInfo(); + } + } + if (!$this->hasConnections()) { + return -1; + } + $dbConnections = array_keys(App::getConfig()->getDBConnections()); + + if ($this->isArgProvided('--connection')) { + $connection = $this->getArgValue('--connection'); + + if (!in_array($connection, $dbConnections)) { + $this->error("No connection was found which has the name '$connection'."); + return -1; + } else { + return App::getConfig()->getDBConnection($connection); + } + } else { + return CLIUtils::getConnectionName($this); + } + } + private function applyNext(MigrationsRunner $runner, &$listOfApplied) : bool { + try { + //$this->println("Executing migration..."); + $applied = $runner->applyOne(); + + if ($applied !== null) { + $this->success("Migration '".$applied->getName()."' applied successfuly."); + $listOfApplied[] = $applied; + return true; + } else { + return false; + } + } catch (Throwable $ex) { + $this->error('Failed to execute migration due to following:'); + $this->println($ex->getMessage().' (Line '.$ex->getLine().')'); + $this->warning('Execution stopped.'); + return false; + } + } + /** + * + * @return MigrationsRunner|int|null + */ + private function getRunnerArg() { + $runner = $this->getArgValue('--runner'); + + if ($runner === null) { + return null; + } + + if (class_exists($runner)) { + try { + $runnerInst = new $runner(); + } catch (Throwable $exc) { + $this->error('The argument --runner has invalid value: Exception: "'.$exc->getMessage().'".'); + return -1; + } + + if (!($runnerInst instanceof MigrationsRunner)) { + $this->error('The argument --runner has invalid value: "'.$runner.'" is not an instance of "MigrationsRunner".'); + return -1; + } else { + return $runnerInst; + } + } else { + $this->error('The argument --runner has invalid value: Class "'.$runner.'" does not exist.'); + return -1; + } + } + private function hasConnections() : bool { + $dbConnections = App::getConfig()->getDBConnections(); + if (count($dbConnections) == 0) { + $this->info('No connections were found in application configuration.'); + return false; + } + return true; + } + private function hasMigrations(string $namespace) : bool { + $tmpRunner = new MigrationsRunner(ROOT_PATH.DS.str_replace('\\', DS, $namespace), $namespace, null); + $this->println("Checking namespace '$namespace' for migrations..."); + $count = count($tmpRunner->getMigrations()); + if ($count == 0) { + $this->info("No migrations found in the namespace '$namespace'."); + return false; + } + $this->info("Found $count migration(s) in the namespace '$namespace'."); + return true; + } +} diff --git a/webfiori/framework/cli/commands/UpdateSettingsCommand.php b/webfiori/framework/cli/commands/UpdateSettingsCommand.php index be850b020..b1345151d 100644 --- a/webfiori/framework/cli/commands/UpdateSettingsCommand.php +++ b/webfiori/framework/cli/commands/UpdateSettingsCommand.php @@ -33,7 +33,7 @@ public function __construct() { .'Possible values are: version, app-name, scheduler-pass, page-title, ' .'page-description, primary-lang, title-sep, home-page, theme,' .'admin-theme.', true), - ], 'Update application settings which are stored in the class "AppConfig".'); + ], 'Update application settings which are stored in specific configuration driver.'); } public function exec() : int { $options = []; diff --git a/webfiori/framework/cli/helpers/ClassInfoReader.php b/webfiori/framework/cli/helpers/ClassInfoReader.php index 933df8414..3606546a1 100644 --- a/webfiori/framework/cli/helpers/ClassInfoReader.php +++ b/webfiori/framework/cli/helpers/ClassInfoReader.php @@ -45,7 +45,7 @@ public function __construct(CLICommand $owner) { * * @return string A string that represents the name of the class. */ - public function getName($suffix = null, $errMsg = 'Invalid class name is given.') { + public function getName(?string $suffix = null, $errMsg = 'Invalid class name is given.') { return $this->getOwner()->readClassName('Enter a name for the new class:', $suffix, $errMsg); } /** @@ -74,7 +74,7 @@ public function getOwner() { * @param string $defaultNs An optional default namespace to use in case the * user did not provide a one. Note that this also will be the default path. * - * @param string $suffix An optional string which will be appended to the + * @param string|null $suffix An optional string which will be appended to the * name of the class. * * @return array The method will return an array that contains 3 indices: @@ -85,7 +85,7 @@ public function getOwner() { *
  • path: The location at which the class will be created.
  • * */ - public function readClassInfo($defaultNs = null, $suffix = null) { + public function readClassInfo(?string $defaultNs = null, ?string $suffix = null) { $classExist = true; do { diff --git a/webfiori/framework/cli/helpers/CreateClassHelper.php b/webfiori/framework/cli/helpers/CreateClassHelper.php index aad7e229b..d576d1458 100644 --- a/webfiori/framework/cli/helpers/CreateClassHelper.php +++ b/webfiori/framework/cli/helpers/CreateClassHelper.php @@ -43,7 +43,7 @@ class CreateClassHelper { * * @param ClassWriter $writer The writer that will hold class information. */ - public function __construct(CLICommand $command, ClassWriter $writer = null) { + public function __construct(CLICommand $command, ?ClassWriter $writer = null) { $this->command = $command; $this->classWriter = $writer; $this->classInfoReader = new ClassInfoReader($this->command); @@ -60,16 +60,14 @@ public function __construct(CLICommand $command, ClassWriter $writer = null) { * * @param string $confirmTxt The text of the question which will be asked. * - * @return boolean If the user choose 'y', the method will return true. If - * he choose 'n', the method will return false. - * * @param boolean|null $default Default answer to use if empty input is given. * It can be true for 'y' and false for 'n'. Default value is null which * means no default will be used. - * - * + * + * @return boolean If the user choose 'y', the method will return true. If + * he choose 'n', the method will return false. */ - public function confirm(string $confirmTxt, $default = null) { + public function confirm(string $confirmTxt, ?bool $default = null) { return $this->getCommand()->confirm($confirmTxt, $default); } /** @@ -92,7 +90,7 @@ public function error(string $message) { * @param string $suffix A string to append to the name of the class if it * was not in provided name. */ - public function getClassInfo($defaultNs = null, $suffix = null) { + public function getClassInfo(?string $defaultNs = null, ?string $suffix = null) { return $this->classInfoReader->readClassInfo($defaultNs, $suffix); } /** @@ -126,7 +124,7 @@ public function getCommand() : CLICommand { * user. * */ - public function getInput(string $prompt, string $default = null, InputValidator $validator = null) { + public function getInput(string $prompt, ?string $default = null, ?InputValidator $validator = null) { return $this->getCommand()->getInput($prompt, $default, $validator); } /** diff --git a/webfiori/framework/cli/helpers/CreateDBAccessHelper.php b/webfiori/framework/cli/helpers/CreateDBAccessHelper.php index a571a23ed..e993c8bce 100644 --- a/webfiori/framework/cli/helpers/CreateDBAccessHelper.php +++ b/webfiori/framework/cli/helpers/CreateDBAccessHelper.php @@ -53,7 +53,7 @@ public function getTable() : Table { public function readDbClassInfo() { $info = $this->getClassInfo(APP_DIR.'\\database', 'DB'); $this->getWriter()->setNamespace($info['namespace']); - $this->getWriter()->setPath($info['namespace']); + $this->getWriter()->setPath(ROOT_PATH.DS.$info['namespace']); $this->getWriter()->setClassName($info['name']); $this->getWriter()->setConnection($this->getConnection()); } diff --git a/webfiori/framework/cli/helpers/CreateFullRESTHelper.php b/webfiori/framework/cli/helpers/CreateFullRESTHelper.php index af5c3b18d..25978d388 100644 --- a/webfiori/framework/cli/helpers/CreateFullRESTHelper.php +++ b/webfiori/framework/cli/helpers/CreateFullRESTHelper.php @@ -232,7 +232,7 @@ private function readTableInfo() { $this->println("Now, time to collect database table information."); $ns = CLIUtils::readNamespace($this->getCommand(), APP_DIR.'\\database', 'Provide us with a namespace for table class:'); $this->tableObjWriter->setNamespace($ns); - $this->tableObjWriter->setPath($ns); + $this->tableObjWriter->setPath(ROOT_PATH.DS.$ns); $create = new CreateTableObj($this->getCommand()); $create->getWriter()->setTable($this->tableObjWriter->getTable()); @@ -240,7 +240,7 @@ private function readTableInfo() { $tableHelper->setTableName(); $tableHelper->setTableComment(); $tableHelper->getCreateHelper()->setNamespace($ns); - $tableHelper->getCreateHelper()->setPath($ns); + $tableHelper->getCreateHelper()->setPath(ROOT_PATH.DS.$ns); $tableHelper->getCreateHelper()->setClassName($this->tableObjWriter->getName()); $this->println('Now you have to add columns to the table.'); $tableHelper->addColumns(); @@ -319,7 +319,7 @@ private function writeServices() { $writer->addUseStatement($t->getEntityMapper()->getEntityName(true)); $writer->addUseStatement(Json::class); $writer->setNamespace($this->apisNs); - $writer->setPath($this->apisNs); + $writer->setPath(ROOT_PATH.DS.$this->apisNs); $writer->setClassName($sName); $apiType = $serviceProps['type']; diff --git a/webfiori/framework/cli/helpers/CreateMigration.php b/webfiori/framework/cli/helpers/CreateMigration.php new file mode 100644 index 000000000..b624701c5 --- /dev/null +++ b/webfiori/framework/cli/helpers/CreateMigration.php @@ -0,0 +1,88 @@ +isConfigured = false; + if (!$command->isArgProvided('--defaults')) { + $ns = CLIUtils::readNamespace($command, $ns , 'Migration namespace:'); + } + + $runner = $this->initRunner($ns, $command); + if ($runner === null) { + $command->error("Unable to set migrations path."); + } else { + parent::__construct($command, new DatabaseMigrationWriter($runner)); + $this->writer = $this->getWriter(); + $this->setNamespace($ns); + + $this->isConfigured = true; + if (!$command->isArgProvided('--defaults')) { + $this->setClassName($command->readClassName('Provide an optional name for the class that will have migration logic:', null)); + $this->readClassInfo(); + + } + } + } + public function isConfigured() : bool { + return $this->isConfigured; + } + private function initRunner($ns, $command) { + $path = ROOT_PATH.DS. str_replace('\\', DS, $ns); + if (!is_dir($path)) { + $command->warning("The path '$path' does not exist."); + $create = $command->confirm("Would you like to create it?", true); + + if ($create) { + if (!mkdir($path)) { + $command->error("Unable to create directory."); + return null; + } + } else { + return null; + } + } + return new MigrationsRunner($path, $ns, null); + } + + private function readClassInfo() { + + $name = $this->getInput('Enter an optional name for the migration:', $this->writer->getMigrationName()); + $order = $this->getCommand()->readInteger('Enter an optional execution order for the migration:', $this->writer->getMigrationOrder()); + + + $this->writer->setMigrationName($name); + $this->writer->setMigrationOrder($order); + } +} diff --git a/webfiori/framework/cli/helpers/TableObjHelper.php b/webfiori/framework/cli/helpers/TableObjHelper.php index f1198a5ee..e79795738 100644 --- a/webfiori/framework/cli/helpers/TableObjHelper.php +++ b/webfiori/framework/cli/helpers/TableObjHelper.php @@ -337,7 +337,7 @@ public function setTableComment() { * @param string $defaultName A string to set as default table name in case * of hitting 'enter' without providing a value. */ - public function setTableName($defaultName = null) { + public function setTableName(?string $defaultName = null) { $invalidTableName = true; $helper = $this->getCreateHelper(); diff --git a/webfiori/framework/config/ClassDriver.php b/webfiori/framework/config/ClassDriver.php index ca230c84a..570c0f488 100644 --- a/webfiori/framework/config/ClassDriver.php +++ b/webfiori/framework/config/ClassDriver.php @@ -67,7 +67,7 @@ public static function a($file, $str, $tabSize = 0) { * @param string $description An optional description to describe the porpuse * of the constant. */ - public function addEnvVar(string $name, $value = null, string $description = null) { + public function addEnvVar(string $name, mixed $value = null, ?string $description = null) { $this->configVars['env-vars'][$name] = [ 'value' => $value, 'description' => $description @@ -889,12 +889,16 @@ private function initDefaultConfig() { 'version-info' => [ 'version' => '1.0', 'version-type' => 'Stable', - 'release-date' => '2021-01-10' + 'release-date' => date('Y-m-d') ], 'env-vars' => [ 'WF_VERBOSE' => [ 'value' => false, 'description' => 'Configure the verbosity of error messsages at run-time. This should be set to true in testing and false in production.' + ], + "CLI_HTTP_HOST" => [ + "value" => "127.0.0.1", + "description" => "Host name that will be used when runing the application as command line utility." ] ], 'site' => [ diff --git a/webfiori/framework/config/ConfigurationDriver.php b/webfiori/framework/config/ConfigurationDriver.php index d41af29a0..f807ecd07 100644 --- a/webfiori/framework/config/ConfigurationDriver.php +++ b/webfiori/framework/config/ConfigurationDriver.php @@ -27,7 +27,7 @@ interface ConfigurationDriver { * @param string $description An optional description to describe the porpuse * of the constant. */ - public function addEnvVar(string $name, $value = null, string $description = null); + public function addEnvVar(string $name, mixed $value = null, ?string $description = null); /** * Adds new database connections information or update existing connections. * diff --git a/webfiori/framework/config/Controller.php b/webfiori/framework/config/Controller.php index 843e81c54..0229e65c0 100644 --- a/webfiori/framework/config/Controller.php +++ b/webfiori/framework/config/Controller.php @@ -36,7 +36,7 @@ public function __construct() { * * @param string|null $description An optional text that describes the variable. */ - public function addEnvVar(string $name, $value, string $description = null) { + public function addEnvVar(string $name, $value, ?string $description = null) { $this->getDriver()->addEnvVar($name, $value, $description); } /** diff --git a/webfiori/framework/config/JsonDriver.php b/webfiori/framework/config/JsonDriver.php index bd56c0433..5d6ad10ed 100644 --- a/webfiori/framework/config/JsonDriver.php +++ b/webfiori/framework/config/JsonDriver.php @@ -64,7 +64,7 @@ public function __construct() { 'description' => 'Configure the verbosity of error messsages at run-time. This should be set to true in testing and false in production.' ], 'none', 'same'), "CLI_HTTP_HOST" => new Json([ - "value" => "example.com", + "value" => "127.0.0.1", "description" => "Host name that will be used when runing the application as command line utility." ], 'none', 'same') ], 'none', 'same'), @@ -87,7 +87,7 @@ public function __construct() { * @param string $description An optional description to describe the porpuse * of the constant. */ - public function addEnvVar(string $name, $value = null, string $description = null) { + public function addEnvVar(string $name, mixed $value = null, ?string $description = null) { $this->json->get('env-vars')->add($name, new Json([ 'value' => $value, 'description' => $description diff --git a/webfiori/framework/middleware/CacheMiddleware.php b/webfiori/framework/middleware/CacheMiddleware.php new file mode 100644 index 000000000..7d7767a18 --- /dev/null +++ b/webfiori/framework/middleware/CacheMiddleware.php @@ -0,0 +1,115 @@ +setPriority(50); + $this->addToGroups(['web']); + $this->fromCache = false; + } + /** + * Checks if the response is loaded from the cache or caching must be performed. + * + * @param Request $request An object that represents the request that + * will be received. + * + * @param Response $response An object that represents the response + * that will be sent back. + * + */ + public function after(Request $request, Response $response) { + + if (!$this->fromCache) { + $uriObj = Router::getRouteUri(); + + if ($uriObj !== null) { + $key = $this->getKey(); + $data = [ + 'headers' => $response->getHeaders(), + 'http-code' => $response->getCode(), + 'body' => $response->getBody() + ]; + Cache::set($key, $data, $uriObj->getCacheDuration()); + } + } + } + /** + * This method will do nothing. + * + * @param Request $request An object that represents the request that + * will be received. + * + * @param Response $response An object that represents the response + * that will be sent back. + */ + public function afterSend(Request $request, Response $response) { + + } + /** + * Attempt to load an item from the cache and send the response back if + * cached. + * + * @param Request $request An object that represents the request that + * will be received. + * + * @param Response $response An object that represents the response + * that will be sent back. + */ + public function before(Request $request, Response $response) { + $key = $this->getKey(); + $data = Cache::get($key); + + if ($data !== null) { + $this->fromCache = true; + $response->write($data['body']); + $response->setCode($data['http-code']); + foreach ($data['headers'] as $headerObj) { + $response->addHeader($headerObj->getName(), $headerObj->getValue()); + } + $response->send(); + } + } + /** + * Creates the key of cache item. + * + * This method will attempt to use 3 items to create a unique key. The items include: + * + * + * @return string + */ + public function getKey() : string { + $key = Request::getUri()->getUri(true, true); + + //Following steps are used to make cached response unique per user. + $session = SessionsManager::getActiveSession(); + if ($session !== null) { + $key .= $session->getId(); + } + $authHeader = Request::getAuthHeader(); + if ($authHeader !== null) { + $key .= $authHeader->getScheme().$authHeader->getCredentials(); + } + //End + return $key; + } +} diff --git a/webfiori/framework/middleware/MiddlewareManager.php b/webfiori/framework/middleware/MiddlewareManager.php index 493cc0a62..17e4e6313 100644 --- a/webfiori/framework/middleware/MiddlewareManager.php +++ b/webfiori/framework/middleware/MiddlewareManager.php @@ -10,44 +10,40 @@ */ namespace webfiori\framework\middleware; -use webfiori\collections\LinkedList; +use Exception; + /** * This class is used to manage the operations which are related to middleware. * * @author Ibrahim * - * @since 1.0 - * - * @since 2.0.0 */ class MiddlewareManager { private static $inst; /** * - * @var LinkedList + * @var array */ private $middlewareList; private function __construct() { - $this->middlewareList = new LinkedList(); + $this->middlewareList = []; } /** - * Returns a set of middlewares that belongs to a specific group. + * Returns a set of middleware that belongs to a specific group. * * @param string $groupName The name of the group. * - * @return LinkedList The method will return a linked list with all + * @return array The method will return a linked list with all * middleware in the group. If no group which has the given name exist, the * list will be empty. - * - * @since 1.0 */ - public static function getGroup(string $groupName) { - $list = new LinkedList(); + public static function getGroup(string $groupName) : array { + $list = []; $mdList = self::get()->middlewareList; foreach ($mdList as $mw) { if (in_array($groupName, $mw->getGroups())) { - $list->add($mw); + $list[] = $mw; } } @@ -61,8 +57,6 @@ public static function getGroup(string $groupName) { * @return AbstractMiddleware|null If a middleware with the given name is * found, the method will return it. Other than that, the method will return * null. - * - * @since 1.0 */ public static function getMiddleware(string $name) { $mdList = self::get()->middlewareList; @@ -76,27 +70,37 @@ public static function getMiddleware(string $name) { /** * Register a new middleware. * - * @param AbstractMiddleware $middleware The middleware that will be registered. - * - * @since 1.0 + * @param AbstractMiddleware|string $middleware The middleware that will be registered. */ - public static function register(AbstractMiddleware $middleware) { - self::get()->middlewareList->add($middleware); + public static function register($middleware) : bool { + if (gettype($middleware) == 'string') { + try { + $middleware = new $middleware(); + } catch (Exception $exc) { + return false; + } + } + if ($middleware instanceof AbstractMiddleware) { + self::get()->middlewareList[] = $middleware; + return true; + } + return false; } /** * Removes a middleware given its name. * * @param string $name The name of the middleware. - * - * @since 1.0 */ public static function remove(string $name) { $manager = self::get(); - $mw = $manager->getMiddleware($name); - - if ($mw instanceof AbstractMiddleware) { - $manager->middlewareList->remove($manager->middlewareList->indexOf($mw)); + $newList = []; + + foreach ($manager->middlewareList as $md) { + if ($md->getName() != $name) { + $newList[] = $md; + } } + $manager->middlewareList = $newList; } /** * diff --git a/webfiori/framework/middleware/StartSessionMiddleware.php b/webfiori/framework/middleware/StartSessionMiddleware.php new file mode 100644 index 000000000..93e298c0c --- /dev/null +++ b/webfiori/framework/middleware/StartSessionMiddleware.php @@ -0,0 +1,42 @@ +setPriority(PHP_INT_MAX); + $this->addToGroup('web'); + } + public function after(Request $request, Response $response) { + try { + $sessionsCookiesHeaders = SessionsManager::getCookiesHeaders(); + + foreach ($sessionsCookiesHeaders as $headerVal) { + Response::addHeader('set-cookie', $headerVal); + } + } catch (Error $exc) { + } + } + + public function afterSend(Request $request, Response $response) { + SessionsManager::validateStorage(); + } + + public function before(Request $request, Response $response) { + SessionsManager::start('wf-session'); + } +} diff --git a/webfiori/framework/router/RouteOption.php b/webfiori/framework/router/RouteOption.php index 0a3827d74..4bcbd3835 100644 --- a/webfiori/framework/router/RouteOption.php +++ b/webfiori/framework/router/RouteOption.php @@ -28,6 +28,10 @@ class RouteOption { * An option which is used to indicate if path is case sensitive or not. */ const CASE_SENSITIVE = 'case-sensitive'; + /** + * An option which is used to set the duration of route cache in seconds. + */ + const CACHE_DURATION = 'cache-ttl'; /** * An option which is used to set an array as closure parameters (applies to routes of type closure only) */ diff --git a/webfiori/framework/router/Router.php b/webfiori/framework/router/Router.php index b8f6a4350..fc1e514a9 100644 --- a/webfiori/framework/router/Router.php +++ b/webfiori/framework/router/Router.php @@ -507,14 +507,16 @@ public static function incSiteMapRoute() { Response::addHeader('content-type','text/xml'); }; self::closure([ - 'path' => '/sitemap.xml', - 'route-to' => $sitemapFunc, - 'in-sitemap' => true + RouteOption::PATH => '/sitemap.xml', + RouteOption::TO => $sitemapFunc, + RouteOption::SITEMAP => true, + RouteOption::CACHE_DURATION => 86400//1 day ]); self::closure([ - 'path' => '/sitemap', - 'route-to' => $sitemapFunc, - 'in-sitemap' => true + RouteOption::PATH => '/sitemap', + RouteOption::TO => $sitemapFunc, + RouteOption::SITEMAP => true, + RouteOption::CACHE_DURATION => 86400//1 day ]); } /** @@ -529,7 +531,8 @@ public static function notFound() { * Adds new route to a web page. * * Note that the route which created using this method will be added to - * 'global' and 'web' middleware groups. + * 'global' and 'web' middleware groups. Additionally, the routes will + * be cached for one hour. * * @param array $options An associative array that contains route * options. Available options are: @@ -755,13 +758,14 @@ private function addRouteHelper0($options): bool { $asApi = $options[RouteOption::API]; $closureParams = $options[RouteOption::CLOSURE_PARAMS] ; $path = $options[RouteOption::PATH]; + $cache = $options[RouteOption::CACHE_DURATION]; if ($routeType == self::CLOSURE_ROUTE && !is_callable($routeTo)) { return false; } $routeUri = new RouterUri($this->getBase().$path, $routeTo,$caseSensitive, $closureParams); $routeUri->setAction($options[RouteOption::ACTION]); - + $routeUri->setCacheDuration($cache); if (!$this->hasRouteHelper($routeUri)) { if ($asApi === true) { $routeUri->setType(self::API_ROUTE); @@ -928,6 +932,12 @@ private function checkOptionsArr(array $options): array { } else { $caseSensitive = true; } + + if (isset($options[RouteOption::CACHE_DURATION])) { + $cacheDuration = $options[RouteOption::CACHE_DURATION]; + } else { + $cacheDuration = 0; + } $routeType = $options[RouteOption::TYPE] ?? Router::CUSTOMIZED; @@ -978,7 +988,8 @@ private function checkOptionsArr(array $options): array { RouteOption::VALUES => $varValues, RouteOption::MIDDLEWARE => $mdArr, RouteOption::REQUEST_METHODS => $this->getRequestMethodsHelper($options), - RouteOption::ACTION => $action + RouteOption::ACTION => $action, + RouteOption::CACHE_DURATION => $cacheDuration ]; } private function copyOptionsToSub($options, &$subRoute) { @@ -1367,7 +1378,6 @@ private function resolveUrlHelper(string $uri, bool $loadResource = true) { private function routeFound(RouterUri $route, bool $loadResource) { if ($route->isRequestMethodAllowed()) { $this->uriObj = $route; - $route->getMiddleware()->insertionSort(false); foreach ($route->getMiddleware() as $mw) { $mw->before(Request::get(), Response::get()); @@ -1376,7 +1386,6 @@ private function routeFound(RouterUri $route, bool $loadResource) { if ($route->getType() == self::API_ROUTE && !defined('API_CALL')) { define('API_CALL', true); } - if (is_callable($route->getRouteTo())) { if ($loadResource === true) { call_user_func_array($route->getRouteTo(),$route->getClosureParams()); @@ -1453,6 +1462,7 @@ private function routeFound(RouterUri $route, bool $loadResource) { * @throws RoutingException */ private function searchRoute(RouterUri $routeUri, string $uri, bool $loadResource, bool $withVars = false): bool { + $pathArray = $routeUri->getPathArray(); $requestMethod = Request::getMethod(); $indexToSearch = 'static'; @@ -1600,7 +1610,10 @@ private static function view(array $options): bool { if (gettype($options) == 'array') { $options[RouteOption::TYPE] = Router::VIEW_ROUTE; self::addToMiddlewareGroup($options, 'web'); - + if (!isset($options[RouteOption::CACHE_DURATION])) { + //Cache pages for 1 hour by default + $options[RouteOption::CACHE_DURATION] = 3600; + } return Router::getInstance()->addRouteHelper1($options); } diff --git a/webfiori/framework/router/RouterUri.php b/webfiori/framework/router/RouterUri.php index d96e4a42b..01e6471bf 100644 --- a/webfiori/framework/router/RouterUri.php +++ b/webfiori/framework/router/RouterUri.php @@ -12,7 +12,6 @@ use Closure; use InvalidArgumentException; -use webfiori\collections\LinkedList; use webfiori\framework\middleware\MiddlewareManager; use webfiori\http\Uri; use webfiori\ui\HTMLNode; @@ -46,6 +45,8 @@ class RouterUri extends Uri { */ private $action; private $assignedMiddlewareList; + private $sortedMiddleeareList; + private $cacheDuration; /** * * @var array @@ -131,12 +132,34 @@ public function __construct(string $requestedUri, $routeTo, bool $caseSensitive $this->isCS = $caseSensitive; $this->setType(Router::CUSTOMIZED); $this->setRoute($routeTo); - $this->assignedMiddlewareList = new LinkedList(); + $this->assignedMiddlewareList = []; + $this->sortedMiddleeareList = []; $this->setClosureParams($closureParams); $this->incInSiteMap = false; $this->languages = []; $this->addMiddleware('global'); + $this->setCacheDuration(0); + } + /** + * Returns the duration of URI cache. + * + * @return int The duration of URI cache. Default value is zero which indicates + * that no caching will happen. + */ + public function getCacheDuration() : int { + return $this->cacheDuration; + } + /** + * Sets the duration of URI cache. + * + * @param int $val A positive value that represent cache duration in seconds. + * If 0 is given, it indicates that no caching will happen. + */ + public function setCacheDuration(int $val) { + if ($val >= 0) { + $this->cacheDuration = $val; + } } /** * Adds a language to the set of languages at which the resource that the URI @@ -168,12 +191,12 @@ public function addMiddleware(string $name) { $group = MiddlewareManager::getGroup($name); foreach ($group as $mw) { - $this->assignedMiddlewareList->add($mw); + $this->assignedMiddlewareList[] = $mw; } return; } - $this->assignedMiddlewareList->add($mw); + $this->assignedMiddlewareList[] = $mw; } /** * Returns the name of the action that will be called in the controller. @@ -238,12 +261,19 @@ public function getLanguages() : array { /** * Returns a list that holds objects for the middleware. * - * @return LinkedList + * @return array * - * @since 1.4.0 */ - public function getMiddleware() : LinkedList { - return $this->assignedMiddlewareList; + public function getMiddleware() : array { + if (count($this->assignedMiddlewareList) != count($this->sortedMiddleeareList)) { + $compareFunc = function ($a, $b) { + return $a->compare($b); + }; + $this->sortedMiddleeareList = $this->assignedMiddlewareList; + + usort($this->sortedMiddleeareList, $compareFunc); + } + return $this->sortedMiddleeareList; } /** diff --git a/webfiori/framework/scheduler/TasksManager.php b/webfiori/framework/scheduler/TasksManager.php index 181b0db5b..191d59abf 100644 --- a/webfiori/framework/scheduler/TasksManager.php +++ b/webfiori/framework/scheduler/TasksManager.php @@ -164,7 +164,7 @@ public static function activeTask() { * * @since 1.0 */ - public static function createTask(string $when = '*/5 * * * *', string $taskName = '', callable $function = null, array $funcParams = []) : bool { + public static function createTask(string $when = '*/5 * * * *', string $taskName = '', ?callable $function = null, array $funcParams = []) : bool { try { $task = new BaseTask($when); @@ -260,7 +260,7 @@ public static function dayOfWeek() : int { * * @since 1.0.1 */ - public static function execLog(bool $bool = null) : bool { + public static function execLog(?bool $bool = null) : bool { if ($bool !== null) { self::get()->setLogEnabledHelper($bool); } @@ -644,7 +644,7 @@ public static function reset() { * * @since 1.0.6 */ - public static function run(string $pass = '', string $taskName = null, bool $force = false, SchedulerCommand $command = null) { + public static function run(string $pass = '', ?string $taskName = null, bool $force = false, ?SchedulerCommand $command = null) { self::get()->command = $command; self::log('Running task(s) check...'); $activeSession = SessionsManager::getActiveSession(); @@ -929,7 +929,7 @@ private function logTaskExecution($task,$forced = false) { * @param bool $xForce * @param SchedulerCommand|null $command */ - private static function runTaskHelper(array &$retVal, AbstractTask $task, bool $xForce, SchedulerCommand $command = null) { + private static function runTaskHelper(array &$retVal, AbstractTask $task, bool $xForce, ?SchedulerCommand $command = null) { if ($task->isTime() || $xForce) { if ($command !== null) { $task->setCommand($command); @@ -965,7 +965,7 @@ private static function runTaskHelper(array &$retVal, AbstractTask $task, bool $ * @param AbstractTask|null $task * @since 1.0.4 */ - private function setActiveTaskHelper(AbstractTask $task = null) { + private function setActiveTaskHelper(?AbstractTask $task = null) { $this->activeTask = $task; if ($task !== null) { diff --git a/webfiori/framework/session/Session.php b/webfiori/framework/session/Session.php index b05112763..8833a16ed 100644 --- a/webfiori/framework/session/Session.php +++ b/webfiori/framework/session/Session.php @@ -276,7 +276,7 @@ public function deserialize(string $serialized): bool { * @return string A new random session ID. * */ - public static function generateSessionID(string $sessionName = null): string { + public static function generateSessionID(?string $sessionName = null): string { $date = date('Y-m-d\TH:i:sO'); $hash = hash('sha256', $date); $salt = time() + call_user_func(self::$randFunc, 0, 100); diff --git a/webfiori/framework/ui/StarterPage.php b/webfiori/framework/ui/StarterPage.php index 7081cd905..eec5aab1b 100644 --- a/webfiori/framework/ui/StarterPage.php +++ b/webfiori/framework/ui/StarterPage.php @@ -29,6 +29,10 @@ public function __construct() { $this->getDocument()->getDocumentRoot()->setStyle([ 'background-color' => '#e0f2b4' ]); + $this->getChildByID(self::MAIN_ELEMENTS[2])->setStyle([ + 'background' => 'rgb(213,238,153)', + 'background' => 'radial-gradient(circle, rgba(213,238,153,0.5550420851934523) 26%, rgba(4,101,37,0.45700286950717783) 68%)' + ]); $this->setTitle('Welcome to WebFiori'); $div = $this->insert('div'); $div->addChild('img', [ @@ -80,7 +84,10 @@ public function __construct() { private function createCard($link, $icon, $cardTitle, $paragraph, \webfiori\ui\HTMLNode $el) { $card = $el->addChild('v-card', [ 'hover', - 'height' => '220px' + 'height' => '220px', + 'style' => [ + 'background' => 'rgba(255,255,255,.6)' + ] ]); $card->addChild('v-card-title')->addChild('v-icon',[ 'style' => 'margin:10px' diff --git a/webfiori/framework/ui/WebPage.php b/webfiori/framework/ui/WebPage.php index c1eb7f334..bebd05585 100644 --- a/webfiori/framework/ui/WebPage.php +++ b/webfiori/framework/ui/WebPage.php @@ -685,7 +685,7 @@ public function include(string $path, array $args = []) : HTMLNode { * * @since 1.0 */ - public function includeI18nLables($bool = null) : bool { + public function includeI18nLables(?bool $bool = null) : bool { if ($bool !== null) { $this->includeLables = $bool === true; } @@ -1002,7 +1002,7 @@ public function setLang(string $lang = 'EN') { * * @see Theme::usingTheme() */ - public function setTheme($themeNameOrClass = null) { + public function setTheme(?string $themeNameOrClass = null) { if ($themeNameOrClass !== null && strlen(trim($themeNameOrClass)) == 0) { return; } @@ -1188,7 +1188,7 @@ private function checkLang() { } $this->setLang($langCodeFromSession); } - private function getConfigVar(string $meth, string $default = null, array $params = []) { + private function getConfigVar(string $meth, ?string $default = null, array $params = []) { try { return call_user_func_array([App::getConfig(), $meth], $params); } catch (InitializationException $ex) { diff --git a/webfiori/framework/ui/ui-functions.php b/webfiori/framework/ui/ui-functions.php index d6404330c..ef20cb3fb 100644 --- a/webfiori/framework/ui/ui-functions.php +++ b/webfiori/framework/ui/ui-functions.php @@ -40,7 +40,7 @@ * * @throws MissingLangException */ -function label(string $path, string $langCode = null) { +function label(string $path, ?string $langCode = null) { return Lang::getLabel($path, $langCode); } /** diff --git a/webfiori/framework/writers/APITestCaseWriter.php b/webfiori/framework/writers/APITestCaseWriter.php index c319d4a33..f440f1450 100644 --- a/webfiori/framework/writers/APITestCaseWriter.php +++ b/webfiori/framework/writers/APITestCaseWriter.php @@ -27,11 +27,11 @@ class APITestCaseWriter extends ClassWriter { * * @param WebServicesManager|null $manager Web services manager instance. * - * @param AbstractWebService $service The web service at which the test case + * @param AbstractWebService|string|null $service The web service at which the test case * will be based on. */ - public function __construct(WebServicesManager $manager = null, $service = null) { - parent::__construct('WebService', ROOT_PATH.'\\tests\\apis', 'tests\\apis'); + public function __construct(?WebServicesManager $manager = null, null|string|AbstractWebService $service = null) { + parent::__construct('WebService', ROOT_PATH.DS.'tests'.DS.'apis', 'tests\\apis'); $this->setSuffix('Test'); if ($manager !== null) { diff --git a/webfiori/framework/writers/ClassWriter.php b/webfiori/framework/writers/ClassWriter.php index bb636e0e3..6b4ae9571 100644 --- a/webfiori/framework/writers/ClassWriter.php +++ b/webfiori/framework/writers/ClassWriter.php @@ -131,12 +131,13 @@ public function append($strOrArr, $tabsCount = 0) { * indices of the array are parameters names and values are types of * parameters. * - * @param string|null $returns An optional name of return type. + * @param string|null $returns An optional name of return type. This can be + * a string such as 'int|null|Object'. * * @return string The method will create method definition string and return * it. */ - public function f($funcName, $argsArr = [], $returns = null) { + public function f($funcName, $argsArr = [], ?string $returns = null) { $argsPart = '('; foreach ($argsArr as $argName => $argType) { @@ -370,7 +371,7 @@ public function setPath(string $path) : bool { if (strlen($trimmed) == 0) { return false; } - $this->path = $path; + $this->path = str_replace('\\', DS, str_replace('/', DS, $trimmed)); return true; } diff --git a/webfiori/framework/writers/DBClassWriter.php b/webfiori/framework/writers/DBClassWriter.php index 22f5c975a..378125c05 100644 --- a/webfiori/framework/writers/DBClassWriter.php +++ b/webfiori/framework/writers/DBClassWriter.php @@ -42,8 +42,8 @@ class DBClassWriter extends ClassWriter { * @param Table $table The table instance at which the class will build * database operations based on. */ - public function __construct($className = 'NewDBOperationsClass', $ns = '\\', Table $table = null) { - parent::__construct($className, $ns, $ns); + public function __construct(?string $className = 'NewDBOperationsClass', string $ns = '\\', ?Table $table = null) { + parent::__construct($className, ROOT_PATH.DS.$ns, $ns); if ($table !== null) { $this->setTable($table); @@ -238,7 +238,7 @@ public function writeClassBody() { ], 2); } $this->append([ - "\$this->register('".str_replace("\\", "\\\\", $this->getPath())."');", + "\$this->register('".str_replace("\\", "\\\\", $this->getNamespace())."');", ], 2); $this->append('}', 1); @@ -353,7 +353,7 @@ private function writeColUpdate(Column $colObj, $key) { $this->append($paramsComment, 1); if (strpos($phpType, '|null') !== false) { - $phpType = substr($phpType,0, strlen($phpType) - strlen('|null')); + $phpType = '?'.substr($phpType,0, strlen($phpType) - strlen('|null')); } $this->append([ " */", diff --git a/webfiori/framework/writers/DatabaseMigrationWriter.php b/webfiori/framework/writers/DatabaseMigrationWriter.php new file mode 100644 index 000000000..078e7c3ee --- /dev/null +++ b/webfiori/framework/writers/DatabaseMigrationWriter.php @@ -0,0 +1,106 @@ +getMigrations()); + $this->setMigrationOrder($count); + if ($count < 10) { + $name = 'Migration00'.$count; + } else if ($count < 100) { + $name = 'Migration0'.$count; + } else { + $name = 'Migration'.$count; + } + } + + $this->setMigrationName($name); + + parent::__construct($name, APP_PATH.'database'.DS.'migrations', APP_DIR.'\\database\\migrations'); + $this->addUseStatement([ + Database::class, + AbstractMigration::class, + ]); + + } + public function getMigrationName() : string { + return $this->name; + } + public function getMigrationOrder() : int { + return $this->order; + } + public function setMigrationName(string $name) { + $this->name = $name; + } + public function setMigrationOrder(int $order) { + $this->order = $order; + } + + public function writeClassBody() { + $this->append([ + '/**', + ' * Creates new instance of the class.', + ' */', + $this->f('__construct'), + + ], 1); + $this->append("parent::__construct('".$this->getMigrationName()."', ".$this->getMigrationOrder().");", 2); + $this->append('}', 1); + $this->append('/**', 1); + $this->append(' * Performs the action that will apply the migration.', 1); + $this->append(' * ', 1); + $this->append(' * @param Database $schema The database at which the migration will be applied to.', 1); + $this->append(' */', 1); + $this->append($this->f('up', ['schema' => 'Database']), 1); + $this->append('//TODO: Implement the action which will apply the migration to database.', 2); + $this->append('}', 1); + $this->append('/**', 1); + $this->append(' * Performs the action that will revert back the migration.', 1); + $this->append(' * ', 1); + $this->append(' * @param Database $schema The database at which the migration will be applied to.', 1); + $this->append(' */', 1); + $this->append($this->f('down', ['schema' => 'Database']), 1); + $this->append('//TODO: Implement the action which will revert back the migration.', 2); + $this->append('}', 1); + $this->append('}'); + } + public function writeClassComment() { + $classTop = [ + '/**', + ' * A database migration class.', + ' */' + ]; + $this->append($classTop); + } + + public function writeClassDeclaration() { + $this->append('class '.$this->getName().' extends AbstractMigration {'); + } +} diff --git a/webfiori/framework/writers/TableClassWriter.php b/webfiori/framework/writers/TableClassWriter.php index 9f40c52ac..19b6bba9e 100644 --- a/webfiori/framework/writers/TableClassWriter.php +++ b/webfiori/framework/writers/TableClassWriter.php @@ -75,7 +75,7 @@ class TableClassWriter extends ClassWriter { * * @since 1.0 */ - public function __construct($tableObj = null) { + public function __construct(?Table $tableObj = null) { parent::__construct('NewTable', APP_PATH.'database', APP_DIR.'\\database'); $this->setSuffix('Table'); diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 5537d2177..abfb26960 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -46,7 +46,7 @@ class WebServiceWriter extends ClassWriter { * provided, the constant ROOT_PATH is used. * */ - public function __construct($webServicesObj = null) { + public function __construct(?AbstractWebService $webServicesObj = null) { parent::__construct('NewWebService', APP_PATH.'apis', APP_DIR.'\\apis'); $this->setSuffix('Service');