php-mohamed-nabil/marrow-framework

此包的最新版本(v1.0.2)没有提供许可证信息。

marrow是一个PHP MVC框架,用于构建具有良好MVC模式结构的PHP Web应用,使开发更简单。


README

marrow

A php框架 使用MVC设计模式从头开始构建,具有(命令行微服务、路由、模板引擎、容器、服务提供者模式、mysql数据库、中间件)等功能,帮助您了解Poupler框架及其工作原理和内部操作。从新项目开始。

请查看以下存储库以创建新骨架项目。 https://github.com/PHPMohamedNabil/marrow

这是第一个版本,目前正在测试中。

目录

安装

composer create-project php-mohamed-nabil/marrow --prefer-dist myapp

请求生命周期

所有请求都指向public/index.php文件,该文件作为所有Web应用请求的前端控制器。

<?php

define('CoreStart',microtime());

use Core\Request;
use Core\Response;
use Core\http\Kernal;

require('autoload.php');

$app= require_once __DIR__.'/../bootstrap'.DS.'bootstrap.php';

$kernal = new Kernal;

$kernal->lightOn();

$kernal->handle(new Request,$app);

$kernal->lightOff();

首先创建一个新的应用程序实例,然后使用内核类运行所需的类或服务(位于startups文件夹下的启动项)来处理应用程序请求,然后将响应返回给客户端。

内核文件

内核文件就像主板一样,负责传递、配置和准备所有应用程序设置,并运行应用程序服务:请检查core/http/kernel.php内核文件

<?php

namespace Core\Http;

use Core\Request;
use Optimus\Onion\Onion;
use Core\App;
use Core\Lightes\LightesFaced;
use Core\Lightes\Lightes;
use Dotenv\Dotenv;
use Core\Session\Storage\SessionStorage;
use Core\Session\SessionFactory;
use Spatie\Ignition\Ignition;
use Core\Configs\Config;

class Kernal{


    protected $app_middlewares=[];

    public $lightes;

    public function __construct()
    {   
       $dot_env = Dotenv::createMutable(ROOT_PATH);
       $dot_env->load();
         
       app()::$config      = Config::getInstance();

       app()->session      = SessionFactory::create(SessionStorage::class,require_once(SESSION_CONFIG));

       $light_start        = new Lightes(require_once(CONFIG_CONSTAN.'startups.php'));
        
       $this->lightes      = new LightesFaced($light_start);
 
       app()->_csrftoken   = session_token();  

          config()->load(CONFIG.DS.'app.php');
    }

最轻组件

此类实现了设计模式,您可以选择或实现所有服务在应用程序请求开始时运行,并在响应返回后运行服务:请检查core/lightes

应用外观模式

<?php

namespace Core\Lightes;
use Core\Lightes\LightesInterface;


class Lightes implements LightesInterface{
     
    private  $service_rooms;

	public function __construct(array $rooms)
	{
        $this->setRoom($rooms);
	}

	public function setRoom($rooms)
	{

           $this->service_rooms=$rooms;
	}

	public function on()
	{ 
		
		foreach($this->service_rooms as $service)
		{
			   $service =  app()::$container->get($service);
              
			   app()::$container->resloveClassMethod($service,'register');
			   app()::$container->resloveClassMethod($service,'startup');
			  
		}

	}

	


	public function off()
	{
        
	}


}

使用lightes接口创建自己的外观类实现

<?php

namespace Core\Lightes;


interface LightesInterface{

       public function on();

       public function off();
}

内核内部

请查看内核处理函数接受两个参数,第一个是请求对象,第二个是应用程序实例

在应用程序请求之前和之后运行中间件,然后将请求路由到资源

public function handle(Request $request,App $app)
    {  
        $onion =  new Onion;
        
        $onion->layer($this->middlewares())->peel($request, function($request){
                return $request;
            });

        return $app->run();
    }

内核的开关方法

您可以看到这些方法在请求之前和响应输出之后运行

    public function lightOn()
    {  
       $this->enviroment();
       $this->lightes->turnOn();
       $this->setTimeZone();
    }

    public function lightOff()
    {   
        //after output services here
         return $this->lightes->turnOff();
    }

迁移命令

要安装数据库模式,请运行:php migrate 运行所有迁移 php create_migration (迁移名称) 在迁移文件夹中创建新的迁移文件 php migrate role=(all) 回滚所有迁移 php migrate role=(迁移名称) 回滚迁移名称文件

控制器和模型命令

php create_controller (控制器名称) 在控制器文件夹下创建新的控制器文件

php create_controller (controlername) resource 在控制器文件夹下创建新的资源控制器

php create_controller (controlername) resource model 在控制器文件夹和模型文件夹下创建新的资源控制器和模型文件

php create_controller (controlername) model 在控制器文件夹和模型文件夹下创建新的控制器和模型文件

php create_model (模型名称) 在模型文件夹下创建新的模型文件

php create_repo (仓库名称) 在仓库文件夹下创建新的仓库文件

env文件

您可以通过浏览.env文件来检查和配置数据库连接、Web应用程序设置和会话设置。

APP_NAME=MyFIRSTAPP
SECRET_KEY=4fe8895cff6b23cd1f49b1c14c34a5a161248d1fba2d55392d3ef7d3d6296811
ENVIROMENT=development

DB=mysql
HOSTNAME=localhost
USERNAME=root
PASSWORD=
DBNAME=native_api

SESSION_LIFE_TIME=1800
SESSION_IDLE_TIME=1000

请随意编辑上述设置以适应您的环境设置。

生成应用程序密钥

此密钥非常重要,因为它在哈希数据算法中非常重要,它使用哈希应用程序名称并使用哈希过程作为密钥。

运行命令 php generate_key

generate key

您将看到生成的密钥,将其复制到 .env 文件中的 SECERET_KEY 值

路由

支持 GET、POST、HEAD、PUT、DELETE、OPTIONS

路由位于 app\routes 文件夹中:1-web.php 用于 web 路由

在我们的项目中,我们正在处理的路由,您可以按需更改它

use App\Core\Route\Router as Route;
use App\Controllers\ProductController;
use App\Controllers\UserController;

Route::get('/',function(){
   return view('home');
});

Route::middlewares('api',function(){
    
   Route::prefix('api/',function(){
      
      Route::resource(['product'=>ProductController::class]);

      Route::post('/user/register',[UserController::class,'store']);
      Route::get('/users/',[UserController::class,'allUsers'])->middleware('checktoken');
      Route::post('/user/login',[UserController::class,'userAuth']);

      Route::get('/user/profile',[UserController::class,'profile'])->middleware('checktoken');
      
   });



});

路由占位符

创建路由占位符,只需在占位符前加上 : 即可

use App\Core\Route\Router as Route;
use App\Controllers\ProductController;
use App\Controllers\UserController;

Route::get('/user/:id',[UserController::class,'profile']);

路由正则表达式

使用正则表达式方法创建正则表达式路由,只需编写自己的正则表达式(不带正则表达式分隔符)

use App\Core\Route\Router as Route;
use App\Controllers\ProductController;
use App\Controllers\UserController;

Route::get('/user/:id',[UserController::class,'profile'])->regx('(\d+)$');

应用程序路由列表

运行命令 **php route_list** 查看应用程序路由

routelist

样式模板引擎

marrow 使用样式模板引擎,这是一个快速且强大的基于原生代码的 PHP 模板引擎,具有强大的功能,如(模板继承、模板部分和硬编译功能),请参阅样式文档:[style](https://github.com/PHPMohamedNabil/Style)

应用程序 URL

打开 app\config\config_constants.php 文件:您将看到所有应用程序常量,最重要的是 SITE_URL

//webiste address and routes
define('ROUTES_WEB',APP.'routes');
define('SITE_URL','https://:8000/');
define('SITE_AD_URL','https://:8000/admin/');
define('VENDOR',ROOT_PATH.'vendor'.DS);

将 SUTE_URL 常量更改为您的网站或本地主机 URL,该 URL 在公共文件夹中有文档根目录。

_tcsrf

这是一个 CSRF 令牌参数,您必须在任何 POST 请求中发送它(检查 cookie 获取完整的 CSRF 令牌)如下所示

中间件

从中间件数组中创建应用程序通用中间件或路由中间件,从 config/middlewares.php 中删除 CSRF 中间件。

<?php
use App\Middlewares\csrf;
use App\Middlewares\PostSize;
use App\Middlewares\test;
use App\Middlewares\XcsrfCookie;
use App\Middlewares\ViewValidationError;
use App\Middlewares\ApiMiddlware;
use App\Middlewares\isLogedIn;

return[
        //startup application middleware here
      'web'=>[
               ViewValidationError::class,
               csrf::class,
               XcsrfCookie::class,
           
        ],
        //routes middlewares here example: ['middleware_name'=>'middleware']
      'route'=>[
           'test'        => test::class,
           'api'         => ApiMiddlware::class,
           'checktoken'  => isLogedIn::class
      ]
];

服务提供者

创建应用程序服务提供者(类和服务在应用程序引导之前运行)。您可以在 startup 文件夹下创建这些类,并将其分配给 app\config\startup.php 中的 startups 数组。

应用容器模式(控制反转)

转到 startup 文件夹并创建新文件,例如:TimeZoneStartup.php,用于在应用程序启动之前设置默认时区(在每次请求应用程序之前)类似于 Laravel 框架中的服务提供者

<?php

namespace App\Startups;

use App\Repositories\BookInterface;
use App\Repositories\BookRepository;
use App\Core\Container\Container;
use App\Startups\StartupInterface;
use App\Core\Database\NativeDB;
use App\Core\Request;
class TimeZoneStartup implements StartupInterface
{
   //timezone service provider if you are saving timezone in db
   //and wants to change it before application startup
      
      public function startup()
      {  
        config()->set('date_default_timezone_set','Europe/Moscow');

        //example:'Europe/Moscow'     
      }

      public function register()
      {   
         
      }

      
      
}

这意味着每次请求应用程序时,都会将 date_default_timezone_set 设置为 'Europe/Moscow',或者您可以使用 date_default_timezone_set() 内置 PHP 函数编写自己的其他方法来更改时区。

所有启动项都是逐个运行的,实例化每个类并运行(引导)启动方法,然后运行注册方法

<?php


use App\Startups\ProductStartup;
use App\Startups\TimeZoneStartup;

return[

     ProductStartup::class,
     TimeZoneStartup::class

];

控制器

类似于 Laravel 中的控制器系统(模拟大多数),您可以在构造函数中分配中间件

<?php

namespace App\Controllers;

use Core\Controller;
use Style\Style;
use Core\Request;

class HomeController extends Controller
{  
	  

	 public function __construct()
	 {
	 	//middleware_array,except_methods array(optional)
        $this->middleware(['test',['home']]);
	 }

模型-数据库

只需像任何框架一样创建模型,但这里使用的是原生数据库(没有 ORM 库)。

<?php

namespace App\Models;

use Core\Model;

class UserModel extends Model
{  
	//you have to set table attributes her $id,$column_2,$column_3 ...
	//you should set table name attribute or we can predict it like User to be users ,UserCategory user_categories
    protected $mass = ['username','email']; // for mass assignment
	
}

连接到数据库如下

所有模型都返回模型数据的对象

             // $model = new user(12);
		//$model->columns['model_title']='updated';   
		//$model->columns['model_price']=2500000;  
         
       
        //$model->create(['model_title'=>'coding model']); //for mass assignment
		//$model->save(); //for insert
		//$model->amend();// for update
		//$model->purge(); //for delete
		//$model->deleteSoft(12); //for soft delete  

或如下

 $user = new user;
$user->get() // all users
$user->get(12) //user with id = 12
$user->create(['username'=>'hambola','password'=>123]); // create new user hambola with new password
$user->purge(12); remove user number  12 with (id=12)
$user->update($this->model->table,['username'=>'hmobla edited'],['id'=>12]); edit user hambola with id =12

NativeDB 类

您可以使用此类轻松快速地创建自定义查询

<?php

namespace App\Repositories;

use App\Repositories\ProductRepositoryInterface;
use App\Models\Product;
use App\Core\Database\NativeDB;
 $users = NativeDB::getInstance()->table('users')->paginate(10); returns array first element is data object ,seconde is key 'links' which has pagination links
 $links = $users?$users['links']:[];

             foreach($users[0] as $user)
              {
                 echo $user->username.':'.$user->id;
              }
NativeDB::getInstance()->table('users')->select('username')->limit(10)->run();  //username from users table limi 10 records
NativeDB::getInstance()->table('users')->select('username')->where('id','=',12)->run(); //username from users table where id =12
NativeDB::getInstance()->table('users')->select('username')->where('id','=',12)->where('username','=','hambola')->run();   //username from users table where id =12 and username = hambola
NativeDB::getInstance()->table('users')->select('username')->group_by('id')->run(); //username from users table groub by id
NativeDB::getInstance()->table('users')->insertInto(['username'=>'hambola brother'])->run(); //insert new user from users table
NativeDB::getInstance()->table('users')->deleteRow(['username'=>'hambola brother'])->run(); // delete where username hambola borhter
NativeDB::getInstance()->table('users')->updateRow(['username'=>'hambola sister'],['id'=>13],$whereoperator='',$soft_delete=false)->run(); // update  where id = 13 (keep two last params as example till further updates).

创建迁移文件

首先 运行命令 php create_migration users_table(将 users_table 替换为您自己的迁移文件名)。它将在迁移文件夹中创建一个新文件 users_table_date_time_000

<?php

namespace App\Migrations;

use Core\Database\NativeDB;

class users__2023_09_03_17_56_59{
      
     //$db for database object
	public function up($db)
	{
          $db->query('CREATE TABLE IF NOT EXISTS  `users` (
                 `id` BIGINT NOT NULL AUTO_INCREMENT,
                 `username` varchar(256) NOT NULL,
                 `password` VARCHAR(255) NOT NULL ,
                 `created` datetime NOT NULL,
				 `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
				PRIMARY KEY (`id`)

             )');
           

	}

     public function down($db)
     { 

       $db->query('DROP TABLE users');

     }

在创建带有 SQL 方案的迁移文件后,运行 **php migrate** 将运行所有迁移文件并将它们全部提交。要回滚迁移,运行 **php migrate rollback**,回滚迁移运行 **php migrate roll=users__2023_09_03_17_56_59**,要回滚所有迁移,运行 php migrate roll=all

异常

使用了点火库(ignition library)具有特殊的自定义样式。core_exception

大框架(中间件,管道,存储库,命令,迁移,容器,配置,模板引擎)的概念

最后运行php marrow以在本地主机8000端口启动您的项目,或者例如运行php marrow(端口号)php marrow 4500