xiaolingzi's blog

每天都在成长...

欢迎您:亲

PHP脚本程序框架LingPHP构建

xiaolingzi 发表于 2017-03-07 18:58:49

对于web程序,有太多较为成熟的框架可以使用,我们直接拿来用即可。而php除了写web代码之后,还经常用来写一些脚本任务,如服务器部署的一些计划任务程序或者临时使用的处理程序。之前都是大家按自己的想法随便去写,这样会导致代码的凌乱不好管理,另一方面也不便于代码的复用,故开始构建一个简单的框架。

框架需要解决的主要问题如下:

1. 类自动加载,命名空间的引入,这是框架的基本。这样可以避免文件的到处引用。

2. 项目集中管理。各个程序归总起来管理。

3. 统一的调用方式。每个项目有统一的执行入口和参数选项。

4. 代码复用。引入简单的分层架构,将代码进行有组织管理。

5. 配置区分开发环境和线上环境。开发环境的很多配置比如文件路径都和正式服务器的不一样,如果不做分离,不小心就会把开发环境的配置覆盖掉正式的配置导致出错。

6. 统一的文件命名规范。

7. Linux下多进程和守护进程的支持。

我们先看一下最终的组织结构,然后再解释他们是怎么解决上面的问题的。结构如下:

application
--APP
----BaseAPP.php
----ClassLoader.php
----Global.php
----Process.php
----AppException.php

--config
----dev
------common_config.json
----prod
------common_config.json
--Lib
----BLL
----DAL
----Entity
----Utility
------IO
--------ConfigHandler.php
--Plugins
--Projects
----Project1
------config
--------dev
----------common_config.json
--------prod
----------common_config.json
------App.php
----Project2
------App.php

第一个问题:首先我们要实现自动加载类,规定类的命名空间严格按文件路径来命名,比如Lib\DAL\Abc\XxxDAL.php文件的命名空间Lib\DAL\Abc,这样我们通过根目录路径拼接命名空间上的路径即可得到文件的实际路径。

我们在APP目录新建ClassLoader.php文件,APP目录是框架基本功能的公用目录。

ClassLoader.php的代码如下:

<?php
        
class ClassLoader
{
    private static $_namespaceArr = array();
    public static function defaultLoader($className)
    {
        //获取根目录
        $dir=dirname(__DIR__);
                
        $className=str_replace("\\", "/", $className);
        $className=trim($className,"/");
                
        $classNameArr=explode("/", $className);
        $namespace = $classNameArr[0];
                
        if(array_key_exists($namespace, self::$_namespaceArr))
        {
            $dir=self::$_namespaceArr[$namespace];
        }
                
        $filename=$dir."/".$className.".php";
                
        if(is_file($filename))
        {
          require_once $filename;
        }
    }
            
    public static function registNamespace($namespace,$filePath)
    {
        if(!empty($namespace) && !empty($filePath))
        {
            self::$_namespaceArr[$namespace]=$filePath;
        }
    }
}
        
spl_autoload_register(array('ClassLoader', 'defaultLoader'));

第二个问题:具体各个项目都放在Projects项目目录下,如上面Project1和Project2

第三个问题:统一的调用入口和参数选项。如上面结构中Project1下的App.php,这个文件类似于其他语言程序中的main文件,调用该项目的任何功能都是通过执行该文件,通过传参的方式决定执行什么功能。由于所有项目的App.php文件都要引用自动加载和读取参数,我们将他们提出来放在APP目录中的BaseApp.php文件中。

BaseApp.php文件代码如下:

<?php
require_once 'AppException.php';
require_once 'Global.php';
require_once 'ClassLoader.php';
require_once 'Process.php';
        
//传入的i的参数值,即执行命令
$command = "";
        
if(php_sapi_name() == "cli")
{
    //用i作为执行脚本文件的参数名 php 文件路径 -i xx方式
    $inputParamArr = getopt("i:");
            
    //如果未传入i参数,或者i的参数值为空,则提示输入。
    if(!array_key_exists("i", $inputParamArr) || (array_key_exists("i", $inputParamArr) && empty($inputParamArr["i"])))
    {
        $isInput=false;
        while(!$command)
        {
            if(!$isInput)
            {
                fwrite(STDOUT, 'Please input the operation command[i]:');
                $isInput = true;
            }
            else
            {
                fwrite(STDOUT,'The operation command can not be empty, please input again[i]:');
            }
                
            $command = trim(fgets(STDIN));
        }
        $inputParamArr["i"]=$command;
    }
    else
    {
        $command = $inputParamArr["i"];
    }
}
        
        
function appStart($configArr)
{
    (new Process())->start($configArr);
}

这里我们还引用了Global.php文件,这个文件也是在APP目录下,这个文件的目的是用来提前创建一下全局变量,它的代码如下:

<?php
//项目根目录
define("ROOT_PATH", getcwd());
//框架目录
define("FRAME_PATH", dirname(dirname(getcwd())));
//环境变量
define("ENVIRONMENT", "dev");

然后项目下的App.php引用BaseApp.php文件,再加上根据命令进行不同操作的逻辑,即可形成统一的执行文件,App.php的代码如下:

<?php
require_once dirname(dirname(__DIR__)).'/App/BaseApp.php';
        
use Projects\Project1\Test;
        
function main()
{
    $instance = new Test();
    $instance->getConfig();
}
        
$processConfig = array();
global $command;
switch ($command)
{
    case "a":
        $processConfig = array(
            "workFunction"=>"main"
            ,"daemonize"=>false
        );
        break;
    case "b":
        $processConfig = array(
            "workFunction"=>"main"
            ,"workerNumber"=>3
            ,"daemonize"=>true
            ,"loopTimespan"=>2
        );
        break;
    default:
        echo "Nothing to do!\n";
}
        
appStart($processConfig);

最后所有项目的调用方式为

php 路径/application/projects/Project1/App.php -i xx

第四个问题:代码的复用。

我们将核心代码都放在Lib,第三方插件放在Plugins目录下。其中Lib下的代码我们使用简单的分层架构。

BLL 为业务逻辑层

DAL 为数据层

Entity 为实体层(使用ORM框架才有)

Utility 公共类层

第五个问题:配置分离。application目录下有一个config目录,里面再分为dev和prod两个目录分布为开发环境和正式环境,然后这两个目录下有相同的配置文件,只是配置的值不一样。这个config目录是比较共性的一些配置。同时每个项目如有需要也可以有一个类似的config目录用来放只跟当前项目相关的配置。配置文件的格式可以自己定,这里我们确定为json格式。

然后我们在Global.php中进行全局定义一个环境变量,用来标识目前是在什么环境下。这样出了Global.php这个文件,其他文件我们随便发正式都不用担心开发环境的配置覆盖掉正式环境的配置了。

对于配置文件配置项的读取,我们可以在公共类层定义一个类来专门读取配置的。

假设common_config.json的配置简单如下:

{
    "a":1,
    "b":2
}

那读取配置的文件(即结构中的ConfigHandler.php)代码如下:

<?php
namespace Lib\Utility\IO;
                   
class ConfigHandler
{
    /**
     * 读取公共配置
     * @param string $key
     * @return Ambigous <>
     */
    static public function getCommonConfigs($key)
    {
        $filename=dirname(dirname(ROOT_PATH)).'/config/'.ENVIRONMENT.'/common_config.json';
        $result=self::getArrayFromJsonFile($filename);
        return $result[$key];
    }
                       
    /**
     * 读取项目配置
     * @param string $key
     * @return Ambigous <>
     */
    static public function getLocalConfigs($key)
    {
        $filename=ROOT_PATH.'/config/'.ENVIRONMENT.'/common_config.json';
        $result=self::getArrayFromJsonFile($filename);
        return $result[$key];
    }
                       
    static public function getArrayFromJsonFile($filename)
    {
        $content=self::getContentFromFile($filename);
        if(!empty($content))
        {   
            return json_decode($content,true);
        }
        return array();
    }
                       
    static public function getContentFromFile($filename)
    {
        if(! file_exists($filename))
        {
            return null;
        }
        $content = file_get_contents($filename);
        return $content;
    }
                       
}

这样我们就可以通过公共类的方法类读取公共配置项或者项目配置项了。

第六个问题:统一文件命名规范。所有代码的目录和文件都是大驼峰结构,配置文件和资源文件为小写加下滑杠的方式。当然这种命名方式很多php开发者可能不喜欢,可以根据自己的喜好来定就好。

最后,是这个简单框架的下载地址:

  GitHub地址 https://github.com/xiaolingzi/LingPHP-PHP-Application

      

转载请注明出处:http://www.xxling.com/article/3104.aspx

  • 分类: PHP
  • 阅读: (673)
  • 评论: (0)
拍砖 取消
请输入昵称
请输入邮箱
*
 选择评论类型
300字以内  请输入评论内容