diff --git a/.env.example b/.env.example
index 8c2f682c..31caf0f2 100644
--- a/.env.example
+++ b/.env.example
@@ -1,12 +1,12 @@
-APP_ENV=development
+APP_ENV=local
APP_NAME=TinyMVC
APP_URL=http://127.0.0.1:8080/
APP_LANG=en
DB_DRIVER=mysql
+DB_NAME=tinymvc
DB_HOST=127.0.0.1
DB_PORT=3306
-DB_NAME=tinymvc
DB_USERNAME=
DB_PASSWORD=
@@ -16,4 +16,4 @@ MAILER_PORT=1025
MAILER_USERNAME=
MAILER_PASSWORD=
-ENCRYPTION_KEY=6a6e690053b3f816a6c6b22634e44624beaf28ce2fb25ec9631b1b1fce25
\ No newline at end of file
+ENCRYPTION_KEY=
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 96365ddb..e31d3f1e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,12 @@
SHELL := /bin/bash
-.PHONY: tests
+.PHONY: tests, docker
tests:
php console db:delete
php console db:create
php console migrations:run
- php console tests:run
\ No newline at end of file
+ php console tests:run
+
+docker:
+ docker-compose up --build
\ No newline at end of file
diff --git a/app/Database/Seeds/Seeder.php b/app/Database/Seeds/Seeder.php
index 1f225eb4..f150143d 100644
--- a/app/Database/Seeds/Seeder.php
+++ b/app/Database/Seeds/Seeder.php
@@ -8,6 +8,7 @@
namespace App\Database\Seeds;
+use App\Database\Factories\UserFactory;
use App\Database\Models\User;
/**
@@ -17,6 +18,6 @@ class Seeder
{
public static function run()
{
- User::factory(5)->create();
+ User::factory(UserFactory::class, 5)->create();
}
}
diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php
index 077acfdb..753e5126 100644
--- a/app/Http/Controllers/Auth/AuthController.php
+++ b/app/Http/Controllers/Auth/AuthController.php
@@ -26,7 +26,9 @@ class AuthController
{
public function login(Request $request, Response $response)
{
- if (!Auth::check($request)) $response->view('auth.login');
+ if (!Auth::check($request)) {
+ $response->view('auth.login');
+ }
$uri = !Session::has('intended') ? Auth::HOME : Session::pull('intended');
$response->redirect()->to($uri)->go();
@@ -34,7 +36,9 @@ public function login(Request $request, Response $response)
public function signup(Request $request, Response $response)
{
- if (!Auth::check($request)) $response->view('auth.signup');
+ if (!Auth::check($request)) {
+ $response->view('auth.signup');
+ }
$uri = !Session::has('intended') ? Auth::HOME : Session::pull('intended');
$response->redirect()->to($uri)->go();
diff --git a/app/Http/Validators/AuthRequest.php b/app/Http/Validators/AuthRequest.php
index f03bc385..9c76d256 100644
--- a/app/Http/Validators/AuthRequest.php
+++ b/app/Http/Validators/AuthRequest.php
@@ -8,9 +8,9 @@
namespace App\Http\Validators;
-use Core\Http\Validator\GUMPValidator as Validator;
+use Core\Http\Validator\GUMPValidator;
-class AuthRequest extends Validator
+class AuthRequest extends GUMPValidator
{
/**
* Validation rules
diff --git a/app/Http/Validators/RegisterUser.php b/app/Http/Validators/RegisterUser.php
index 82017087..0c09fd4f 100644
--- a/app/Http/Validators/RegisterUser.php
+++ b/app/Http/Validators/RegisterUser.php
@@ -8,10 +8,10 @@
namespace App\Http\Validators;
-use Core\Http\Validator\GUMPValidator as Validator;
+use Core\Http\Validator\GUMPValidator;
use Core\Database\Repository;
-class RegisterUser extends Validator
+class RegisterUser extends GUMPValidator
{
/**
* Validation rules
diff --git a/bootstrap.php b/bootstrap.php
index 1dcc603b..a2002782 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -6,23 +6,20 @@
* @link https://github.com/eliseekn/tinymvc
*/
-use Core\Http\Request;
+use Core\Routing\Route;
use Core\Support\Config;
use Core\Support\Whoops;
use Core\Support\Storage;
/**
- * Define application environnement
+ * Setup application
*/
-//application root path
define('DS', DIRECTORY_SEPARATOR);
define('APP_ROOT', __DIR__ . DS);
-//register whoops error handler
Whoops::register();
-//setup storages
$storage = Storage::path(absolute_path('storage'));
if (!$storage->isDir()) $storage->createDir();
@@ -30,7 +27,6 @@
if (!$storage->path(config('storage.cache'))->isDir()) $storage->createDir();
if (!$storage->path(config('storage.sqlite'))->isDir()) $storage->createDir();
-//errors display
if (config('errors.display') === true) {
ini_set('display_errors', 1);
ini_set('error_reporting', E_ALL);
@@ -38,7 +34,6 @@
ini_set('display_errors', 0);
}
-//errors logging
if (config('errors.log') === true) {
ini_set('log_errors', 1);
ini_set('error_log', Storage::path(config('storage.logs'))->file('tinymvc_' . date('m_d_y') . '.log'));
@@ -46,21 +41,17 @@
ini_set('log_errors', 0);
}
-//handle exceptions
function handleExceptions($e)
{
throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e->getPrevious());
}
-//set exceptions and errors handlers
set_exception_handler('handleExceptions');
-//remove PHP maximum execution time
set_time_limit(0);
-//load .env file
-if (!Storage::path()->isFile('.env') && !empty((new Request())->uri())) {
- throw new Exception('Run "php console app:setup" console command to setup application or copy ".env.example" file to ".env"');
-}
+Route::load();
+
+if (Storage::path()->isFile('.env')) Config::loadEnv();
-Config::loadEnv();
+throw new Exception('Copy ".env.example" file to ".env" then edit or run "php console app:setup" console command to setup application');
diff --git a/composer.json b/composer.json
index 1a488b44..5928e1d2 100644
--- a/composer.json
+++ b/composer.json
@@ -40,7 +40,6 @@
"phpunit/phpunit": "^9.5",
"symfony/var-dumper": "^5.2",
"filp/whoops": "^2.12",
- "fakerphp/faker": "^1.15",
- "brianium/paratest": "^6.3"
+ "fakerphp/faker": "^1.15"
}
}
diff --git a/composer.lock b/composer.lock
index 8f16369b..a664aba1 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "af420ed118b3d703a305e97b6e22244d",
+ "content-hash": "21d8b44028f4f2fa5b989818bbe930dd",
"packages": [
{
"name": "dflydev/dot-access-data",
@@ -1490,100 +1490,6 @@
}
],
"packages-dev": [
- {
- "name": "brianium/paratest",
- "version": "v6.3.1",
- "source": {
- "type": "git",
- "url": "https://github.com/paratestphp/paratest.git",
- "reference": "3d81e35876f6497467310b123583cca6bd4c38f2"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/paratestphp/paratest/zipball/3d81e35876f6497467310b123583cca6bd4c38f2",
- "reference": "3d81e35876f6497467310b123583cca6bd4c38f2",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "ext-pcre": "*",
- "ext-reflection": "*",
- "ext-simplexml": "*",
- "php": "^7.3 || ^8.0",
- "phpunit/php-code-coverage": "^9.2.6",
- "phpunit/php-file-iterator": "^3.0.5",
- "phpunit/php-timer": "^5.0.3",
- "phpunit/phpunit": "^9.5.8",
- "sebastian/environment": "^5.1.3",
- "symfony/console": "^4.4.23 || ^5.3.6",
- "symfony/process": "^4.4.22 || ^5.3.4"
- },
- "require-dev": {
- "doctrine/coding-standard": "^9.0.0",
- "ekino/phpstan-banned-code": "^0.4.0",
- "ergebnis/phpstan-rules": "^0.15.3",
- "ext-posix": "*",
- "infection/infection": "^0.24",
- "phpstan/phpstan": "^0.12.94",
- "phpstan/phpstan-deprecation-rules": "^0.12.6",
- "phpstan/phpstan-phpunit": "^0.12.21",
- "phpstan/phpstan-strict-rules": "^0.12.10",
- "squizlabs/php_codesniffer": "^3.6.0",
- "symfony/filesystem": "^5.3.4",
- "thecodingmachine/phpstan-strict-rules": "^0.12.1",
- "vimeo/psalm": "^4.9.2"
- },
- "bin": [
- "bin/paratest"
- ],
- "type": "library",
- "autoload": {
- "psr-4": {
- "ParaTest\\": [
- "src/"
- ]
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Brian Scaturro",
- "email": "scaturrob@gmail.com",
- "role": "Developer"
- },
- {
- "name": "Filippo Tessarotto",
- "email": "zoeslam@gmail.com",
- "role": "Developer"
- }
- ],
- "description": "Parallel testing for PHP",
- "homepage": "https://github.com/paratestphp/paratest",
- "keywords": [
- "concurrent",
- "parallel",
- "phpunit",
- "testing"
- ],
- "support": {
- "issues": "https://github.com/paratestphp/paratest/issues",
- "source": "https://github.com/paratestphp/paratest/tree/v6.3.1"
- },
- "funding": [
- {
- "url": "https://github.com/sponsors/Slamdunk",
- "type": "github"
- },
- {
- "url": "https://paypal.me/filippotessarotto",
- "type": "paypal"
- }
- ],
- "time": "2021-08-10T07:38:58+00:00"
- },
{
"name": "doctrine/instantiator",
"version": "1.4.0",
@@ -3873,12 +3779,12 @@
}
],
"aliases": [],
- "minimum-stability": "stable",
+ "minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": "^7.2"
+ "php": ">=7.2"
},
"platform-dev": [],
"plugin-api-version": "2.0.0"
diff --git a/config/app.php b/config/app.php
index 4afb2239..0eeb95eb 100644
--- a/config/app.php
+++ b/config/app.php
@@ -11,7 +11,7 @@
*/
return [
- 'env' => env('APP_ENV', 'test'),
+ 'env' => env('APP_ENV', 'local'),
'name' => env('APP_NAME', 'TinyMVC'),
'url' => env('APP_URL', 'http://127.0.0.1:8080/'),
'lang' => env('APP_LANG', 'en'),
diff --git a/console b/console
index 2be6616a..819fe9f0 100644
--- a/console
+++ b/console
@@ -43,6 +43,7 @@ $console->add(new \Core\Console\Make\Test());
$console->add(new \Core\Console\App\Setup());
$console->add(new \Core\Console\App\EncryptionKey());
+$console->add(new \Core\Console\App\Environnement());
$console->add(new \Core\Console\Cache());
$console->add(new \Core\Console\Server());
diff --git a/core/Application.php b/core/Application.php
index 7fbd3c1d..a200e97e 100644
--- a/core/Application.php
+++ b/core/Application.php
@@ -11,7 +11,6 @@
use Core\Http\Request;
use Core\Routing\Router;
use Core\Support\Whoops;
-use Core\Support\Storage;
use Core\Support\Exception;
use Core\Http\Response\Response;
@@ -20,34 +19,22 @@
*/
class Application
{
- private $response;
- private $request;
-
public function __construct()
{
- $this->request = new Request();
- $this->response = new Response();
-
Whoops::register();
-
- $routes = Storage::path(config('storage.routes'))->getFiles();
-
- foreach ($routes as $route) {
- require_once config('storage.routes') . $route;
- }
}
- public function run()
+ public function execute()
{
- try {
- Router::dispatch($this->request, $this->response);
- }
+ $response = new Response();
+
+ try { Router::dispatch(new Request(), $response); }
catch (Exception $e) {
if (config('errors.log')) save_log('Exception: ' . $e);
if (config('errors.display')) die($e);
- $this->response->view(config('errors.views.500'), [], 500);
+ $response->view(config('errors.views.500'), [], 500);
}
}
}
diff --git a/core/Console/App/EncryptionKey.php b/core/Console/App/EncryptionKey.php
index 06f29ce7..e6a7f116 100644
--- a/core/Console/App/EncryptionKey.php
+++ b/core/Console/App/EncryptionKey.php
@@ -27,26 +27,26 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
- $config = [
- 'APP_ENV' => config('app.env') . PHP_EOL,
- 'APP_NAME' => config('app.name') . PHP_EOL,
- 'APP_URL' => config('app.url') . PHP_EOL,
- 'APP_LANG' => config('app.lang') . PHP_EOL . PHP_EOL,
- 'DB_DRIVER' => config('database.driver') . PHP_EOL,
- 'DB_NAME' => config('database.name') . PHP_EOL,
- 'DB_HOST' => config('database.' . config('database.driver') . '.host') . PHP_EOL,
- 'DB_PORT' => config('database.' . config('database.driver') . '.port') . PHP_EOL,
- 'DB_USERNAME' => config('database.' . config('database.driver') . '.username') . PHP_EOL,
- 'DB_PASSWORD' => config('database.' . config('database.driver') . '.password') . PHP_EOL . PHP_EOL,
- 'MAILER_TRANSPORT' => config('mailer.transport') . PHP_EOL,
- 'MAILER_HOST' => config('mailer.' . config('mailer.transport') . '.host') . PHP_EOL,
- 'MAILER_PORT' => config('mailer.' . config('mailer.transport') . '.port') . PHP_EOL,
- 'MAILER_USERNAME' => config('mailer.' . config('mailer.transport') . '.username') . PHP_EOL,
- 'MAILER_PASSWORD' => config('mailer.' . config('mailer.transport') . '.password') . PHP_EOL . PHP_EOL,
+ Config::loadEnv();
+
+ Config::saveEnv([
+ 'APP_ENV' => env('APP_ENV') . PHP_EOL,
+ 'APP_NAME' => env('APP_NAME') . PHP_EOL,
+ 'APP_URL' => env('APP_URL') . PHP_EOL,
+ 'APP_LANG' => env('APP_LANG') . PHP_EOL . PHP_EOL,
+ 'DB_DRIVER' => env('DB_DRIVER') . PHP_EOL,
+ 'DB_NAME' => env('DB_NAME') . PHP_EOL,
+ 'DB_HOST' => env('DB_HOST') . PHP_EOL,
+ 'DB_PORT' => env('DB_PORT') . PHP_EOL,
+ 'DB_USERNAME' => env('DB_USERNAME') . PHP_EOL,
+ 'DB_PASSWORD' => env('DB_PASSWORD') . PHP_EOL . PHP_EOL,
+ 'MAILER_TRANSPORT' => env('MAILER_TRANSPORT') . PHP_EOL,
+ 'MAILER_HOST' => env('MAILER_HOST') . PHP_EOL,
+ 'MAILER_PORT' => env('MAILER_PORT') . PHP_EOL,
+ 'MAILER_USERNAME' => env('MAILER_USERNAME') . PHP_EOL,
+ 'MAILER_PASSWORD' => env('MAILER_PASSWORD') . PHP_EOL . PHP_EOL,
'ENCRYPTION_KEY' => generate_token()
- ];
-
- Config::saveEnv($config);
+ ]);
$output->writeln('Application encryption key has been generated');
diff --git a/core/Console/App/Environnement.php b/core/Console/App/Environnement.php
new file mode 100644
index 00000000..cb1c8f8b
--- /dev/null
+++ b/core/Console/App/Environnement.php
@@ -0,0 +1,58 @@
+setDescription('Define application environnement');
+ $this->addArgument('env', InputArgument::REQUIRED, 'Specify application environnement (test, local or prod');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ Config::loadEnv();
+
+ Config::saveEnv([
+ 'APP_ENV' => $input->getArgument('env') . PHP_EOL,
+ 'APP_ENV' => env('APP_ENV') . PHP_EOL,
+ 'APP_NAME' => env('APP_NAME') . PHP_EOL,
+ 'APP_URL' => env('APP_URL') . PHP_EOL,
+ 'APP_LANG' => env('APP_LANG') . PHP_EOL . PHP_EOL,
+ 'DB_DRIVER' => env('DB_DRIVER') . PHP_EOL,
+ 'DB_NAME' => env('DB_NAME') . PHP_EOL,
+ 'DB_HOST' => env('DB_HOST') . PHP_EOL,
+ 'DB_PORT' => env('DB_PORT') . PHP_EOL,
+ 'DB_USERNAME' => env('DB_USERNAME') . PHP_EOL,
+ 'DB_PASSWORD' => env('DB_PASSWORD') . PHP_EOL . PHP_EOL,
+ 'MAILER_TRANSPORT' => env('MAILER_TRANSPORT') . PHP_EOL,
+ 'MAILER_HOST' => env('MAILER_HOST') . PHP_EOL,
+ 'MAILER_PORT' => env('MAILER_PORT') . PHP_EOL,
+ 'MAILER_USERNAME' => env('MAILER_USERNAME') . PHP_EOL,
+ 'MAILER_PASSWORD' => env('MAILER_PASSWORD') . PHP_EOL . PHP_EOL,
+ 'ENCRYPTION_KEY' => env('ENCRYPTION_KEY')
+ ]);
+
+ $output->writeln('Application environnement has been defined');
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/core/Console/App/Setup.php b/core/Console/App/Setup.php
index c1d542eb..b160c003 100644
--- a/core/Console/App/Setup.php
+++ b/core/Console/App/Setup.php
@@ -35,63 +35,52 @@ protected function execute(InputInterface $input, OutputInterface $output)
while (!$finish_setup) {
$output->write('Application name (default: TinyMVC): ');
- $config['APP_NAME'] = fgets(STDIN);
- if (strlen($config['APP_NAME']) <= 1) $config['APP_NAME'] = 'TinyMVC' . PHP_EOL;
+ $config['APP_NAME'] = $this->getInput('TinyMVC');
$output->write('Application url (default: http://127.0.0.1:8080/): ');
- $app_url = fgets(STDIN);
-
- if (strlen($app_url) <= 1) $app_url = 'http://127.0.0.1:8080/';
-
- $app_url = rtrim($app_url, PHP_EOL);
+ $app_url = trim($this->getInput('http://127.0.0.1:8080/'));
- if (!empty($app_url) && $app_url[-1] !== '/') $app_url = $app_url . '/';
+ if (!empty($app_url) && $app_url[-1] !== '/') {
+ $app_url = $app_url . '/';
+ }
$config['APP_URL'] = $app_url . PHP_EOL;
$output->write('Application language (default: en): ');
- $config['APP_LANG'] = fgets(STDIN);
- if (strlen($config['APP_LANG']) <= 1) $config['APP_LANG'] = 'en' . PHP_EOL . PHP_EOL;
+ $config['APP_LANG'] = $this->getInput('en') . PHP_EOL;
$output->write('Database driver [mysql (default), sqlite]: ');
- $config['DB_DRIVER'] = fgets(STDIN);
- if (strlen($config['DB_DRIVER']) <= 1 || !in_array($config['DB_DRIVER'], ['mysql', 'sqlite'])) $config['DB_DRIVER'] = 'mysql' . PHP_EOL;
+ $config['DB_DRIVER'] = $this->getInput('mysql', ['mysql', 'sqlite']);
$output->write('Database host (default: 127.0.0.1): ');
- $config['DB_HOST'] = fgets(STDIN);
- if (strlen($config['DB_HOST']) <= 1) $config['DB_HOST'] = '127.0.0.1' . PHP_EOL;
+ $config['DB_HOST'] = $this->getInput('127.0.0.1');
$output->write('Database port (default: 3306): ');
- $config['DB_PORT'] = fgets(STDIN);
- if (strlen($config['DB_PORT']) <= 1) $config['DB_PORT'] = '3306' . PHP_EOL;
+ $config['DB_PORT'] = $this->getInput('3306');
$output->write('Database name (default: tinymvc): ');
- $config['DB_NAME'] = fgets(STDIN);
- if (strlen($config['DB_NAME']) <= 1) $config['DB_NAME'] = 'tinymvc' . PHP_EOL;
+ $config['DB_NAME'] = $this->getInput('tinymvc');
$output->write('Database username: ');
- $config['DB_USERNAME'] = fgets(STDIN);
+ $config['DB_USERNAME'] = $this->getInput('');
$output->write('Database password: ');
- $config['DB_PASSWORD'] = fgets(STDIN) . PHP_EOL;
+ $config['DB_PASSWORD'] = $this->getInput('') . PHP_EOL;
$output->write('Mailer transport [smtp (default), sendmail]: ');
- $config['MAILER_TRANSPORT'] = fgets(STDIN);
- if (strlen($config['MAILER_TRANSPORT']) <= 1 || !in_array($config['MAILER_TRANSPORT'], ['smtp', 'sendmail'])) $config['MAILER_TRANSPORT'] = 'smtp' . PHP_EOL;
+ $config['MAILER_TRANSPORT'] = $this->getInput('smtp', ['smtp', 'sendmail']);
$output->write('Mailer host (default: 127.0.0.1): ');
- $config['MAILER_HOST'] = fgets(STDIN);
- if (strlen($config['MAILER_HOST']) <= 1) $config['MAILER_HOST'] = '127.0.0.1' . PHP_EOL;
+ $config['MAILER_HOST'] = $this->getInput('127.0.0.1');
$output->write('Mailer port (default: 1025): ');
- $config['MAILER_PORT'] = fgets(STDIN);
- if (strlen($config['MAILER_PORT']) <= 1) $config['MAILER_PORT'] = '1025' . PHP_EOL;
+ $config['MAILER_PORT'] = $this->getInput('1025');
$output->write('Mailer username: ');
- $config['MAILER_USERNAME'] = fgets(STDIN);
+ $config['MAILER_USERNAME'] = $this->getInput('');
$output->write('Mailer password: ');
- $config['MAILER_PASSWORD'] = fgets(STDIN) . PHP_EOL;
+ $config['MAILER_PASSWORD'] = $this->getInput('') . PHP_EOL;
$finish_setup = true;
}
@@ -104,8 +93,25 @@ protected function execute(InputInterface $input, OutputInterface $output)
Storage::path(config('storage.lang'))->copyFile('en.php', config('app.lang') . '.php');
}
- $output->writeln('Application has been setted up. You need to restart server to make changes applies.');
+ $output->writeln('Application has been setted up. You need to restart server to apply changes.');
return Command::SUCCESS;
}
+
+ private function getInput(string $default, ?array $expected = null)
+ {
+ $input = fgets(STDIN);
+
+ if (is_null($expected)) {
+ if ($input === "\n") {
+ return $default . PHP_EOL;
+ }
+ } else {
+ if (!in_array(trim($input), $expected)) {
+ return $default . PHP_EOL;
+ }
+ }
+
+ return $input;
+ }
}
diff --git a/core/Console/Make/Make.php b/core/Console/Make/Make.php
index d80af0df..3009a100 100644
--- a/core/Console/Make/Make.php
+++ b/core/Console/Make/Make.php
@@ -52,28 +52,23 @@ public static function generateClass(string $base_name, string $suffix = '', boo
{
$name = ucfirst(strtolower($base_name));
- if (!$singular) {
- $name = self::fixPluralTypo($name);
- }
-
- if ($force_singlular) {
- $name = self::fixPluralTypo($name, true);
- }
+ if (!$singular) $name = self::fixPluralTypo($name);
+ if ($force_singlular) $name = self::fixPluralTypo($name, true);
if (strpos($name, '_')) {
- list($f, $s) = explode('_', $name);
- $name = ucfirst($f) . ucfirst($s);
- }
+ $words = explode('_', $name);
+ $name = '';
- if ($suffix === 'migration') {
- $suffix = 'table';
+ foreach ($words as $word) {
+ $name .= ucfirst($word);
+ }
}
+ if ($suffix === 'migration') $suffix = 'table';
+
$class = $name . ucfirst($suffix);
- if (strpos($class, 'Table')) {
- $class .= date('_YmdHis');
- }
+ if (strpos($class, 'Table')) $class .= date('_YmdHis');
return [lcfirst($name), $class];
}
diff --git a/core/Console/Tests.php b/core/Console/Tests.php
index 9d49a805..53e1487f 100644
--- a/core/Console/Tests.php
+++ b/core/Console/Tests.php
@@ -35,7 +35,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$server_process->setTimeout(null);
$server_process->start();
- $process = new Process(['php', 'vendor/bin/paratest', '-p' . config('testing.process')]);
+ $process = new Process(['php', 'vendor/bin/phpunit']);
$process->setTimeout(null);
$process->start();
diff --git a/core/Database/Model.php b/core/Database/Model.php
index 3a6681a5..59f7b08d 100644
--- a/core/Database/Model.php
+++ b/core/Database/Model.php
@@ -34,13 +34,9 @@ public function __construct(string $table, $data = null)
*
* @return \Core\Database\Factory
*/
- public static function factory(int $count = 1)
+ public static function factory(string $factory, int $count = 1)
{
- foreach (Storage::path(config('storage.factories'))->getFiles() as $file) {
- $factory = '\App\Database\Factories\\' . get_file_name($file);
-
- return new $factory($count);
- }
+ return new $factory($count);
}
public static function findBy(string $column, $operator = null, $value = null)
@@ -235,6 +231,7 @@ public function decrement(string $column, $value = null)
{
if (is_null($value)) {
$this->{$column}--;
+ return;
}
$this->{$column} = $this->{$column} - $value;
@@ -250,7 +247,7 @@ public function toArray(string ...$attributes)
$d = [];
foreach ($attributes as $attribute) {
- if ($this->has($attribute)) {
+ if (isset($attribute)) {
$d = array_merge($d, [
$attribute => $this->{$attribute}
]);
diff --git a/core/Http/Client/ClientInterface.php b/core/Http/Client/ClientInterface.php
new file mode 100644
index 00000000..ae8903bd
--- /dev/null
+++ b/core/Http/Client/ClientInterface.php
@@ -0,0 +1,14 @@
+ $url) {
+ foreach ($url as $key => $url) {
$curl_array[$key] = curl_init();
$curl = $curl_array[$key];
@@ -115,34 +114,34 @@ function ($curl, $header) use (&$response_headers, $key) {
return new self();
}
- public static function get($urls, array $headers = []): self
+ public static function get($url, array $headers = []): self
{
- return self::send('GET', $urls, [], $headers);
+ return self::send('GET', $url, [], $headers);
}
- public static function post($urls, array $data = [], array $headers = [], bool $json = false): self
+ public static function post($url, array $data = [], array $headers = [], bool $json = false): self
{
- return self::send('POST', $urls, $data, $headers, $json);
+ return self::send('POST', $url, $data, $headers, $json);
}
- public static function put($urls, array $data = [], array $headers = [], bool $json = false): self
+ public static function put($url, array $data = [], array $headers = [], bool $json = false): self
{
- return self::send('PUT', $urls, $data, $headers, $json);
+ return self::send('PUT', $url, $data, $headers, $json);
}
- public static function delete($urls, array $headers = []): self
+ public static function delete($url, array $headers = []): self
{
- return self::send('DELETE', $urls, [], $headers);
+ return self::send('DELETE', $url, [], $headers);
}
- public static function options($urls, array $data = [], array $headers = [], bool $json = false): self
+ public static function options($url, array $data = [], array $headers = [], bool $json = false): self
{
- return self::send('OPTIONS', $urls, $data, $headers, $json);
+ return self::send('OPTIONS', $url, $data, $headers, $json);
}
- public static function patch($urls, array $data = [], array $headers = [], bool $json = false): self
+ public static function patch($url, array $data = [], array $headers = [], bool $json = false): self
{
- return self::send('PATCH', $urls, $data, $headers, $json);
+ return self::send('PATCH', $url, $data, $headers, $json);
}
public function getHeaders()
diff --git a/core/Http/Request.php b/core/Http/Request.php
index 0d54b445..da6a7f46 100644
--- a/core/Http/Request.php
+++ b/core/Http/Request.php
@@ -48,6 +48,8 @@ public function queries(?string $key = null, $default = null)
public function inputs(?string $key = null, $default = null)
{
+ $_POST = array_merge($_POST, $this->rawData());
+
$input = is_null($key) ? $_POST : ($_POST[$key] ?? '');
return empty($input) || is_null($input) ? $default : $input;
}
@@ -60,7 +62,10 @@ public function files(?string $key = null, $default = null)
public function rawData()
{
- return file_get_contents('php://input');
+ $data = [];
+ parse_raw_http_request($data);
+
+ return $data;
}
private function getSingleFile(string $input, array $allowed_extensions = [])
diff --git a/core/Http/Validator/GUMPValidator.php b/core/Http/Validator/GUMPValidator.php
index 79828f74..18094363 100644
--- a/core/Http/Validator/GUMPValidator.php
+++ b/core/Http/Validator/GUMPValidator.php
@@ -17,15 +17,12 @@
*/
class GUMPValidator implements ValidatorInterface
{
- protected static $rules = [];
- protected static $messages = [];
+ protected static array $rules = [];
+ protected static array $messages = [];
- /**
- * @var bool|array
- */
+ /** @var bool|array */
protected static $errors;
-
- protected static $inputs = [];
+ protected static array $inputs = [];
public static function addRule(string $rule, callable $callback, string $error_message)
{
diff --git a/core/Routing/Route.php b/core/Routing/Route.php
index 4664cf1e..cbc39b27 100644
--- a/core/Routing/Route.php
+++ b/core/Routing/Route.php
@@ -8,6 +8,7 @@
namespace Core\Routing;
+use Core\Support\Storage;
use Core\Http\Response\Response;
/**
@@ -108,21 +109,6 @@ public static function groupPrefix(string $prefix, $callback): self
return new self();
}
- public static function group(array $groups, $callback): self
- {
- $route = new self();
-
- if (array_key_exists('prefix', $groups)) {
- $route->groupPrefix($groups['prefix'], $callback);
- }
-
- if (array_key_exists('middlewares', $groups)) {
- $route->groupMiddlewares($groups['middlewares'], $callback);
- }
-
- return $route;
- }
-
public function register()
{
if (empty(static::$tmp_routes)) return;
@@ -175,4 +161,13 @@ private static function update(string $old, string $new)
return $new_array;
}
+
+ public static function load()
+ {
+ $routes = Storage::path(config('storage.routes'))->getFiles();
+
+ foreach ($routes as $route) {
+ require_once config('storage.routes') . $route;
+ }
+ }
}
diff --git a/core/Routing/Router.php b/core/Routing/Router.php
index dbea4beb..6e6daa0d 100644
--- a/core/Routing/Router.php
+++ b/core/Routing/Router.php
@@ -28,6 +28,8 @@ private static function match(Request $request, string $method, string $route, &
) {
return false;
}
+
+ array_shift($params);
return true;
}
@@ -88,8 +90,6 @@ public static function dispatch(Request $request, Response $response)
$request->method($request_method);
if (self::match($request, $method, $route, $params)) {
- array_shift($params);
-
if (!isset($options['handler'])) {
throw new Exception("No handler defined for route {$route}");
}
diff --git a/core/Support/Auth.php b/core/Support/Auth.php
index d1580edb..84801c06 100644
--- a/core/Support/Auth.php
+++ b/core/Support/Auth.php
@@ -102,13 +102,11 @@ public static function check(Request $request)
$result = Session::has('user');
if (!$result) {
- if (empty($request->getHttpAuth())) {
- return false;
- }
+ if (empty($request->getHttpAuth())) return false;
list($method, $token) = $request->getHttpAuth();
- $result = trim($method) === 'Bearer' && self::checkToken(Encryption::decrypt($token), $user);
+ $result = trim($method) === 'Bearer' && self::checkToken(Encryption::decrypt($token), $user);
}
return $result;
diff --git a/core/Support/Helpers.php b/core/Support/Helpers.php
index 7ade9045..a2648b29 100644
--- a/core/Support/Helpers.php
+++ b/core/Support/Helpers.php
@@ -496,3 +496,62 @@ function class_uses_recursive($class)
return array_unique($results);
}
}
+
+if (!function_exists('parse_raw_http_request')) {
+ /**
+ * Parse raw HTTP request data
+ *
+ * Pass in $a_data as an array. This is done by reference to avoid copying
+ * the data around too much.
+ *
+ * Any files found in the request will be added by their field name to the
+ * $data['files'] array.
+ *
+ * @ref: http://www.chlab.ch/blog/archives/webdevelopment/manually-parse-raw-http-data-php
+ *
+ * @param array Empty array to fill with data
+ * @return array Associative array of request data
+ */
+function parse_raw_http_request(array &$a_data)
+{
+ // read incoming data
+ $input = file_get_contents('php://input');
+
+ // grab multipart boundary from content type header
+ preg_match('/boundary=(.*)$/', $_SERVER['CONTENT_TYPE'], $matches);
+
+ // content type is probably regular form-encoded
+ if (!count($matches)) {
+ // we expect regular puts to containt a query string containing data
+ parse_str(urldecode($input), $a_data);
+ return $a_data;
+ }
+
+ $boundary = $matches[1];
+
+ // split content by boundary and get rid of last -- element
+ $a_blocks = preg_split("/-+$boundary/", $input);
+ array_pop($a_blocks);
+
+ // loop data blocks
+ foreach ($a_blocks as $id => $block) {
+ if (empty($block))
+ continue;
+
+ // you'll have to var_dump $block to understand this and maybe replace \n or \r with a visibile char
+
+ // parse uploaded files
+ if (strpos($block, 'application/octet-stream') !== FALSE) {
+ // match "name", then everything after "stream" (optional) except for prepending newlines
+ preg_match("/name=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/s", $block, $matches);
+ $a_data['files'][$matches[1]] = $matches[2];
+ }
+ // parse all other fields
+ else {
+ // match "name" and optional value in between newline sequences
+ preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $block, $matches);
+ $a_data[$matches[1]] = $matches[2];
+ }
+ }
+}
+}
\ No newline at end of file
diff --git a/core/Testing/ApplicationTestCase.php b/core/Testing/ApplicationTestCase.php
index 2e5496ea..29c68f74 100644
--- a/core/Testing/ApplicationTestCase.php
+++ b/core/Testing/ApplicationTestCase.php
@@ -8,38 +8,37 @@
namespace Core\Testing;
-use Core\Http\Client;
-use Core\Database\Repository;
use Core\Support\Auth;
+use Core\Database\Repository;
use PHPUnit\Framework\TestCase;
+use Core\Http\Client\Curl as Client;
/**
* Manage application tests
*/
class ApplicationTestCase extends TestCase
{
- /**
- * @var \Core\Http\Client
- */
- private $client;
-
- private $headers = [];
- private $token = '';
+ private Client $client;
+ private array $headers;
+ private string $token;
protected function setUp(): void
{
- parent::setUp();
+ $uses = array_flip(class_uses_recursive(static::class));
+
+ if (isset($uses[\Core\Testing\Traits\LoadFaker::class])) {
+ $this->loadFaker();
+ }
+ }
+ protected function tearDown(): void
+ {
$uses = array_flip(class_uses_recursive(static::class));
if (isset($uses[\Core\Testing\Traits\RefreshDatabase::class])) {
$this->refreshDatabase();
}
- if (isset($uses[\Core\Testing\Traits\LoadFaker::class])) {
- $this->loadFaker();
- }
-
$this->token = '';
$this->headers = [];
}
diff --git a/core/Testing/Traits/LoadFaker.php b/core/Testing/Concerns/LoadFaker.php
similarity index 92%
rename from core/Testing/Traits/LoadFaker.php
rename to core/Testing/Concerns/LoadFaker.php
index 56b2db33..1252caf4 100644
--- a/core/Testing/Traits/LoadFaker.php
+++ b/core/Testing/Concerns/LoadFaker.php
@@ -6,7 +6,7 @@
* @link https://github.com/eliseekn/tinymvc
*/
-namespace Core\Testing\Traits;
+namespace Core\Testing\Concerns;
use Faker\Factory;
diff --git a/core/Testing/Traits/RefreshDatabase.php b/core/Testing/Concerns/RefreshDatabase.php
similarity index 93%
rename from core/Testing/Traits/RefreshDatabase.php
rename to core/Testing/Concerns/RefreshDatabase.php
index f30cd593..57ed3c74 100644
--- a/core/Testing/Traits/RefreshDatabase.php
+++ b/core/Testing/Concerns/RefreshDatabase.php
@@ -6,7 +6,7 @@
* @link https://github.com/eliseekn/tinymvc
*/
-namespace Core\Testing\Traits;
+namespace Core\Testing\Concerns;
use Symfony\Component\Process\Process;
diff --git a/index.php b/index.php
index 500337a1..23f7c004 100644
--- a/index.php
+++ b/index.php
@@ -16,4 +16,4 @@
require_once 'bootstrap.php';
$app = new Application();
-$app->run();
+$app->execute();
diff --git a/resources/stubs/Validator.stub b/resources/stubs/Validator.stub
index 5d3fa550..2533af0b 100644
--- a/resources/stubs/Validator.stub
+++ b/resources/stubs/Validator.stub
@@ -8,9 +8,9 @@
namespace App\Http\Validators;
-use Core\Http\Validator\GUMPValidator as Validator;
+use Core\Http\Validator\GUMPValidator;
-class CLASSNAME extends Validator
+class CLASSNAME extends GUMPValidator
{
/**
* Validation rules
diff --git a/tests/Application/AuthenticationTest.php b/tests/Application/AuthenticationTest.php
index 2344be64..3b79f379 100644
--- a/tests/Application/AuthenticationTest.php
+++ b/tests/Application/AuthenticationTest.php
@@ -6,9 +6,10 @@
* @link https://github.com/eliseekn/tinymvc
*/
+use App\Database\Factories\UserFactory;
use App\Database\Models\User;
use Core\Testing\ApplicationTestCase;
-use Core\Testing\Traits\RefreshDatabase;
+use Core\Testing\Concerns\RefreshDatabase;
class AuthenticationTest extends ApplicationTestCase
{
@@ -16,16 +17,14 @@ class AuthenticationTest extends ApplicationTestCase
public function testCanNotAuthenticateWithUnregisteredUserCredentials()
{
- $user = User::factory()->make();
-
- $response = $this->post('authenticate', ['email' => $user->email, 'password' => 'password']);
+ $response = $this->post('authenticate', ['email' => 'a@b.c', 'password' => 'password']);
$response->assertSessionHasErrors();
$response->assertRedirectedToUrl(url('login'));
}
public function testCanAuthenticateWithRegisteredUserCredentials()
{
- $user = User::factory()->create();
+ $user = User::factory(UserFactory::class)->create();
$response = $this->post('authenticate', ['email' => $user->email, 'password' => 'password']);
$response->assertSessionDoesNotHaveErrors();
@@ -34,12 +33,23 @@ public function testCanAuthenticateWithRegisteredUserCredentials()
public function testCanRegisterUser()
{
- $user = User::factory()->make(['password' => 'password']);
+ $user = User::factory(UserFactory::class)->make(['password' => 'password']);
$response = $this->post('register', $user->toArray());
$response->assertSessionDoesNotHaveErrors();
$response->assertRedirectedToUrl(url('login'));
- $this->assertDatabaseHas('users', ['email' => $user->email]);
+ $this->assertDatabaseHas('users', $user->toArray('name', 'email'));
+ }
+
+ public function testCanNotRegisterSameUserTwice()
+ {
+ $user = User::factory(UserFactory::class)->make(['password' => 'password']);
+
+ $this->post('register', $user->toArray());
+
+ $response = $this->post('register', $user->toArray());
+ $response->assertSessionHasErrors();
+ //$response->assertRedirectedToUrl(url('login'));
}
}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 3b82469e..0a15a102 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -6,7 +6,6 @@
* @link https://github.com/eliseekn/tinymvc
*/
-//load packages and main configuration for tests
require_once 'bootstrap.php';
session_start();