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 RewriteEngine On # Redirect to the public directory RewriteRule ^$ public/ [L] RewriteRule (.*) public/$1 [L] ================================================ FILE: .php-cs-fixer.dist.php ================================================ setDefaultHeaderComment( 'App Project', '' // [fullname] [] )->setFinder( Finder::create()->in(__DIR__)->exclude('storage') ); ================================================ FILE: App.php ================================================ Aplus Framework App Project # 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 ================================================ 'The app is running! Yep!', 'title' => 'Aplus Framework', ]; ================================================ FILE: app/Languages/es/home.php ================================================ '¡La app se está ejecutando! ¡Sí!', 'title' => 'Aplus Framework', ]; ================================================ FILE: app/Languages/pt-br/home.php ================================================ 'O app está rodando! Sim!', 'title' => 'Aplus Framework', ]; ================================================ FILE: app/Models/.gitkeep ================================================ ================================================ FILE: app/Views/_layouts/default.php ================================================ <?= isset($title) ? esc($title) : 'Aplus Framework' ?> renderBlock('contents') ?> ================================================ FILE: app/Views/errors/404.php ================================================ extends('default'); $view->block('contents'); ?>

Go to homepage

endBlock(); ================================================ FILE: app/Views/home/index.php ================================================ extends('default', 'contents'); ?>

================================================ FILE: bin/console ================================================ #!/usr/bin/env php runCli(); ================================================ FILE: boot/app.php ================================================ |string $helper A list of helper names as array * or a helper name as string * * @return array 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 $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 $pathArgs Route path arguments * @param array $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 $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 $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 $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|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 $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 $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 ================================================ 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 ================================================ [ '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 ================================================ [ 'register' => true, 'extensions' => '.php', 'namespaces' => [ 'App' => APP_DIR, ], 'classes' => [], ], ]; ================================================ FILE: config/cache.php ================================================ [ '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 ================================================ [ '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 ================================================ [ '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 ================================================ [ 'debugbar_view' => null, 'options' => [ 'color' => 'magenta', 'icon_path' => null, 'info_contents' => null, ], ], ]; ================================================ FILE: config/exceptionHandler.php ================================================ [ '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 ================================================ [ '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 ================================================ [ 'autoloader_instance' => 'default', ], ]; ================================================ FILE: config/logger.php ================================================ [ '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 ================================================ [ '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 ================================================ [ 'directories' => [ APP_DIR . 'Migrations', ], 'table' => 'Migrations', 'database_instance' => 'default', ], ]; ================================================ FILE: config/request.php ================================================ [ 'allowed_hosts' => env('request.default.allowed_hosts', []), 'force_https' => env('request.default.force_https', false), 'server_vars' => [], 'json_flags' => null, ], ]; ================================================ FILE: config/response.php ================================================ [ '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 ================================================ [ 'files' => [ BOOT_DIR . 'routes.php', ], 'auto_options' => false, 'auto_methods' => false, 'placeholders' => [], 'response_instance' => 'default', 'language_instance' => 'default', ], ]; ================================================ FILE: config/session.php ================================================ [ '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 ================================================ [ 'validators' => [ Validator::class, FilesValidator::class, ], 'language_instance' => 'default', ], ]; ================================================ FILE: config/view.php ================================================ [ '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 `_ 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 `_ and `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 `_. For more details see the `Config `_ and `MVC `_ 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 `_. 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 Options Indexes FollowSymLinks AllowOverride All Require all granted ServerName domain.tld SetEnv ENVIRONMENT production DocumentRoot /var/www/domain.tld/public 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 `_. 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 ================================================ Aplus Framework App Project build/docs build/docs-cache latest app extra App.php application true guide guide