Full Code of aplus-framework/app for AI

master 962822556c48 cached
74 files
75.3 KB
20.6k tokens
75 symbols
2 requests
Download .txt
Repository: aplus-framework/app
Branch: master
Commit: 962822556c48
Files: 74
Total size: 75.3 KB

Directory structure:
gitextract_403vn0ul/

├── .editorconfig
├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .gitlab-ci.yml
├── .htaccess
├── .php-cs-fixer.dist.php
├── App.php
├── README.md
├── SECURITY.md
├── app/
│   ├── Commands/
│   │   └── Index.php
│   ├── Controllers/
│   │   └── Home.php
│   ├── Languages/
│   │   ├── en/
│   │   │   └── home.php
│   │   ├── es/
│   │   │   └── home.php
│   │   └── pt-br/
│   │       └── home.php
│   ├── Models/
│   │   └── .gitkeep
│   └── Views/
│       ├── _layouts/
│       │   └── default.php
│       ├── errors/
│       │   └── 404.php
│       └── home/
│           └── index.php
├── bin/
│   └── console
├── boot/
│   ├── app.php
│   ├── constants.php
│   ├── helpers.php
│   ├── init.php
│   └── routes.php
├── composer.json
├── config/
│   ├── antiCsrf.php
│   ├── autoloader.php
│   ├── cache.php
│   ├── console.php
│   ├── database.php
│   ├── debugger.php
│   ├── exceptionHandler.php
│   ├── language.php
│   ├── locator.php
│   ├── logger.php
│   ├── mailer.php
│   ├── migrator.php
│   ├── request.php
│   ├── response.php
│   ├── router.php
│   ├── session.php
│   ├── validation.php
│   └── view.php
├── docker-compose.yml
├── guide/
│   └── index.rst
├── php-server.ini
├── phpdoc.dist.xml
├── phpmd.xml
├── phpstan.neon.dist
├── phpunit.xml.dist
├── preload.php
├── public/
│   ├── .htaccess
│   ├── index.php
│   └── robots.txt
├── storage/
│   ├── cache/
│   │   └── .gitignore
│   ├── logs/
│   │   └── .gitignore
│   ├── sessions/
│   │   └── .gitignore
│   └── uploads/
│       └── .gitignore
└── tests/
    ├── AppTest.php
    ├── TestCase.php
    ├── app/
    │   ├── Commands/
    │   │   └── IndexTest.php
    │   ├── Controllers/
    │   │   └── HomeTest.php
    │   └── Languages/
    │       └── LanguagesTest.php
    ├── bin/
    │   └── ConsoleTest.php
    ├── boot/
    │   ├── AppTest.php
    │   ├── ConstantsTest.php
    │   ├── HelpersTest.php
    │   ├── InitTest.php
    │   └── RoutesTest.php
    ├── config/
    │   └── ConfigsTest.php
    ├── public/
    │   └── IndexTest.php
    └── support/
        ├── Helpers/
        │   └── tests.php
        ├── Models/
        │   └── UsersModel.php
        └── routes.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.{md,rst}]
trim_trailing_whitespace = false

[*.yml]
indent_size = 2


================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests

on:
  push:
  pull_request:
  schedule:
    - cron: '0 4 * * *'

jobs:
  tests:
    runs-on: ubuntu-24.04
    timeout-minutes: 30

    name: PHP 8.3

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3
          tools: composer
          coverage: xdebug

      - name: Install dependencies
        run:
          composer update

      - name: Composer normalize
        run:
          composer normalize --dry-run --indent-size=4 --indent-style=space

      - name: Coding Standard
        run:
          vendor/bin/php-cs-fixer fix --diff --dry-run --verbose

      - name: PHPMD
        run:
          vendor/bin/phpmd app xml phpmd.xml

      - name: PHPStan
        run:
          vendor/bin/phpstan analyse -vvv

      - name: PHPUnit
        run: vendor/bin/phpunit

      - name: Upload coverage results to Coveralls
        env:
          COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mkdir -p build/logs/
          cp build/coverage/clover.xml build/logs/clover.xml
          composer global require php-coveralls/php-coveralls
          php-coveralls --coverage_clover=build/logs/clover.xml -v

  tests-latest:
    runs-on: ubuntu-24.04
    timeout-minutes: 30

    name: PHP Latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: latest
          tools: composer
          coverage: xdebug

      - name: Install dependencies
        run:
          composer update

      - name: PHPUnit
        run: vendor/bin/phpunit


================================================
FILE: .gitignore
================================================
.config.php
.env
.env.php
.idea
.php-cs-fixer.cache
.phpunit.cache
.phpunit.result.cache
.vscode
build
composer.lock
composer.phar
phpstan.neon
phpunit.xml
raw-tests
vendor


================================================
FILE: .gitlab-ci.yml
================================================
---
image: registry.gitlab.com/aplus-framework/images/lempa:4

include:
  - template: Security/SAST.gitlab-ci.yml

variables:
  SAST_EXCLUDED_PATHS: guide, tests, vendor

test:php:
  stage: test
  timeout: 30 minutes
  cache:
    paths:
      - vendor/
  before_script:
    - php -v
    - composer update
  script:
    - composer normalize --dry-run --indent-size=4 --indent-style=space
    - vendor/bin/php-cs-fixer fix --diff --dry-run --verbose
    - vendor/bin/phpmd app xml phpmd.xml
    - vendor/bin/phpstan analyse -vvv
    - vendor/bin/phpunit --colors=never
    - phpdoc
  artifacts:
    paths:
      - build/coverage/
      - build/docs/
  coverage: '/^\s*Lines:\s*\d+.\d+\%/'

test:php-latest:
  image: registry.gitlab.com/aplus-framework/images/lempa:latest
  stage: test
  timeout: 30 minutes
  cache:
    paths:
      - vendor/
  before_script:
    - php -v
    - composer update
  script:
    - vendor/bin/phpunit --colors=never
  coverage: '/^\s*Lines:\s*\d+.\d+\%/'

pages:
  stage: deploy
  timeout: 10 minutes
  dependencies:
    - test:php
  environment:
    name: production
    url: https://aplus-framework.gitlab.io
  script:
    - mv public/ app-public/
    - mkdir public/
    - mv build/coverage/ public/
    - mv build/docs/ public/
  artifacts:
    paths:
      - public/
  only:
    - master


================================================
FILE: .htaccess
================================================
Options All -Indexes

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Redirect to the public directory
    RewriteRule ^$ public/ [L]
    RewriteRule (.*) public/$1 [L]
</IfModule>


================================================
FILE: .php-cs-fixer.dist.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
use Framework\CodingStandard\Config;
use Framework\CodingStandard\Finder;

return (new Config())->setDefaultHeaderComment(
    'App Project',
    '' // [fullname] [<email>]
)->setFinder(
    Finder::create()->in(__DIR__)->exclude('storage')
);


================================================
FILE: App.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Class App.
 *
 * @package app
 *
 * @see https://docs.aplus-framework.com/guides/projects/app/#the-global-class-app
 */
class App extends Framework\MVC\App
{
}


================================================
FILE: README.md
================================================
<a href="https://github.com/aplus-framework/app"><img src="https://raw.githubusercontent.com/aplus-framework/app/master/guide/image.png" alt="Aplus Framework App Project" align="right" width="100"></a>

# Aplus Framework App Project

- [Home](https://aplus-framework.com/packages/app)
- [User Guide](https://docs.aplus-framework.com/guides/projects/app/index.html)
- [API Documentation](https://docs.aplus-framework.com/packages/app.html)
- [Online Demo](https://demo.aplus-framework.com)

[![tests](https://github.com/aplus-framework/app/actions/workflows/tests.yml/badge.svg)](https://github.com/aplus-framework/app/actions/workflows/tests.yml)
[![coverage](https://coveralls.io/repos/github/aplus-framework/app/badge.svg?branch=master)](https://coveralls.io/github/aplus-framework/app?branch=master)
[![packagist](https://img.shields.io/packagist/v/aplus/app)](https://packagist.org/packages/aplus/app)
[![open-source](https://img.shields.io/badge/open--source-sponsor-magenta)](https://aplus-framework.com/sponsor)

## Getting Started

Make sure you have [Composer](https://getcomposer.org/doc/00-intro.md) installed.

Follow the installation instructions in the [User Guide](https://docs.aplus-framework.com/guides/projects/app/index.html).

To install the latest version:

```
composer create-project aplus/app
```

Or, to install the latest [LTS](https://aplus-framework.com/lts) version:

```
composer create-project aplus/app:^24
```

Enter the project directory.

---

Optionally, you can start a new project on GitHub from [this template](https://github.com/new?template_name=app&template_owner=aplus-framework).

## Licensing

Add a `LICENSE` file.

If you think about open-source your project,
[choose a license](https://choosealicense.com/licenses/).

If your project is proprietary, you can add your custom license or
[not](https://choosealicense.com/no-permission/).

Edit the `.php-cs-fixer.dist.php` file.
Set the project name and copyright information.

To update the comment header in all PHP files, run:

```
vendor/bin/php-cs-fixer fix -vvv
```

## Code Quality

Aplus Framework uses Code Quality Tools in all its projects.

By default, App Project also uses the following tools as dev-dependencies:

- [PHP-CS-Fixer](https://cs.symfony.com)
- [phpDocumentor](https://phpdoc.org)
- [PHPMD](https://phpmd.org)
- [PHPStan](https://phpstan.org)
- [PHPUnit](https://phpunit.de)

### Static Analysis

You can find bugs in your code without writing tests by running:

```
vendor/bin/phpstan analyse
```

See the `phpstan.neon.dist` file for more details.

### Mess Detector

You can look for several potential problems in the source code by running:

```
vendor/bin/phpmd app xml phpmd.xml
```

Customize your rules in the `phpmd.xml` file.

### Coding Standard

We extend PHP-CS-Fixer to create the
[Coding Standard Library](https://github.com/aplus-framework/coding-standard).

It is [PSR-12](https://www.php-fig.org/psr/psr-12/) compatible.

You can see what to fix in the source code by running:

```
vendor/bin/php-cs-fixer fix --diff --dry-run --verbose
```

### Testing

We extend PHPUnit to create the
[Testing Library](https://github.com/aplus-framework/testing).

You can unit test your code by running:

```
vendor/bin/phpunit
```

See the `phpunit.xml.dist` file for more details.

### Documenting

Good software usually has good documentation.

You can build beautiful HTML pages about your project's documentation.

You must have phpDocumentor installed on your computer or run `phpdoc`
[inside a container](#containers).

## Development Environment

The App Project is delivered with a dev-dependency to easily configure the
built-in PHP development server.

Just run

```
vendor/bin/php-server
```

and your project will be available at http://localhost:8080.

See the `php-server.ini` file for more details.

### Containers

Aplus has Docker [images](https://gitlab.com/aplus-framework/images) for testing
and building software.

You can run it in CI or local environments.

With [Docker](https://www.docker.com/get-started) installed on your computer,
you can run:

```
docker-compose run --service-ports lempa
```

This will log you as the **developer** user into a Docker container where you can
run all your tests.

By default, the web app will be available at http://localhost, on ports 80 and 443.

See the `docker-compose.yml` file for more details.

## Continuous Integration

App Project is cross-platform and can be used in public and private projects.

You can use it on [GitLab](https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/),
on [GitHub](https://docs.github.com/en/actions/automating-builds-and-tests/about-continuous-integration),
on your computer, anywhere you want.

The App Project is already pre-configured to run in a GitLab CI environment.

See the `.gitlab-ci.yml` file for more details.

Just upload your project to GitLab and it will run
[pipelines](https://docs.gitlab.com/ee/ci/pipelines/#view-pipelines).

On GitHub, it will run [workflows](https://docs.github.com/en/actions) to test
your code every Push or Pull Request.

Check the `.github` folder to see more.

## And now?

Go build an API or a website, an awesome app! ⚡

See you.

---

If you have a little time...

Visit the Aplus Framework website: [aplus-framework.com](https://aplus-framework.com)

Follow Aplus on:

- [GitHub](https://github.com/aplus-framework)
- [X](https://x.com/AplusFramework)
- [Facebook](https://www.facebook.com/AplusFramework)
- [YouTube](https://www.youtube.com/@AplusFramework)

Stay tuned for our updates.

Share your experiences about meet us!

**Remember**:

> Coding is Art.
>
> Coding is Engineering.
>
> Good developer loves to code.
>
> **Code with Love!**

---

The Aplus Framework Team


================================================
FILE: SECURITY.md
================================================
# Security Policy

The **latest** and **LTS** versions of this library receive security fixes.

If you find any vulnerability send an email to `aplusframework@gmail.com`.


================================================
FILE: app/Commands/Index.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace App\Commands;

use Framework\CLI\CLI;

/**
 * Class Index.
 *
 * @package app
 */
class Index extends \Framework\CLI\Commands\Index
{
    protected function showHeader() : void
    {
        $banner = <<<'EOL'
                _          _                _
               / \   _ __ | |_   _ ___     / \   _ __  _ __
              / _ \ | '_ \| | | | / __|   / _ \ | '_ \| '_ \
             / ___ \| |_) | | |_| \__ \  / ___ \| |_) | |_) |
            /_/   \_\ .__/|_|\__,_|___/ /_/   \_\ .__/| .__/
                    |_|                         |_|   |_|

            EOL;
        CLI::write($banner, 'green');
    }
}


================================================
FILE: app/Controllers/Home.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace App\Controllers;

use Framework\MVC\Controller;
use Framework\Routing\Attributes\Route;

/**
 * Class Home.
 *
 * @package app
 */
final class Home extends Controller
{
    /**
     * Renders the application homepage.
     */
    #[Route('GET', '/', name: 'home.index')]
    public function index() : string
    {
        return view('home/index');
    }
}


================================================
FILE: app/Languages/en/home.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
return [
    'description' => 'The app is running! Yep!',
    'title' => 'Aplus Framework',
];


================================================
FILE: app/Languages/es/home.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
return [
    'description' => '¡La app se está ejecutando! ¡Sí!',
    'title' => 'Aplus Framework',
];


================================================
FILE: app/Languages/pt-br/home.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
return [
    'description' => 'O app está rodando! Sim!',
    'title' => 'Aplus Framework',
];


================================================
FILE: app/Models/.gitkeep
================================================


================================================
FILE: app/Views/_layouts/default.php
================================================
<?php
/**
 * @var string|null $description
 * @var string|null $title
 * @var Framework\MVC\View $view
 */
?>
<!doctype html>
<html lang="<?= App::language()->getCurrentLocale() ?>" dir="<?= App::language()
    ->getCurrentLocaleDirection() ?>">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="<?= isset($description)
        ? esc($description) : 'Website built with Aplus Framework' ?>">
    <meta name="theme-color" content="#000">
    <title><?= isset($title) ? esc($title) : 'Aplus Framework' ?></title>
    <link rel="shortcut icon" href="/favicon.ico">
    <style>
        body {
            background: #fff;
            color: #000;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 1rem;
            line-height: 1.5rem;
            margin: 1rem;
        }

        a {
            color: #f0f;
            text-decoration: none;
        }

        a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
<?= $view->renderBlock('contents') ?>
</body>
</html>


================================================
FILE: app/Views/errors/404.php
================================================
<?php
/**
 * @var string $message
 * @var string $title
 * @var Framework\MVC\View $view
 */
$view->extends('default');
$view->block('contents');
?>
    <h1><?= $title ?></h1>
    <p><?= $message ?></p>
    <p>
        <a href="<?= route_url('home.index') ?>">Go to homepage</a>
    </p>
<?php
$view->endBlock();


================================================
FILE: app/Views/home/index.php
================================================
<?php
/**
 * @var Framework\MVC\View $view
 */
$view->extends('default', 'contents');
?>
<h1><?= lang('home.title') ?></h1>
<p><?= lang('home.description') ?></p>


================================================
FILE: bin/console
================================================
#!/usr/bin/env php
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
(require __DIR__ . '/../boot/app.php')->runCli();


================================================
FILE: boot/app.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
} else {
    require_once __DIR__ . '/init.php';
    require_once __DIR__ . '/constants.php';
    require_once BOOT_DIR . 'helpers.php';
    require_once ROOT_DIR . 'App.php';
}

return new App(CONFIG_DIR, IS_DEV);


================================================
FILE: boot/constants.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * @package app
 */
/**
 * The current environment name.
 */
define('ENVIRONMENT', $_SERVER['ENVIRONMENT'] ?? 'production');
/**
 * True if it is in development environment, otherwise false.
 */
define('IS_DEV', ENVIRONMENT === 'development');
/**
 * Path to the root directory.
 */
define('ROOT_DIR', dirname(__DIR__) . \DIRECTORY_SEPARATOR);
/**
 * Path to the app directory.
 */
define('APP_DIR', ROOT_DIR . 'app' . \DIRECTORY_SEPARATOR);
/**
 * Path to the bin directory.
 */
define('BIN_DIR', ROOT_DIR . 'bin' . \DIRECTORY_SEPARATOR);
/**
 * Path to the boot directory.
 */
define('BOOT_DIR', ROOT_DIR . 'boot' . \DIRECTORY_SEPARATOR);
/**
 * Path to the config directory.
 */
define('CONFIG_DIR', ROOT_DIR . 'config' . \DIRECTORY_SEPARATOR);
/**
 * Path to the public directory.
 */
define('PUBLIC_DIR', ROOT_DIR . 'public' . \DIRECTORY_SEPARATOR);
/**
 * Path to the storage directory.
 */
define('STORAGE_DIR', ROOT_DIR . 'storage' . \DIRECTORY_SEPARATOR);
/**
 * Path to the vendor directory.
 */
define('VENDOR_DIR', ROOT_DIR . 'vendor' . \DIRECTORY_SEPARATOR);
/**
 * Path to the aplus directory.
 */
define('APLUS_DIR', VENDOR_DIR . 'aplus' . \DIRECTORY_SEPARATOR);


================================================
FILE: boot/helpers.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * @package app
 */

use Framework\Helpers\ArraySimple;
use Framework\HTTP\Response;
use Framework\MVC\Model;
use Framework\Routing\Route;
use Framework\Session\Session;
use JetBrains\PhpStorm\Pure;

/**
 * Load helper files.
 *
 * @param array<int,string>|string $helper A list of helper names as array
 * or a helper name as string
 *
 * @return array<int,string> A list of all loaded files
 */
function helpers(array | string $helper) : array
{
    if (is_array($helper)) {
        $files = [];
        foreach ($helper as $item) {
            $files[] = helpers($item);
        }
        return array_merge(...$files);
    }
    $files = App::locator()->findFiles('Helpers/' . $helper);
    foreach ($files as $file) {
        require_once $file;
    }
    return $files;
}

/**
 * Escape special characters to HTML entities.
 *
 * @param string|null $text The text to be escaped
 * @param string $encoding The escaped text encoding
 *
 * @return string The escaped text
 */
#[Pure]
function esc(?string $text, string $encoding = 'UTF-8') : string
{
    $text = (string) $text;
    return empty($text)
        ? $text
        : htmlspecialchars($text, \ENT_QUOTES | \ENT_HTML5, $encoding);
}

/**
 * Renders a view.
 *
 * @param string $path View path
 * @param array<string,mixed> $variables Variables passed to the view
 * @param string $instance The View instance name
 *
 * @return string The rendered view contents
 */
function view(string $path, array $variables = [], string $instance = 'default') : string
{
    return App::view($instance)->render($path, $variables);
}

/**
 * Get the current URL.
 *
 * @return string
 */
function current_url() : string
{
    return App::request()->getUrl()->toString();
}

/**
 * Get the current Route.
 *
 * @return Route
 */
function current_route() : Route
{
    return App::router()->getMatchedRoute();
}

/**
 * Get a URL based in a Route name.
 *
 * @param string $name Route name
 * @param array<mixed> $pathArgs Route path arguments
 * @param array<mixed> $originArgs Route origin arguments
 *
 * @return string The Route URL
 */
function route_url(string $name, array $pathArgs = [], array $originArgs = []) : string
{
    $route = App::router()->getNamedRoute($name);
    $matched = App::router()->getMatchedRoute();
    if (empty($originArgs)
        && $matched
        && $route->getOrigin() === $matched->getOrigin()
    ) {
        $originArgs = App::router()->getMatchedOriginArguments();
    }
    return $route->getUrl($originArgs, $pathArgs);
}

/**
 * Renders a language file line with dot notation format.
 *
 * e.g. home.hello matches 'home' for file and 'hello' for line.
 *
 * @param string $line The dot notation file line
 * @param array<int|string,string> $args The arguments to be used in the
 * formatted text
 * @param string|null $locale A custom locale or null to use the current
 *
 * @return string|null The rendered text or null if not found
 */
function lang(string $line, array $args = [], ?string $locale = null) : ?string
{
    return App::language()->lang($line, $args, $locale);
}

/**
 * Get the Session instance.
 *
 * @return Session
 */
function session() : Session
{
    return App::session();
}

/**
 * Get data from old redirect.
 *
 * @param string|null $key Set null to return all data
 * @param bool $escape
 *
 * @see Framework\HTTP\Request::getRedirectData()
 * @see Framework\HTTP\Response::redirect()
 * @see redirect()
 *
 * @return mixed The old value. If $escape is true and the value is not
 * stringable, an empty string will return
 */
function old(?string $key, bool $escape = true) : mixed
{
    App::session()->activate();
    $data = App::request()->getRedirectData($key);
    if ($data !== null && $escape) {
        $data = is_scalar($data) || (is_object($data) && method_exists($data, '__toString'))
            ? esc((string) $data)
            : '';
    }
    return $data;
}

/**
 * Tells if session has old data.
 *
 * @param string|null $key null to check all data or a specific key in the
 * array simple format
 *
 * @see old()
 *
 * @return bool
 */
function has_old(?string $key = null) : bool
{
    App::session()->activate();
    return App::request()->getRedirectData($key) !== null;
}

/**
 * Renders the AntiCSRF input.
 *
 * @param string $instance The antiCsrf service instance name
 *
 * @return string An HTML hidden input if antiCsrf service is enabled or an
 * empty string if it is disabled
 */
function csrf_input(string $instance = 'default') : string
{
    return App::antiCsrf($instance)->input();
}

/**
 * Set Response status as "404 Not Found" and auto set body as
 * JSON or HTML page based on Request Content-Type header.
 *
 * @param array<string,mixed> $variables
 *
 * @return Response
 */
function respond_not_found(array $variables = []) : Response
{
    $request = App::request();
    $response = App::response();
    $response->setStatus(404);
    if ($request->isJson() || $request->negotiateAccept([
            'text/html',
            'application/json',
        ]) === 'application/json') {
        return $response->setJson([
            'error' => [
                'code' => 404,
                'reason' => 'Not Found',
            ],
        ]);
    }
    $variables['title'] ??= lang('routing.error404');
    $variables['message'] ??= lang('routing.pageNotFound');
    return $response->setBody(
        view('errors/404', $variables)
    );
}

/**
 * Sets the HTTP Redirect Response with data accessible in the next HTTP
 * Request.
 *
 * @param string $location Location Header value
 * @param array<int|string,mixed> $data Session data available on next
 * Request
 * @param int|null $code HTTP Redirect status code. Leave null to determine
 * based on the current HTTP method.
 *
 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
 * @see Framework\HTTP\Request::getRedirectData()
 * @see old()
 *
 * @throws InvalidArgumentException for invalid Redirection code
 *
 * @return Response
 */
function redirect(string $location, array $data = [], ?int $code = null) : Response
{
    if ($data) {
        App::session()->activate();
    }
    return App::response()->redirect($location, $data, $code);
}

/**
 * Redirect to a named route.
 *
 * @param array<mixed>|string $route route name as string or an array with the
 * route name, an array with path args and other array with origin args
 * @param array<mixed> $data Session data available on next
 * Request
 * @param int|null $code HTTP Redirect status code. Leave null to determine
 * based on the current HTTP method.
 *
 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
 * @see Framework\HTTP\Request::getRedirectData()
 * @see old()
 * @see redirect()
 *
 * @throws InvalidArgumentException for invalid Redirection code
 *
 * @return Response
 */
function redirect_to(
    array | string $route,
    array $data = [],
    ?int $code = null
) : Response {
    $route = (array) $route;
    $route = route_url(...$route);
    return redirect($route, $data, $code);
}

/**
 * Get configs from a service.
 *
 * @param string $name The service name
 * @param string $key The instance name and, optionally, with keys in the
 * ArraySimple keys format
 *
 * @return mixed The key value
 */
function config(string $name, string $key = 'default') : mixed
{
    [$instance, $keys] = array_pad(explode('[', $key, 2), 2, null);
    $config = App::config()->get($name, $instance);
    if ($keys === null) {
        return $config;
    }
    $pos = strpos($keys, ']');
    if ($pos === false) {
        $pos = strlen($key);
    }
    $parent = substr($keys, 0, $pos);
    $keys = substr($keys, $pos + 1);
    $key = $parent . $keys;
    return ArraySimple::value($key, $config);
}

/**
 * Get same Model instance.
 *
 * @template T of Model
 *
 * @param class-string<T> $class
 *
 * @return T
 */
function model(string $class) : Model
{
    return Model::get($class);
}

/**
 * Get an environment variable.
 *
 * @param string $key
 * @param mixed $default
 *
 * @return mixed
 */
function env(string $key, mixed $default = null) : mixed
{
    return $_ENV[$key] ?? $default;
}


================================================
FILE: boot/init.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
if (is_file(__DIR__ . '/../.env.php')) {
    require __DIR__ . '/../.env.php';
    if (isset($_ENV['ENVIRONMENT'])) {
        $_SERVER['ENVIRONMENT'] = $_ENV['ENVIRONMENT'];
    }
}

if (isset($_SERVER['ENVIRONMENT']) && $_SERVER['ENVIRONMENT'] === 'development') {
    error_reporting(-1);
    ini_set('display_errors', 'On');
} else {
    error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_NOTICE & ~\E_USER_DEPRECATED & ~\E_USER_NOTICE);
    ini_set('display_errors', 'Off');
}

date_default_timezone_set('UTC');
//set_time_limit(30);


================================================
FILE: boot/routes.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
use Framework\Routing\RouteCollection;

App::router()->serve(
    env('app.default.origin', 'http://localhost:8080'),
    static function (RouteCollection $routes) : void {
        $routes->namespace('App\Controllers', [
            $routes->get('/', 'Home::index', 'home.index'),
        ]);
        $routes->notFound(static fn () => respond_not_found());
    }
);


================================================
FILE: composer.json
================================================
{
    "name": "aplus/app",
    "description": "Aplus Framework App Project",
    "license": "MIT",
    "type": "project",
    "keywords": [
        "app",
        "application",
        "libraries",
        "mvc",
        "database",
        "routing",
        "http",
        "config",
        "session",
        "skeleton",
        "project"
    ],
    "homepage": "https://aplus-framework.com/packages/app",
    "support": {
        "email": "support@aplus-framework.com",
        "issues": "https://github.com/aplus-framework/app/issues",
        "forum": "https://aplus-framework.com/forum",
        "source": "https://github.com/aplus-framework/app",
        "docs": "https://docs.aplus-framework.com/guides/projects/app/"
    },
    "funding": [
        {
            "type": "Aplus Sponsor",
            "url": "https://aplus-framework.com/sponsor"
        }
    ],
    "require": {
        "php": ">=8.3",
        "aplus/framework": "^25.1"
    },
    "require-dev": {
        "ext-xdebug": "*",
        "aplus/coding-standard": "^2.8",
        "aplus/testing": "^3.0",
        "ergebnis/composer-normalize": "^2.44",
        "jetbrains/phpstorm-attributes": "^1.1",
        "natanfelles/php-server": "^2.11",
        "phpmd/phpmd": "^2.15",
        "phpstan/phpstan": "^1.12"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "App.php"
        ],
        "files": [
            "boot/init.php",
            "boot/constants.php",
            "boot/helpers.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "config": {
        "allow-plugins": {
            "ergebnis/composer-normalize": true
        },
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true
    }
}


================================================
FILE: config/antiCsrf.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Anti-CSRF config.
 *
 * @see App::antiCsrf()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#anti-csrf-service
 */
return [
    'default' => [
        'enabled' => true,
        'token_name' => 'csrf_token',
        'token_bytes_length' => 8,
        'generate_token_function' => 'base64_encode',
        'request_instance' => 'default',
        'session_instance' => 'default',
    ],
];


================================================
FILE: config/autoloader.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Autoloader config.
 *
 * @see App::autoloader()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#autoloader-service
 */
return [
    'default' => [
        'register' => true,
        'extensions' => '.php',
        'namespaces' => [
            'App' => APP_DIR,
        ],
        'classes' => [],
    ],
];


================================================
FILE: config/cache.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Cache config.
 *
 * @see App::cache()
 * @see Framework\Cache\FilesCache::$configs
 * @see Framework\Cache\MemcachedCache::$configs
 * @see Framework\Cache\RedisCache::$configs
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#cache-service
 */

use Framework\Cache\FilesCache;
use Framework\Cache\Serializer;

return [
    'default' => [
        'class' => env('cache.default.class', FilesCache::class),
        'configs' => env('cache.default.configs', [
            'directory' => STORAGE_DIR . 'cache',
        ]),
        'prefix' => env('cache.default.prefix'),
        'serializer' => Serializer::PHP,
        'default_ttl' => null,
        'logger_instance' => 'default',
    ],
];


================================================
FILE: config/console.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Console config.
 *
 * @see App::console()
 * @see App::run()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#console-service
 */
return [
    'default' => [
        'find_in_namespaces' => false,
        'directories' => [
            APLUS_DIR . 'dev-commands/src',
            APP_DIR . 'Commands',
        ],
        'commands' => null,
        'language_instance' => 'default',
        'locator_instance' => 'default',
    ],
];


================================================
FILE: config/database.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Database config.
 *
 * @see App::database()
 * @see Framework\Database\Database::makeConfig()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#database-service
 */
return [
    'default' => [
        'config' => [
            'username' => env('database.default.config.username', 'root'),
            'password' => env('database.default.config.password', 'password'),
            'schema' => env('database.default.config.schema', 'framework-tests'),
            'host' => env('database.default.config.host', 'localhost'),
            'port' => env('database.default.config.port', 3306),
        ],
        'logger_instance' => 'default',
    ],
];


================================================
FILE: config/debugger.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Debugger config.
 *
 * @see App::debugger()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#debugger-service
 */
return [
    'default' => [
        'debugbar_view' => null,
        'options' => [
            'color' => 'magenta',
            'icon_path' => null,
            'info_contents' => null,
        ],
    ],
];


================================================
FILE: config/exceptionHandler.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Exceptions config.
 *
 * @see App::exceptionHandler()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#exception-handler-service
 */

use Framework\Debug\ExceptionHandler;

return [
    'default' => [
        'initialize' => true,
        'handle_errors' => true,
        'environment' => IS_DEV
            ? ExceptionHandler::DEVELOPMENT
            : ExceptionHandler::PRODUCTION,
        'development_view' => null,
        'production_view' => null,
        'search_engine' => null,
        'show_log_id' => null,
        'json_flags' => null,
        'hidden_inputs' => null,
        'logger_instance' => 'default',
        'language_instance' => 'default',
    ],
];


================================================
FILE: config/language.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Language config.
 *
 * @see App::language()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#language-service
 */

use Framework\Language\FallbackLevel;

return [
    'default' => [
        'default' => 'en',
        'current' => 'en',
        'supported' => [
            'en',
            'es',
            'pt-br',
        ],
        'fallback_level' => FallbackLevel::none,
        'directories' => [
            APP_DIR . 'Languages',
        ],
        'find_in_namespaces' => false,
        'negotiate' => false,
        'autoloader_instance' => 'default',
        'request_instance' => 'default',
    ],
];


================================================
FILE: config/locator.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Locator config.
 *
 * @see App::locator()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#locator-service
 */
return [
    'default' => [
        'autoloader_instance' => 'default',
    ],
];


================================================
FILE: config/logger.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Logger config.
 *
 * @see App::logger()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#logger-service
 */

use Framework\Log\Loggers\MultiFileLogger;
use Framework\Log\LogLevel;

return [
    'default' => [
        'class' => env('logger.default.class', MultiFileLogger::class),
        'destination' => env('logger.default.destination', STORAGE_DIR . 'logs'),
        'level' => env('logger.default.level', LogLevel::DEBUG),
        'config' => [],
    ],
];


================================================
FILE: config/mailer.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Mailer config.
 *
 * @see App::mailer()
 * @see Framework\Email\Mailer::makeConfig()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#mailer-service
 */

return [
    'default' => [
        'host' => env('mailer.default.host', 'localhost'),
        'port' => env('mailer.default.port', 587),
        'tls' => env('mailer.default.tls', true),
        'username' => env('mailer.default.username'),
        'password' => env('mailer.default.password'),
        'charset' => env('mailer.default.charset', 'utf-8'),
        'crlf' => env('mailer.default.crlf', "\r\n"),
        'keep_alive' => env('mailer.default.keep_alive', false),
        'save_logs' => env('mailer.default.save_logs', false),
    ],
];


================================================
FILE: config/migrator.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Migrator config.
 *
 * @see App::migrator()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#migrator-service
 */

return [
    'default' => [
        'directories' => [
            APP_DIR . 'Migrations',
        ],
        'table' => 'Migrations',
        'database_instance' => 'default',
    ],
];


================================================
FILE: config/request.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Request config.
 *
 * @see App::request()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#request-service
 */
return [
    'default' => [
        'allowed_hosts' => env('request.default.allowed_hosts', []),
        'force_https' => env('request.default.force_https', false),
        'server_vars' => [],
        'json_flags' => null,
    ],
];


================================================
FILE: config/response.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Response config.
 *
 * @see App::response()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#response-service
 */
return [
    'default' => [
        'headers' => [],
        'auto_etag' => false,
        'auto_language' => false,
        'cache' => null,
        'csp' => [],
        'csp_report_only' => [],
        'json_flags' => null,
        'replace_headers' => null,
        'language_instance' => 'default',
        'request_instance' => 'default',
    ],
];


================================================
FILE: config/router.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Router config.
 *
 * @see App::router()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#router-service
 */
return [
    'default' => [
        'files' => [
            BOOT_DIR . 'routes.php',
        ],
        'auto_options' => false,
        'auto_methods' => false,
        'placeholders' => [],
        'response_instance' => 'default',
        'language_instance' => 'default',
    ],
];


================================================
FILE: config/session.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Session config.
 *
 * @see App::session()
 * @see Framework\Session\Session::setOptions()
 * @see Framework\Session\SaveHandlers\DatabaseHandler::prepareConfig()
 * @see Framework\Session\SaveHandlers\FilesHandler::prepareConfig()
 * @see Framework\Session\SaveHandlers\MemcachedHandler::prepareConfig()
 * @see Framework\Session\SaveHandlers\RedisHandler::prepareConfig()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#session-service
 */

use Framework\Session\SaveHandlers\FilesHandler;

return [
    'default' => [
        'options' => [],
        'save_handler' => [
            'class' => FilesHandler::class,
            'config' => [
                'prefix' => '',
                'directory' => STORAGE_DIR . 'sessions',
                'match_ip' => false,
                'match_ua' => false,
            ],
        ],
        'logger_instance' => 'default',
        'auto_start' => true,
    ],
];


================================================
FILE: config/validation.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * Validation config.
 *
 * @see App::validation()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#validation-service
 */

use Framework\MVC\Validator;
use Framework\Validation\FilesValidator;

return [
    'default' => [
        'validators' => [
            Validator::class,
            FilesValidator::class,
        ],
        'language_instance' => 'default',
    ],
];


================================================
FILE: config/view.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * View config.
 *
 * @see App::view()
 * @see https://docs.aplus-framework.com/guides/libraries/mvc/index.html#view-service
 */
return [
    'default' => [
        'base_dir' => APP_DIR . 'Views',
        'extension' => '.php',
        'layout_prefix' => '_layouts',
        'include_prefix' => '_includes',
        'show_debug_comments' => true,
        'throw_exceptions_in_destructor' => null,
    ],
];


================================================
FILE: docker-compose.yml
================================================
version: "3.0"
services:
  lempa:
    image: registry.gitlab.com/aplus-framework/images/lempa:4
    container_name: lempa-app
    working_dir: /var/www/aplus
    volumes:
      - .:/var/www/aplus
    ports:
      - "80:80"
      - "443:443"
    #environment:
    #  - PRELOAD=/var/www/aplus/preload.php
    tty: true
  lempa-latest:
    image: registry.gitlab.com/aplus-framework/images/lempa:latest
    container_name: lempa-app-latest
    working_dir: /var/www/aplus
    volumes:
      - .:/var/www/aplus
    ports:
      - "80:80"
      - "443:443"
    #environment:
    #  - PRELOAD=/var/www/aplus/preload.php
    tty: true


================================================
FILE: guide/index.rst
================================================
App
===

.. image:: image.png
    :alt: Aplus Framework App Project

Aplus Framework App Project.

- `Installation`_
- `Structure`_
- `Bootstrap`_
- `Configuration`_
- `Storage`_
- `The global class App`_
- `The App namespace`_
- `Running an Aplus App`_
- `Testing`_
- `Deployment`_
- `Conclusion`_

Installation
------------

The installation of this project can be done with Composer:

.. code-block::

    composer create-project aplus/app

Or, to install the latest `LTS <https://aplus-framework.com/lts>`_ version:

.. code-block::

    composer create-project aplus/app:^24

Structure
---------

The App has a standard structure to organize business logic.

And remember, it's highly customizable. You can adapt it as you like.

This is the basic directory tree:

.. code-block::

    .
    ├── app/
    │   ├── Commands/
    │   │   └── Index.php
    │   ├── Controllers/
    │   │   └── Home.php
    │   ├── Languages/
    │   ├── Models/
    │   └── Views/
    ├── App.php
    ├── bin/
    │   └── console
    ├── boot/
    │   ├── app.php
    │   ├── constants.php
    │   ├── helpers.php
    │   ├── init.php
    │   └── routes.php
    ├── composer.json
    ├── config/
    ├── .env.php
    ├── preload.php
    ├── public/
    │   └── index.php
    ├── storage/
    ├── tests/
    └── vendor/

Bootstrap
---------

Inside the **boot** directory are located files that are part of the application
startup.

App
###

The **app.php** file is responsible for loading the files needed for the app to work.
Such as the Composer autoloader or the initialization files.

It returns an instance of the ``App`` class, which is called to run the application
in HTTP or CLI.

Init
####

The **init.php** file is responsible for setting the environment variables
that are defined in the **.env.php** file.

Also, initial settings are performed, such as setting
`error_reporting <https://www.php.net/manual/en/function.error-reporting.php>`_
and `display_errors <https://www.php.net/manual/en/errorfunc.configuration.php#ini.display-errors>`_.

Constants
#########

In the **constants.php** file, the constants that will be available throughout
the application are set, such as the **ENVIRONMENT** and the paths to the
different directories.

Helpers
#######

The **helpers.php** file contains common functions that will always be available
in the application.

Routes
######

In the **routes.php** file, the application routes are served.


Configuration
-------------

Aplus App is organized in such a way that its configuration files are all in the
same directory.

By default, the directory is called **config**. Located in the application's root
directory.

Configuration files serve to pre-establish values used by services
or routines needed for `helpers`_ and `libraries <https://docs.aplus-framework.com/guides/libraries/index.html>`_.

For more details see the `Config <https://docs.aplus-framework.com/guides/libraries/config/index.html>`_
and `MVC <https://docs.aplus-framework.com/guides/libraries/mvc/index.html>`_
libraries documentation.

Storage
-------

In the **storage** directory, different types of files are stored in
subdirectories:

Cache
#####

Cache files are stored in the **cache** directory.

Logs
####

Log files are stored in the **logs** directory.

Sessions
########

Session files are stored in the **sessions** directory.

Uploads
#######

Upload files are stored in the **uploads** directory.

The global class App
--------------------

The global class ``App``, whose file is located in the root directory, extends
the ``Framework\MVC\App`` class.

Through it, it is possible to customize features and
`services <https://docs.aplus-framework.com/guides/libraries/mvc/index.html#services>`_.

The App namespace
-----------------

Inside the **app** directory is registered the ``App`` namespace.

By default, some files are already inside it:

Commands
########

In the **Commands** directory is the ``App\Commands`` namespace.

In it, you can add commands that will be available in the console.

Controllers
###########

In the **Controllers** directory is the ``App\Controllers`` namespace.

In it, you can add controllers with methods that will act as routes.

Languages
#########

In the subdirectories of **Languages** are stored application language files.

Models
######

In the **Models** directory is the ``App\Models`` namespace.

In it it is possible to add models that represent tables of the application's
database schema.

Views
#####

In the **Views** directory are stored application view files.

Running an Aplus App
--------------------

The Aplus App project is designed to run on HTTP and CLI.

Run HTTP
########

Inside the **public** directory is the front-controller **index.php**.

The **public** directory must be the document root configured on the server.

Note that the directory name may vary by server. In some it may be called
**public_html** and in others **web**, etc.

In development, you can use PHP server running ``vendor/bin/php-server`` or
Docker Compose.

Run CLI
#######

Inside the **bin** directory is the **console** file.

Through it it is possible to run the various commands of the application,
running ``./bin/console``.

Testing
-------

Unit tests can be created within the **tests** directory. See the tests that
come inside it as an example.

Deployment
----------

We will see how to deploy to a `Shared Hosting`_ and a `Private Server`_:

In the following examples, configurations will be made for the domain **domain.tld**.
Replace it with the domain of your application.

Shared Hosting
##############

In shared hosting, it is common that you can upload the project files only by FTP.

Also, typically the document root is a publicly accessible directory called
**www**, **web** or **public_html**.

And the server is Apache, which allows configurations through files called
**.htaccess**.

In the following example the settings can be made locally and then sent to the
hosting server.

Environment Variables
"""""""""""""""""""""

Environment variables are defined in the **.env.php** file.

Edit them according to the examples below:

ENVIRONMENT
^^^^^^^^^^^

Make sure ENVIRONMENT is set to ``production``:

.. code-block:: php

    $_ENV['ENVIRONMENT'] = 'production';

URL Origin
^^^^^^^^^^

Make sure that the URL Origin has the correct domain:

.. code-block:: php

    $_ENV['app.default.origin'] = 'http://domain.tld';

Install Dependencies
""""""""""""""""""""

Install dependencies with Composer:

.. code-block::

    composer install --no-dev

.htaccess files
"""""""""""""""

In the document root and in the **public** directory of the application has
**.htaccess** files that can be configured as needed.

For example, redirecting insecure requests to **HTTPS** or redirecting to the
**www** subdomain.

Finishing
"""""""""

Upload the files to the public directory of your hosting.

Access the domain through the browser: http://domain.tld

It should open the home page of your project.

Private Server
##############

We will be using Ubuntu 24.04 LTS which is supported until 2029 and already
comes with PHP 8.3.

Replace ``domain.tld`` with your domain.

Installing PHP and required packages:

.. code-block::

    sudo apt-get -y install \
    composer \
    curl \
    git \
    php8.3-apcu \
    php8.3-cli \
    php8.3-curl \
    php8.3-fpm \
    php8.3-gd \
    php8.3-igbinary \
    php8.3-intl \
    php8.3-mbstring \
    php8.3-memcached \
    php8.3-msgpack \
    php8.3-mysql \
    php8.3-opcache \
    php8.3-readline \
    php8.3-redis \
    php8.3-xml \
    php8.3-yaml \
    php8.3-zip \
    unzip

Make the application directory:

.. code-block::

    sudo mkdir -p /var/www/domain.tld

Set directory ownership. Replace "username" with your username:

.. code-block::

    sudo chown username:username /var/www/domain.tld

Enter the application directory...

.. code-block::

    cd /var/www/domain.tld

... and clone or download your project.

As an example, we'll install a new app:

.. code-block::

    git clone https://github.com/aplus-framework/app.git .

Set the owner of the storage directory:

.. code-block::

    sudo chown -R www-data:www-data storage

Edit the Environment and the URL Origin of your project in the **.env.php**
file:

.. code-block:: php

    $_ENV['ENVIRONMENT'] = 'production';
    $_ENV['app.default.origin'] = 'http://domain.tld';

Install the necessary PHP packages through Composer:

.. code-block::

    composer install --no-dev --ignore-platform-req=ext-xdebug

* We use ``install`` instead of ``update`` to respect the **composer.lock** file if it exists in your repository.

* We use ``--ignore-platform-req=ext-xdebug`` because we don't need the xdebug extension in production.

Web Servers
"""""""""""

In these examples, we will see how to install and configure two web servers:

- `Apache`_
- `Nginx (recommended)`_

Apache
^^^^^^

Install required packages:

.. code-block::

    sudo apt install apache2 libapache2-mod-php

Enable modules:

.. code-block::

    sudo a2enmod rewrite

Create the file **/etc/apache2/sites-available/domain.tld.conf**:

.. code-block:: apacheconf

    <Directory /var/www/domain.tld/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    <VirtualHost *:80>
        ServerName domain.tld
        SetEnv ENVIRONMENT production
        DocumentRoot /var/www/domain.tld/public
    </VirtualHost>

Enable the site:

.. code-block::

    sudo a2ensite domain.tld

Reload the server:

.. code-block::

    sudo systemctl reload apache2

Access the domain through the browser: http://domain.tld

It should open the home page of your project.

Nginx (recommended)
^^^^^^^^^^^^^^^^^^^

Edit the **php.ini** file:

.. code-block::

    sudo sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php/8.3/fpm/php.ini

Restart PHP-FPM:

.. code-block::

    sudo systemctl restart php8.3-fpm

Install required packages:

.. code-block::

    sudo apt install nginx

Create the file **/etc/nginx/sites-available/domain.tld.conf**:

.. code-block:: nginx

    server {
        listen 80;

        root /var/www/domain.tld/public;

        index index.php;

        server_name domain.tld;

        location / {
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_param ENVIRONMENT production;
            fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        }

        location ~ /\. {
            deny all;
        }
    }

Enable the site:

.. code-block::

    sudo ln -s /etc/nginx/sites-available/domain.tld.conf /etc/nginx/sites-enabled/

Test Nginx configurations:

.. code-block::

    sudo nginx -t

Restart Nginx:

.. code-block::

    sudo systemctl restart nginx

Access the domain through the browser: http://domain.tld

It should open the home page of your project.

Conclusion
----------

Aplus App Project is an easy-to-use tool for, beginners and experienced, PHP developers. 
It is perfect for building powerful, high-performance applications. 
The more you use it, the more you will learn.

.. note::
    Did you find something wrong? 
    Be sure to let us know about it with an
    `issue <https://github.com/aplus-framework/app/issues>`_. 
    Thank you!


================================================
FILE: php-server.ini
================================================
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; php-server configuration file - https://github.com/natanfelles/php-server ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
php = PHP_BINARY
host = localhost
port = 8080
root = ./public
autoindex = true
index = index.html index.php
error_reporting = E_ALL

[ini]
display_errors = 1
display_startup_errors = 1
max_execution_time = 30
post_max_size = 8M
upload_max_filesize = 2M
;opcache.preload = preload.php

xdebug.mode=debug,develop
xdebug.var_display_max_depth = 10
xdebug.var_display_max_children = 256
xdebug.var_display_max_data = 1024

[server]
;ENVIRONMENT = development


================================================
FILE: phpdoc.dist.xml
================================================
<?xml version="1.0" encoding="UTF-8" ?>
<phpdocumentor
    configVersion="3"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="https://www.phpdoc.org"
    xsi:noNamespaceSchemaLocation="https://docs.phpdoc.org/latest/phpdoc.xsd"
>
    <title>Aplus Framework App Project</title>
    <paths>
        <output>build/docs</output>
        <cache>build/docs-cache</cache>
    </paths>
    <version number="latest">
        <folder>latest</folder>
        <api>
            <source dsn=".">
                <path>app</path>
                <path>extra</path>
                <path>App.php</path>
            </source>
            <default-package-name>application</default-package-name>
            <include-source>true</include-source>
        </api>
        <guide>
            <source dsn=".">
                <path>guide</path>
            </source>
            <output>guide</output>
        </guide>
    </version>
    <setting name="graphs.enabled" value="true"/>
    <setting name="guides.enabled" value="true"/>
    <template name="default"/>
</phpdocumentor>



================================================
FILE: phpmd.xml
================================================
<?xml version="1.0"?>
<ruleset name="Mess Detector">
    <!-- cleancode -->
    <rule ref="rulesets/cleancode.xml">
        <exclude name="BooleanArgumentFlag"/>
        <exclude name="StaticAccess"/>
    </rule>
    <!-- codesize -->
    <rule ref="rulesets/codesize.xml"/>
    <!-- controversial -->
    <rule ref="rulesets/controversial.xml"/>
    <!-- design -->
    <rule ref="rulesets/design.xml"/>
    <!-- naming -->
    <rule ref="rulesets/naming.xml"/>
    <!-- unusedcode -->
    <rule ref="rulesets/unusedcode.xml"/>
</ruleset>


================================================
FILE: phpstan.neon.dist
================================================
parameters:
    level: 7
    paths:
        - app
        - boot
        - config
        - public
        - tests
    dynamicConstantNames:
        - ENVIRONMENT
        - IS_DEV
        - ROOT_DIR
        - APP_DIR
        - BIN_DIR
        - BOOT_DIR
        - CONFIG_DIR
        - PUBLIC_DIR
        - STORAGE_DIR
        - VENDOR_DIR
        - APLUS_DIR


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false"
         bootstrap="vendor/autoload.php" colors="true" stopOnError="false" stopOnFailure="false"
         stopOnIncomplete="false" stopOnSkipped="false"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.2/phpunit.xsd"
         cacheDirectory=".phpunit.cache">
    <coverage>
        <report>
            <clover outputFile="build/coverage/clover.xml"/>
            <html outputDirectory="build/coverage"/>
            <text outputFile="php://stdout"/>
        </report>
    </coverage>
    <testsuite name="Tests">
        <directory suffix="Test.php">tests</directory>
    </testsuite>
    <logging/>
    <source>
        <include>
            <directory suffix=".php">app</directory>
            <directory suffix=".php">config</directory>
            <file>.env.php</file>
            <file>App.php</file>
            <file>bin/console</file>
            <file>boot/helpers.php</file>
            <file>boot/routes.php</file>
            <file>public/index.php</file>
        </include>
    </source>
</phpunit>


================================================
FILE: preload.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
require __DIR__ . '/vendor/aplus/autoload/src/Preloader.php';

use Framework\Autoload\Preloader;

$files = (new Preloader())->load();
echo 'Preloading: ' . \PHP_EOL;
foreach ($files as $index => $file) {
    echo ++$index . ') ' . $file . \PHP_EOL;
}
echo 'Total of ' . count($files) . ' preloaded files.' . \PHP_EOL . \PHP_EOL;


================================================
FILE: public/.htaccess
================================================
Options All -Indexes

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Redirect non-www to HTTPS www
    #RewriteCond %{HTTP_HOST} !^www\. [NC]
    #RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [L,R=301]

    # Redirect to HTTPS
    #RewriteCond %{HTTPS} off
    #RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [L,R=301]

    # Send requests to front controller
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^ index.php [QSA,L]
</IfModule>


================================================
FILE: public/index.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
(require __DIR__ . '/../boot/app.php')->runHttp();


================================================
FILE: public/robots.txt
================================================
User-agent: *
Disallow:


================================================
FILE: storage/cache/.gitignore
================================================
*
!.gitignore


================================================
FILE: storage/logs/.gitignore
================================================
*
!.gitignore


================================================
FILE: storage/sessions/.gitignore
================================================
*
!.gitignore


================================================
FILE: storage/uploads/.gitignore
================================================
*
!.gitignore


================================================
FILE: tests/AppTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests;

use App;
use Framework\Config\Config;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

#[RunTestsInSeparateProcesses]
final class AppTest extends TestCase
{
    protected function setUp() : void
    {
        //
    }

    public function testInstance() : void
    {
        self::assertInstanceOf(
            \Framework\MVC\App::class,
            new App(new Config(CONFIG_DIR))
        );
    }
}


================================================
FILE: tests/TestCase.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests;

abstract class TestCase extends \Framework\Testing\TestCase
{
    protected array | string | null $configs = CONFIG_DIR;
}


================================================
FILE: tests/app/Commands/IndexTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\app\Commands;

use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use Tests\TestCase;

#[RunTestsInSeparateProcesses]
final class IndexTest extends TestCase
{
    public function testRun() : void
    {
        $this->app->runCli('index');
        self::assertStdoutContains('index');
    }
}


================================================
FILE: tests/app/Controllers/HomeTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\app\Controllers;

use Framework\HTTP\Status;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use Tests\TestCase;

#[RunTestsInSeparateProcesses]
final class HomeTest extends TestCase
{
    public function testIndex() : void
    {
        $this->app->runHttp('http://localhost:8080');
        self::assertResponseStatusCode(Status::OK);
        self::assertResponseBodyContains('Aplus Framework');
        self::assertMatchedRouteName('home.index');
    }
}


================================================
FILE: tests/app/Languages/LanguagesTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\app\Languages;

use App;
use Tests\TestCase;

final class LanguagesTest extends TestCase
{
    protected string $langDir = APP_DIR . 'Languages/';

    public function testKeys() : void
    {
        $defaultLocale = App::language()->getDefaultLocale();
        foreach ($this->getFiles($defaultLocale) as $file) {
            $rules = require $this->langDir . $defaultLocale . '/' . $file;
            $rules = \array_keys($rules);
            foreach ($this->getCodes() as $code) {
                $lines = require $this->langDir . $code . '/' . $file;
                $lines = \array_keys($lines);
                \sort($lines);
                self::assertSame(
                    $rules,
                    $lines,
                    \sprintf(
                        'Comparing "%s" with "%s"',
                        $defaultLocale . '/' . $file,
                        $code . '/' . $file
                    )
                );
            }
        }
    }

    /**
     * @return array<int,string>
     */
    protected function getCodes() : array
    {
        // @phpstan-ignore-next-line
        $codes = \array_filter((array) \glob($this->langDir . '*'), 'is_dir');
        $length = \strlen($this->langDir);
        $result = [];
        foreach ($codes as $dir) {
            if ($dir === false) {
                continue;
            }
            $result[] = \substr($dir, $length);
        }
        return $result;
    }

    /**
     * @return array<int,string>
     */
    protected function getFiles(string $locale) : array
    {
        $filenames = App::locator()->listFiles($this->langDir . $locale);
        foreach ($filenames as $filename) {
            $files[] = \substr($filename, \strrpos($filename, \DIRECTORY_SEPARATOR) + 1);
        }
        return $files ?? [];
    }
}


================================================
FILE: tests/bin/ConsoleTest.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\bin;

use Framework\CLI\Streams\Stdout;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
final class ConsoleTest extends TestCase
{
    public function testConsole() : void
    {
        Stdout::init();
        require __DIR__ . '/../../bin/console';
        self::assertStringContainsString('about', Stdout::getContents());
        self::assertStringContainsString('help', Stdout::getContents());
        self::assertStringContainsString('index', Stdout::getContents());
    }
}


================================================
FILE: tests/boot/AppTest.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\boot;

use Framework\MVC\App;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
final class AppTest extends TestCase
{
    public function testApp() : void
    {
        $app = require __DIR__ . '/../../boot/app.php';
        self::assertInstanceOf(App::class, $app);
    }
}


================================================
FILE: tests/boot/ConstantsTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\boot;

use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use Tests\TestCase;

#[RunTestsInSeparateProcesses]
final class ConstantsTest extends TestCase
{
    public function testConstants() : void
    {
        require_once __DIR__ . '/../../boot/constants.php';
        self::assertIsString(ENVIRONMENT);
        self::assertIsBool(IS_DEV);
        self::assertDirectoryExists(ROOT_DIR);
        self::assertDirectoryExists(APP_DIR);
        self::assertDirectoryExists(BIN_DIR);
        self::assertDirectoryExists(BOOT_DIR);
        self::assertDirectoryExists(CONFIG_DIR);
        self::assertDirectoryExists(PUBLIC_DIR);
        self::assertDirectoryExists(STORAGE_DIR);
        self::assertDirectoryExists(VENDOR_DIR);
        self::assertDirectoryExists(APLUS_DIR);
    }
}


================================================
FILE: tests/boot/HelpersTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\boot;

use App;
use Framework\HTTP\Response;
use Framework\HTTP\Status;
use Framework\Session\Session;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use Tests\support\Models\UsersModel;
use Tests\TestCase;

#[RunTestsInSeparateProcesses]
final class HelpersTest extends TestCase
{
    public function testHelpers() : void
    {
        $files = helpers('tests');
        self::assertEmpty($files);
        $dir = \realpath(__DIR__ . '/../support/') . \DIRECTORY_SEPARATOR;
        App::autoloader()->addNamespace('Support', $dir);
        $helperPath = $dir . 'Helpers/tests.php';
        $files = helpers('tests');
        self::assertSame([$helperPath], $files);
        $files = helpers(['tests']);
        self::assertSame([$helperPath], $files);
    }

    public function testEsc() : void
    {
        self::assertSame('', esc(null));
        self::assertSame('', esc(''));
        self::assertSame('&lt;script&gt;&lt;/script&gt;', esc('<script></script>'));
    }

    public function testView() : void
    {
        self::assertStringContainsString(
            'Aplus Framework',
            view('home/index')
        );
    }

    public function testCurrentUrl() : void
    {
        $this->app->runHttp('http://localhost:8080');
        self::assertSame('http://localhost:8080/', current_url());
    }

    public function testCurrentRoute() : void
    {
        $this->app->runHttp('http://localhost:8080');
        self::assertMatchedRouteName(current_route()->getName());
    }

    public function testRouteUrl() : void
    {
        $configs = App::config()->get('router');
        $configs['files'][] = __DIR__ . '/../support/routes.php';
        App::config()->set('router', $configs);
        $this->app->runHttp('https://foo.com/users/25');
        self::assertSame(
            'http://localhost:8080/',
            route_url('home.index')
        );
        self::assertSame(
            'https://foo.com/',
            route_url('test.home')
        );
        self::assertSame(
            'https://foo.com/users/{int}',
            route_url('test.users.show')
        );
        self::assertSame(
            'https://foo.com/users/25',
            route_url('test.users.show', [25])
        );
        self::assertSame(
            'http://foo.com/users/13',
            route_url('test.users.show', [13], ['http'])
        );
    }

    public function testSession() : void
    {
        self::assertInstanceOf(Session::class, session());
    }

    public function testOld() : void
    {
        $this->setOldData();
        self::assertNull(old('unknown'));
        self::assertSame('', old('user'));
        self::assertSame('John Doe', old('user[name]'));
        self::assertSame(
            '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;',
            old('xss')
        );
        self::assertSame(
            '<script>alert("xss")</script>',
            old('xss', false)
        );
    }

    protected function setOldData() : void
    {
        App::session()->activate();
        App::response()->redirect('/foo', [
            'user' => [
                'name' => 'John Doe',
            ],
            'xss' => '<script>alert("xss")</script>',
        ]);
        App::session()->stop();
    }

    public function testHasOld() : void
    {
        $this->setOldData();
        self::assertTrue(has_old());
        self::assertTrue(has_old('user'));
        self::assertTrue(has_old('user[name]'));
        self::assertTrue(has_old('xss'));
        self::assertFalse(has_old('foo'));
        self::assertFalse(has_old('bar'));
        self::assertFalse(has_old('bar[bazzz]'));
    }

    public function testHasOldWithoutRedirectData() : void
    {
        self::assertFalse(has_old());
        self::assertFalse(has_old('user'));
        self::assertFalse(has_old('user[name]'));
        self::assertFalse(has_old('bar[bazzz]'));
    }

    public function testRedirect() : void
    {
        self::assertNull(App::response()->getHeader('Location'));
        redirect('/foo');
        self::assertSame('/foo', App::response()->getHeader('Location'));
        redirect('/bar', ['foo' => 'Foo']);
        self::assertSame('/bar', App::response()->getHeader('Location'));
        self::assertSame('Foo', App::request()->getRedirectData('foo'));
    }

    public function testRedirectTo() : void
    {
        $configs = App::config()->get('router');
        $configs['files'][] = __DIR__ . '/../support/routes.php';
        App::config()->set('router', $configs);
        $this->app->runHttp('https://foo.com/users/25');
        $response = redirect_to('home.index');
        self::assertSame(
            'http://localhost:8080/',
            $response->getHeader('Location')
        );
        $response = redirect_to('test.users.show');
        self::assertSame(
            'https://foo.com/users/{int}',
            $response->getHeader('Location')
        );
        $response = redirect_to(['test.users.show', [25]]);
        self::assertSame(
            'https://foo.com/users/25',
            $response->getHeader('Location')
        );
        $response = redirect_to(['test.users.show', [13], ['http']]);
        self::assertSame(
            'http://foo.com/users/13',
            $response->getHeader('Location')
        );
        $response = redirect_to('home.index', ['foo' => 'bar'], Status::SEE_OTHER);
        self::assertSame(
            Status::SEE_OTHER,
            $response->getStatusCode()
        );
        self::assertSame(
            'http://localhost:8080/',
            $response->getHeader('Location')
        );
        self::assertSame(
            ['foo' => 'bar'],
            $response->getRequest()->getRedirectData()
        );
    }

    public function testCsrfInput() : void
    {
        self::assertStringContainsString('<input', csrf_input());
    }

    public function testRespondNotFound() : void
    {
        $response = respond_not_found();
        self::assertInstanceOf(Response::class, $response);
        self::assertStringContainsString('404', $response->getBody());
        self::assertSame(404, $response->getStatusCode());
        self::assertNull($response->getHeader('Content-Type'));
    }

    public function testRespondNotFoundWithContentTypeJson() : void
    {
        $_SERVER['HTTP_CONTENT_TYPE'] = 'application/json';
        $response = respond_not_found();
        self::assertInstanceOf(Response::class, $response);
        self::assertStringContainsString('404', $response->getBody());
        self::assertSame(404, $response->getStatusCode());
        self::assertSame(
            'application/json; charset=UTF-8',
            $response->getHeader('Content-Type')
        );
    }

    public function testRespondNotFoundWithAcceptJson() : void
    {
        $_SERVER['HTTP_ACCEPT'] = 'application/json';
        $response = respond_not_found();
        self::assertInstanceOf(Response::class, $response);
        self::assertStringContainsString('404', $response->getBody());
        self::assertSame(404, $response->getStatusCode());
        self::assertSame(
            'application/json; charset=UTF-8',
            $response->getHeader('Content-Type')
        );
    }

    public function testRespondNotFoundWithAcceptHtml() : void
    {
        $_SERVER['HTTP_ACCEPT'] = 'text/html';
        $response = respond_not_found();
        self::assertInstanceOf(Response::class, $response);
        self::assertStringContainsString('404', $response->getBody());
        self::assertSame(404, $response->getStatusCode());
        self::assertNull($response->getHeader('Content-Type'));
    }

    public function testConfig() : void
    {
        $config = config('view');
        self::assertSame(APP_DIR . 'Views', $config['base_dir']);
        $baseDir = config('view', 'default[base_dir]');
        self::assertSame(APP_DIR . 'Views', $baseDir);
        $baseDir = config('view', 'default[base_dir');
        self::assertSame(APP_DIR . 'Views', $baseDir);
        $baseDir = config('view', 'default[base_directory');
        self::assertNull($baseDir);
    }

    public function testModel() : void
    {
        self::assertInstanceOf(UsersModel::class, model(UsersModel::class));
        self::assertSame('foo', model(UsersModel::class)->foo());
    }

    public function testEnv() : void
    {
        $_ENV['foo'] = 'foo';
        $_SERVER['foo'] = 'bar';
        $_SERVER['bar'] = 'baz';
        self::assertSame('foo', env('foo'));
        self::assertNull(env('bar'));
        self::assertNull(env('unknown'));
    }
}


================================================
FILE: tests/boot/InitTest.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\boot;

use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
final class InitTest extends TestCase
{
    protected function setUp() : void
    {
        \ob_start();
        require __DIR__ . '/../../boot/init.php';
        \ob_end_clean();
    }

    public function testErrorReporting() : void
    {
        if ($_SERVER['ENVIRONMENT'] === 'development') {
            self::assertSame(-1, \error_reporting());
            return;
        }
        self::assertSame(
            \E_ALL & ~\E_DEPRECATED & ~\E_NOTICE & ~\E_USER_DEPRECATED & ~\E_USER_NOTICE,
            \error_reporting()
        );
    }

    public function testDisplayErrors() : void
    {
        if ($_SERVER['ENVIRONMENT'] === 'development') {
            self::assertSame('On', \ini_get('display_errors'));
            return;
        }
        self::assertSame('Off', \ini_get('display_errors'));
    }

    public function testDefaultTimezone() : void
    {
        self::assertSame('UTC', \date_default_timezone_get());
    }
}


================================================
FILE: tests/boot/RoutesTest.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\boot;

use Framework\HTTP\Status;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use Tests\TestCase;

#[RunTestsInSeparateProcesses]
final class RoutesTest extends TestCase
{
    public function testNotFound() : void
    {
        $this->app->runHttp('http://localhost:8080/foo');
        self::assertResponseStatusCode(Status::NOT_FOUND);
        self::assertResponseBodyContains('Error 404');
        self::assertMatchedRouteName('collection-not-found');
    }
}


================================================
FILE: tests/config/ConfigsTest.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\config;

use PHPUnit\Framework\TestCase;

final class ConfigsTest extends TestCase
{
    public function testConfigs() : void
    {
        $files = (array) \glob(CONFIG_DIR . '*.php');
        foreach ($files as $file) {
            $config = require $file;
            self::assertArrayHasKey('default', $config);
        }
    }
}


================================================
FILE: tests/public/IndexTest.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\public;

use Framework\HTTP\Status;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
final class IndexTest extends TestCase
{
    public function testIndex() : void
    {
        $_SERVER['REQUEST_SCHEME'] = 'http';
        $_SERVER['HTTP_HOST'] = 'localhost:8080';
        $_SERVER['REQUEST_URI'] = '/';
        \ob_start();
        require __DIR__ . '/../../public/index.php';
        $contents = \ob_get_clean();
        $headers = xdebug_get_headers();
        self::assertNotEmpty($contents);
        self::assertSame(Status::OK, \http_response_code());
        self::assertContains('Content-Type: text/html; charset=UTF-8', $headers);
    }
}


================================================
FILE: tests/support/Helpers/tests.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */


================================================
FILE: tests/support/Models/UsersModel.php
================================================
<?php declare(strict_types=1);
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Tests\support\Models;

use Framework\MVC\Model;

class UsersModel extends Model
{
    public function foo() : string
    {
        return 'foo';
    }
}


================================================
FILE: tests/support/routes.php
================================================
<?php
/*
 * This file is part of App Project.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
use Framework\Routing\RouteCollection;

App::router()->serve('{scheme}://foo.com', static function (RouteCollection $routes) : void {
    $routes->get('/', static fn () => 'Homepage', 'home');
    $routes->get('/users/{int}', static fn () => 'Show user', 'users.show');
}, 'test');
Download .txt
gitextract_403vn0ul/

├── .editorconfig
├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .gitlab-ci.yml
├── .htaccess
├── .php-cs-fixer.dist.php
├── App.php
├── README.md
├── SECURITY.md
├── app/
│   ├── Commands/
│   │   └── Index.php
│   ├── Controllers/
│   │   └── Home.php
│   ├── Languages/
│   │   ├── en/
│   │   │   └── home.php
│   │   ├── es/
│   │   │   └── home.php
│   │   └── pt-br/
│   │       └── home.php
│   ├── Models/
│   │   └── .gitkeep
│   └── Views/
│       ├── _layouts/
│       │   └── default.php
│       ├── errors/
│       │   └── 404.php
│       └── home/
│           └── index.php
├── bin/
│   └── console
├── boot/
│   ├── app.php
│   ├── constants.php
│   ├── helpers.php
│   ├── init.php
│   └── routes.php
├── composer.json
├── config/
│   ├── antiCsrf.php
│   ├── autoloader.php
│   ├── cache.php
│   ├── console.php
│   ├── database.php
│   ├── debugger.php
│   ├── exceptionHandler.php
│   ├── language.php
│   ├── locator.php
│   ├── logger.php
│   ├── mailer.php
│   ├── migrator.php
│   ├── request.php
│   ├── response.php
│   ├── router.php
│   ├── session.php
│   ├── validation.php
│   └── view.php
├── docker-compose.yml
├── guide/
│   └── index.rst
├── php-server.ini
├── phpdoc.dist.xml
├── phpmd.xml
├── phpstan.neon.dist
├── phpunit.xml.dist
├── preload.php
├── public/
│   ├── .htaccess
│   ├── index.php
│   └── robots.txt
├── storage/
│   ├── cache/
│   │   └── .gitignore
│   ├── logs/
│   │   └── .gitignore
│   ├── sessions/
│   │   └── .gitignore
│   └── uploads/
│       └── .gitignore
└── tests/
    ├── AppTest.php
    ├── TestCase.php
    ├── app/
    │   ├── Commands/
    │   │   └── IndexTest.php
    │   ├── Controllers/
    │   │   └── HomeTest.php
    │   └── Languages/
    │       └── LanguagesTest.php
    ├── bin/
    │   └── ConsoleTest.php
    ├── boot/
    │   ├── AppTest.php
    │   ├── ConstantsTest.php
    │   ├── HelpersTest.php
    │   ├── InitTest.php
    │   └── RoutesTest.php
    ├── config/
    │   └── ConfigsTest.php
    ├── public/
    │   └── IndexTest.php
    └── support/
        ├── Helpers/
        │   └── tests.php
        ├── Models/
        │   └── UsersModel.php
        └── routes.php
Download .txt
SYMBOL INDEX (75 symbols across 18 files)

FILE: App.php
  class App (line 15) | class App extends Framework\MVC\App

FILE: app/Commands/Index.php
  class Index (line 17) | class Index extends \Framework\CLI\Commands\Index
    method showHeader (line 19) | protected function showHeader() : void

FILE: app/Controllers/Home.php
  class Home (line 18) | final class Home extends Controller
    method index (line 23) | #[Route('GET', '/', name: 'home.index')]

FILE: boot/helpers.php
  function helpers (line 27) | function helpers(array | string $helper) : array
  function esc (line 51) | #[Pure]
  function view (line 69) | function view(string $path, array $variables = [], string $instance = 'd...
  function current_url (line 79) | function current_url() : string
  function current_route (line 89) | function current_route() : Route
  function route_url (line 103) | function route_url(string $name, array $pathArgs = [], array $originArgs...
  function lang (line 128) | function lang(string $line, array $args = [], ?string $locale = null) : ...
  function session (line 138) | function session() : Session
  function old (line 156) | function old(?string $key, bool $escape = true) : mixed
  function has_old (line 178) | function has_old(?string $key = null) : bool
  function csrf_input (line 192) | function csrf_input(string $instance = 'default') : string
  function respond_not_found (line 205) | function respond_not_found(array $variables = []) : Response
  function redirect (line 246) | function redirect(string $location, array $data = [], ?int $code = null)...
  function redirect_to (line 273) | function redirect_to(
  function config (line 292) | function config(string $name, string $key = 'default') : mixed
  function model (line 318) | function model(string $class) : Model
  function env (line 331) | function env(string $key, mixed $default = null) : mixed

FILE: tests/AppTest.php
  class AppTest (line 14) | #[RunTestsInSeparateProcesses]
    method setUp (line 17) | protected function setUp() : void
    method testInstance (line 22) | public function testInstance() : void

FILE: tests/TestCase.php
  class TestCase (line 10) | abstract class TestCase extends \Framework\Testing\TestCase

FILE: tests/app/Commands/IndexTest.php
  class IndexTest (line 13) | #[RunTestsInSeparateProcesses]
    method testRun (line 16) | public function testRun() : void

FILE: tests/app/Controllers/HomeTest.php
  class HomeTest (line 14) | #[RunTestsInSeparateProcesses]
    method testIndex (line 17) | public function testIndex() : void

FILE: tests/app/Languages/LanguagesTest.php
  class LanguagesTest (line 13) | final class LanguagesTest extends TestCase
    method testKeys (line 17) | public function testKeys() : void
    method getCodes (line 43) | protected function getCodes() : array
    method getFiles (line 61) | protected function getFiles(string $locale) : array

FILE: tests/bin/ConsoleTest.php
  class ConsoleTest (line 14) | #[RunTestsInSeparateProcesses]
    method testConsole (line 17) | public function testConsole() : void

FILE: tests/boot/AppTest.php
  class AppTest (line 14) | #[RunTestsInSeparateProcesses]
    method testApp (line 17) | public function testApp() : void

FILE: tests/boot/ConstantsTest.php
  class ConstantsTest (line 13) | #[RunTestsInSeparateProcesses]
    method testConstants (line 16) | public function testConstants() : void

FILE: tests/boot/HelpersTest.php
  class HelpersTest (line 18) | #[RunTestsInSeparateProcesses]
    method testHelpers (line 21) | public function testHelpers() : void
    method testEsc (line 34) | public function testEsc() : void
    method testView (line 41) | public function testView() : void
    method testCurrentUrl (line 49) | public function testCurrentUrl() : void
    method testCurrentRoute (line 55) | public function testCurrentRoute() : void
    method testRouteUrl (line 61) | public function testRouteUrl() : void
    method testSession (line 89) | public function testSession() : void
    method testOld (line 94) | public function testOld() : void
    method setOldData (line 110) | protected function setOldData() : void
    method testHasOld (line 122) | public function testHasOld() : void
    method testHasOldWithoutRedirectData (line 134) | public function testHasOldWithoutRedirectData() : void
    method testRedirect (line 142) | public function testRedirect() : void
    method testRedirectTo (line 152) | public function testRedirectTo() : void
    method testCsrfInput (line 193) | public function testCsrfInput() : void
    method testRespondNotFound (line 198) | public function testRespondNotFound() : void
    method testRespondNotFoundWithContentTypeJson (line 207) | public function testRespondNotFoundWithContentTypeJson() : void
    method testRespondNotFoundWithAcceptJson (line 220) | public function testRespondNotFoundWithAcceptJson() : void
    method testRespondNotFoundWithAcceptHtml (line 233) | public function testRespondNotFoundWithAcceptHtml() : void
    method testConfig (line 243) | public function testConfig() : void
    method testModel (line 255) | public function testModel() : void
    method testEnv (line 261) | public function testEnv() : void

FILE: tests/boot/InitTest.php
  class InitTest (line 13) | #[RunTestsInSeparateProcesses]
    method setUp (line 16) | protected function setUp() : void
    method testErrorReporting (line 23) | public function testErrorReporting() : void
    method testDisplayErrors (line 35) | public function testDisplayErrors() : void
    method testDefaultTimezone (line 44) | public function testDefaultTimezone() : void

FILE: tests/boot/RoutesTest.php
  class RoutesTest (line 14) | #[RunTestsInSeparateProcesses]
    method testNotFound (line 17) | public function testNotFound() : void

FILE: tests/config/ConfigsTest.php
  class ConfigsTest (line 12) | final class ConfigsTest extends TestCase
    method testConfigs (line 14) | public function testConfigs() : void

FILE: tests/public/IndexTest.php
  class IndexTest (line 14) | #[RunTestsInSeparateProcesses]
    method testIndex (line 17) | public function testIndex() : void

FILE: tests/support/Models/UsersModel.php
  class UsersModel (line 12) | class UsersModel extends Model
    method foo (line 14) | public function foo() : string
Condensed preview — 74 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (85K chars).
[
  {
    "path": ".editorconfig",
    "chars": 219,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".github/workflows/tests.yml",
    "chars": 1720,
    "preview": "name: Tests\n\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: '0 4 * * *'\n\njobs:\n  tests:\n    runs-on: ubuntu-24.04\n "
  },
  {
    "path": ".gitignore",
    "chars": 173,
    "preview": ".config.php\n.env\n.env.php\n.idea\n.php-cs-fixer.cache\n.phpunit.cache\n.phpunit.result.cache\n.vscode\nbuild\ncomposer.lock\ncom"
  },
  {
    "path": ".gitlab-ci.yml",
    "chars": 1321,
    "preview": "---\nimage: registry.gitlab.com/aplus-framework/images/lempa:4\n\ninclude:\n  - template: Security/SAST.gitlab-ci.yml\n\nvaria"
  },
  {
    "path": ".htaccess",
    "chars": 186,
    "preview": "Options All -Indexes\n\n<IfModule mod_rewrite.c>\n    RewriteEngine On\n\n    # Redirect to the public directory\n    RewriteR"
  },
  {
    "path": ".php-cs-fixer.dist.php",
    "chars": 449,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "App.php",
    "chars": 372,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "README.md",
    "chars": 5770,
    "preview": "<a href=\"https://github.com/aplus-framework/app\"><img src=\"https://raw.githubusercontent.com/aplus-framework/app/master/"
  },
  {
    "path": "SECURITY.md",
    "chars": 171,
    "preview": "# Security Policy\n\nThe **latest** and **LTS** versions of this library receive security fixes.\n\nIf you find any vulnerab"
  },
  {
    "path": "app/Commands/Index.php",
    "chars": 837,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "app/Controllers/Home.php",
    "chars": 572,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "app/Languages/en/home.php",
    "chars": 275,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "app/Languages/es/home.php",
    "chars": 283,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "app/Languages/pt-br/home.php",
    "chars": 275,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "app/Models/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/Views/_layouts/default.php",
    "chars": 1126,
    "preview": "<?php\n/**\n * @var string|null $description\n * @var string|null $title\n * @var Framework\\MVC\\View $view\n */\n?>\n<!doctype "
  },
  {
    "path": "app/Views/errors/404.php",
    "chars": 313,
    "preview": "<?php\n/**\n * @var string $message\n * @var string $title\n * @var Framework\\MVC\\View $view\n */\n$view->extends('default');\n"
  },
  {
    "path": "app/Views/home/index.php",
    "chars": 163,
    "preview": "<?php\n/**\n * @var Framework\\MVC\\View $view\n */\n$view->extends('default', 'contents');\n?>\n<h1><?= lang('home.title') ?></"
  },
  {
    "path": "bin/console",
    "chars": 249,
    "preview": "#!/usr/bin/env php\n<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, p"
  },
  {
    "path": "boot/app.php",
    "chars": 501,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "boot/constants.php",
    "chars": 1362,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "boot/helpers.php",
    "chars": 8307,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "boot/init.php",
    "chars": 713,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "boot/routes.php",
    "chars": 546,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "composer.json",
    "chars": 1917,
    "preview": "{\n    \"name\": \"aplus/app\",\n    \"description\": \"Aplus Framework App Project\",\n    \"license\": \"MIT\",\n    \"type\": \"project\""
  },
  {
    "path": "config/antiCsrf.php",
    "chars": 601,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/autoloader.php",
    "chars": 521,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/cache.php",
    "chars": 900,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/console.php",
    "chars": 645,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/database.php",
    "chars": 859,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/debugger.php",
    "chars": 534,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/exceptionHandler.php",
    "chars": 885,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/language.php",
    "chars": 826,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/locator.php",
    "chars": 404,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/logger.php",
    "chars": 673,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/mailer.php",
    "chars": 914,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/migrator.php",
    "chars": 513,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/request.php",
    "chars": 556,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/response.php",
    "chars": 679,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/router.php",
    "chars": 606,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/session.php",
    "chars": 1124,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/validation.php",
    "chars": 585,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "config/view.php",
    "chars": 592,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "docker-compose.yml",
    "chars": 628,
    "preview": "version: \"3.0\"\nservices:\n  lempa:\n    image: registry.gitlab.com/aplus-framework/images/lempa:4\n    container_name: lemp"
  },
  {
    "path": "guide/index.rst",
    "chars": 11343,
    "preview": "App\n===\n\n.. image:: image.png\n    :alt: Aplus Framework App Project\n\nAplus Framework App Project.\n\n- `Installation`_\n- `"
  },
  {
    "path": "php-server.ini",
    "chars": 689,
    "preview": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n; php-server configuration file - https://"
  },
  {
    "path": "phpdoc.dist.xml",
    "chars": 1082,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<phpdocumentor\n    configVersion=\"3\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSc"
  },
  {
    "path": "phpmd.xml",
    "chars": 540,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"Mess Detector\">\n    <!-- cleancode -->\n    <rule ref=\"rulesets/cleancode.xml\">\n    "
  },
  {
    "path": "phpstan.neon.dist",
    "chars": 359,
    "preview": "parameters:\n    level: 7\n    paths:\n        - app\n        - boot\n        - config\n        - public\n        - tests\n    d"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 1158,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" backupGlobals=\"fal"
  },
  {
    "path": "preload.php",
    "chars": 509,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "public/.htaccess",
    "chars": 492,
    "preview": "Options All -Indexes\n\n<IfModule mod_rewrite.c>\n    RewriteEngine On\n\n    # Redirect non-www to HTTPS www\n    #RewriteCon"
  },
  {
    "path": "public/index.php",
    "chars": 231,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "public/robots.txt",
    "chars": 24,
    "preview": "User-agent: *\nDisallow:\n"
  },
  {
    "path": "storage/cache/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "storage/logs/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "storage/sessions/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "storage/uploads/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "tests/AppTest.php",
    "chars": 613,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/TestCase.php",
    "chars": 346,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/app/Commands/IndexTest.php",
    "chars": 499,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/app/Controllers/HomeTest.php",
    "chars": 667,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/app/Languages/LanguagesTest.php",
    "chars": 2012,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/bin/ConsoleTest.php",
    "chars": 778,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/boot/AppTest.php",
    "chars": 573,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/boot/ConstantsTest.php",
    "chars": 989,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/boot/HelpersTest.php",
    "chars": 8776,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/boot/InitTest.php",
    "chars": 1299,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/boot/RoutesTest.php",
    "chars": 676,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/config/ConfigsTest.php",
    "chars": 555,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/public/IndexTest.php",
    "chars": 947,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/support/Helpers/tests.php",
    "chars": 180,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  },
  {
    "path": "tests/support/Models/UsersModel.php",
    "chars": 368,
    "preview": "<?php declare(strict_types=1);\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license informat"
  },
  {
    "path": "tests/support/routes.php",
    "chars": 462,
    "preview": "<?php\n/*\n * This file is part of App Project.\n *\n * For the full copyright and license information, please view the LICE"
  }
]

About this extraction

This page contains the full source code of the aplus-framework/app GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 74 files (75.3 KB), approximately 20.6k tokens, and a symbol index with 75 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!