2017 © Pedro Peláez
 

project tinymvc

a lightweight php mvc framework

image

suconghou/tinymvc

a lightweight php mvc framework

  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 0 Open issues
  • 7 Versions
  • 0 % Grown

The README.md

简单易用的 MVC 开发框架


框架特色

  • 核心代码不足1000行,仅两个文件便可工作,极速加载
  • 单文件入口,不依赖PathInfo,入口文件即是配置文件,超级简洁
  • 文件夹随意移动,轻松多项目共享,入口文件随意命名,CLI模式轻松使用
  • MYSQL/SQLITE任意切换,注入过滤/ORM,文件缓存/HTTP缓存/数据库缓存,轻松安全
  • 异常捕获,DEBUG日志,自定义错误页,自定义异常路由一应俱全
  • 普通路由,正则路由,回调处理,百变URI随心所欲,插件模式,即插即用
  • 文件加载自动完成,延迟按需加载、无需Include简单高效

安装配置

  • index.php入口文件即配置文件,core.php框架核心,外加一个处理请求的控制器文件
  • PHP8.0及以上
  • 使用PDO连接数据库,支持MySQLSqlite,需开启PDO_MYSQL
  • 定义配置文件的程序路径(一般不需改变)和其他参数,例如SMTP,数据库,即可完美使用
  • 需要URL REWRITE支持,否则链接中要添加index.php
  • rewrite 即为一般的index.php rewrite写法

对于nginx, (*1)

try_files $uri $uri/ /index.php$is_args$args;

加入location / {}里面, (*2)

对于apache, (*3)

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Web程序可将目录结构调整为, (*4)

├── app
│  ├── controller
│  │  └── home.php
│  ├── model
│  │  └── util.php
│  └── system
│     └── core.php
├── README.md
├── var
│  ├── html
│  └── log
└── www
   └── index.php

开始使用


入口文件index.php即为配置文件,主要配置控制器模型等地址, (*5)

配置项lib_path为一个数组,配置控制器,模型,类库的自动查找地址, (*6)

view_path为模板查找文件夹, (*7)

var_path为缓存目录和日志目录, (*8)

文件夹 controller model system 其实并无区别, (*9)

系统并不是按照文件夹结构来区分 controller 和 model 而是按照类本身的特性, (*10)

包含__invoke魔术方法的类将被视为控制器, (*11)

在控制器抛出异常时,此方法将被调用用于处理异常., (*12)

event字段配置闭包函数可用于扩展app, (*13)

  • 所有加载过的控制器,都会记住是否已加载过,不会重复加载,也不会重复实例化
  • 加载过的类库和模型全局可用
  • 所有的同名文件类都可以直接通过类名调用其静态方法,或new实例化

对于低于8.1版本,需要实现array_is_list函数, (*14)

if (!function_exists('array_is_list'))
{
    function array_is_list(array $a)
    {
        return $a === [] || (array_keys($a) === range(0, count($a) - 1));
    }
}

对于低于8.0版本,需要实现str_contains函数, (*15)

if (!function_exists('str_contains'))
{
    function str_contains(string $haystack, string $needle)
    {
        return empty($needle) || strpos($haystack, $needle) !== false;
    }
}

DEBUG

配置文件debug字段用于控制日志记录,其值可为0/1 或者 false/true, (*16)

框架判断其布尔值,非debug模式仅记录ERROR级别日志,忽略INFO,WARN, (*17)

无论是否debug,只要日志目录可写,都会记录相应的错误。, (*18)



路由

框架同时包含正则路由和普通路由., (*19)

普通路由按照目录结构路由,, (*20)

正则路由按照URI匹配., (*21)

使用 route::get() 添加正则路由, (*22)

正则路由优先级高于普通路由., (*23)

正则路由有多种写法, (*24)

字符串函数, (*25)

route::get('/print','print_r');

闭包, (*26)

route::get('/dump',function(...$a){
    var_dump($a);
});

实例化控制器, (*27)

控制器类将被自动实例化,然后执行, (*28)

route::get('/hello',['home','hello'])
route::get('/hello',['home','hello','world'])

静态化控制器, (*29)

控制器类方法将被静态调用, (*30)


route::post('/static','home::echo');

捕获参数, (*31)

route::get('/userinfo/(\d+)',['home','userinfo'])

带命名空间的静态调用,可调度到子文件夹,自己再次分发路由,adminnamespace,form类无实例化,直接调用, (*32)

route::get('/hi/index','admin\form::index')

带命名空间的动态调用,可调度到子文件夹,form类校验,并实例化, (*33)

route::get('/hello/hi',['admin\form','hi'])

二级,三级,文件夹同类, (*34)

闭包模式, (*35)

route::get('/about',function(){echo 'about';})

URL 拼接, (*36)

route::u('/home/hello',['act'=>'hi'])

重定向, (*37)

route::to('/login')

控制器

默认的控制器为home,默认的action为index, (*38)

控制器被实例化时,将传入触发实例化时的路由参数给构造函数, (*39)

控制器实现了单例模式,一个控制器只会实例化一次, (*40)

app::run(array $r) 可以内部转移控制器,交由其他控制器执行, (*41)

自定义异常处理器

除了控制器的__invoke方法能处理异常外,全局异常可配置自定义处理, (*42)

在配置中添加notfounderrfound并赋值一个闭包,开启自定义错误处理, (*43)

'notfound' => function ($e, $cli) {
    echo '404 page not found';
},
'errfound' => function ($e, $cli) {
    echo 'user hand this error : ', $e->getMessage();
},

自定义异常处理后,框架提供的404,500等错误详情,页面将不再展现,只能通过日志查看, (*44)

使用缓存

框架內建2种缓存,可同时工作, (*45)

HTTP 缓存

HTTP 缓存使用 app::cache(int $second)开启, (*46)

需要在控制器输出其他内容前调用, (*47)

客户端下次请求将会200(from cache), (*48)

有客户端发起协商缓存时,框架也会自动验证,命中时返回304, (*49)

可用于缓存接口,页面等, (*50)

页面缓存

模板函数template(string $v, array $data = [], callable|int $callback = 0, string $path = '')实现了页面缓存, (*51)

设置$callback为一个大于1的秒数,即可开启页面自动缓存, (*52)

同时必须确保配置项var_path是可写的,缓存文件存储在其html子文件夹, (*53)

页面缓存的缓存键是当前请求的URI,不包含query部分, (*54)

可用于缓存公共页面,不可缓存带有个人信息的页面, (*55)

请求来临时同样鉴别对应的URI是否已缓存, (*56)

命中时释放缓存,缓存失效时删除缓存, (*57)

配置$callback为1,则返回模板渲染的数据而不是直接输出,配置为闭包可以获取渲染结果,手动处理后续逻辑, (*58)

验证器

request::verify(array $rule, array|bool $post = true, bool|callable $callback = false), (*59)

$post为要验证的数据,若非数组,则true代表$_POST,false代表$_REQUEST, (*60)

$rule, (*61)

例:, (*62)

$r =
    [
        'q' => ['maxlength=50' => 'q最大50字符'],
        'type' => ['set=video' => 'type不合法'],
        'order' => ['set=viewCount' => 'order不合法'],
        'channelId' => ['/^[\w\-]{20,40}$/' => 'channelId为20-40位字母'],
        'pageToken' => ['/^[\w\-]{4,14}$/' => 'pageToken为4-14位字母'],
        'relatedToVideoId' => ['/^[\w\-]{4,14}$/' => 'relatedToVideoId为4-14位字母'],
        'maxResults' => ['number=1,50' => 'maxResults不合法'],
        'regionCode' => ['set=HK,TW,US,KR,JP' => 'regionCode不合法'],
        'part' => 'id,snippet',
    ];

注意 maxResults 的配置项为一个数组,元素可为闭包和其他规则, 如果直接写一个闭包而不是数组,代表使用闭包的返回值,而不是对输入值校验., (*63)

內建的验证类型有, (*64)

类型限定, (*65)

'array', 'bool', 'float', 'int', 'null', 'numeric', 'object', 'scalar', 'string'

ctype类型, (*66)

'alnum', 'alpha', 'cntrl', 'digit', 'graph', 'lower', 'print', 'punct', 'space', 'upper', 'xdigit'

其他 require required default number json url ip email username password phone, (*67)

动态比较的类型有 minlength maxlength length eq eqs set int number numeric, (*68)

required 数字0,字符串0,空数组,空字符串等被认为校验不通过,其他true值,被认为通过校验, (*69)

require 在required的基础上允许数字0和字符串0校验通过 注意:空数组,空字符串,被认为校验不通过, 与required的区别在于数字0和字符串0,required更加严格, (*70)

default 如果值不存在则使用此默认值并忽略规则校验,如果有值,则规则将会生效, (*71)

int,float等为强类型规则, number可以既接受int型也接受字符串格式的整数, (*72)

numeric可以既接受float型也接受字符串格式的小数,也可以既接受int型也接受字符串格式的整数, (*73)

int,number,numeric,length 可以使用区间限定,使用,连接两个区间,int=1,50表示int型1和50之间,length=10,20表示string类型长度在10至20, (*74)

,时,表示需大于等于此起始值,number=1 表示int或字符串整数,值大于等于1,即除0外的正整数, (*75)

minlength=8 表示string长度最小8, (*76)

set 规则只能针对值是string类型的值做判断,值为int类型的,一律判断不通过, (*77)

直接使用数组语法来判断更加复杂的是否在集合中, 此比较方法是强类型的比较, (*78)

eqs为不区分大小写,eq为区分大小写, (*79)

键可扩展为一种静态方法模式校验规则,例 r::domain 表示使用r类中的静态方法domain来校验,实现复杂校验, (*80)

定义callback值,用于检验不通过时的动作, (*81)

false 抛出异常,此为默认, (*82)

闭包函数, 异常将传递给此闭包函数处理, (*83)

true 立即响应json并中断, (*84)

多数据库链接

某个Model要链接其他数据库,只需要重写ready方法即可,然后返回新的PDO实例, (*85)

public static function ready(): PDO
{
    static $_pdo;
    $_pdo ??= self::init(app::get('dbdev'));
    return $_pdo;
}

轻量级的 ORM 操作

无过度封装,简单直接,轻松完成大部分数据库操作., (*86)

PDO参数, (*87)

$options = [PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_TIMEOUT => 3, PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_STRINGIFY_FETCHES => false];

ATTR_EMULATE_PREPARES若是开启的话,数据库中的intfloat取回来时,在PHP中被转化为string,造成类型不一致,故需要, (*88)

ATTR_EMULATE_PREPARES => false
ATTR_STRINGIFY_FETCHES => false

ATTR_EMULATE_PREPARES配置为false时,预编译中的同名命名参数(:name这样的形式)只能使用一次. 见https://www.php.net/manual/en/pdo.prepare.php, (*89)

命名参数与?占位符也不可混用, (*90)

此框架都已自动处理., (*91)

增加

db::insert(array $data,string $table='',bool $ignore=false,bool $replace=false)
db::replace(array $data,string $table='')

replace也是通过insert方法,只是参数不同., (*92)

$ignore设置为true可以使用INSERT IGNORE模式, (*93)

insert方法没有完成ON DUPLICATE KEY UPDATE,若想使用,见下面说明, (*94)

insert方法没有完成INSERT DELAYED INTO,若想使用,见下面说明, (*95)

对于批量插入,参见下面说明, (*96)

删除

db::delete(array $where=[],string $table='')

$where设置为空数组即可删除全表数据, (*97)

详细的$where使用见WHERE 构造器, (*98)

查询

db::find(array $where=[],string $table='',string $col='*',array $orderLimit=[],$fetch='fetchAll')
db::findOne(array $where=[],string $table='',string $col='*',array $orderLimit=[1],$fetch='fetch')
db::findVar(array $where=[],string $table='',string $col='COUNT(1)',array $orderLimit=[1])
db::findPage(array $where=[],string $table='',string $col='*',int $page=1,int $limit=20,array $order=[])

findOne,findVar,findPage均是借助于find方法,只不过传递参数不同., (*99)

findOne默认LIMIT 1,只返回一行数据, (*100)

findVarfindOne的基础上仅返回一个字段,并且默认是COUNT(1)即计算行数,可以修改参数三返回希望的字段., (*101)

findPage为分页,返回指定页的数据和上一页,下一页等,可以添加排序规则,详细见ORDERLIMIT 构造器, (*102)

详细的$where使用见WHERE 构造器, (*103)

大量查询

如果你的一条 SQL 要查询大量数据,结果集往往超过几十万条,一次读取结果集会使得内存溢出,脚本终止., (*104)

其实find的第五个参数可以帮助你., (*105)

该参数为获取结果集的方法,find方法默认是一次性全部获取为数组,你可以传入参数true交由自己主动获取., (*106)

使用参数true后将返回一个PDOStatement,你将可以自由进行后续操作., (*107)

$stm=User::find(['id >'=>1],'userTable','*',['id'=>'ASC'],true);
while($row=$stm->fetch())
{
}

注意: pdo->exec() 返回的是一个int值,代表被影响的行数, 例如runSql()函数, (*108)

stm->execute() 返回的是一个布尔值,代表是否操作成功, 使用 stm->rowCount() 才能取到执行DELETE、 INSERT、或 UPDATE 语句受影响的行数。, (*109)

你可以修改方法fetchfetchObject, (*110)

他们二者的不同是以数组还是对象的方式返回., (*111)

即使循环获取,数据也是从 MySQL 服务器发送到了 PHP 进程中保存,若数据实在太大,可以设置数据任然保存在 MySQL 服务器,循环的过程中现场取, (*112)

在查询之前,给 PDO 实例设置, (*113)

self::setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,false);

然后再循环获取,内存使用会显著下降, (*114)

因 PDO 使用长连接,该设置会影响一定时段内的所有 SQL 查询,你也可以查询完设置回true避免影响其他查询 自 PHP5.5 起,可以使用 yield,大数据量下可以显著帮你节省内存, (*115)

子查询

使用原始值可以实现子查询, (*116)

$where=['!id IN'=>'(SELECT `id` FROM `user` WHERE fid=1)','age >'=>18];

更新

db::update(array $where,array $data,string $table='')

$where的具体形式见WHERE 构造器, (*117)

$data的具体形式见SET 构造器, (*118)

WHERE 构造器

db::condition(array &$where,string $prefix='WHERE')

在查询和删除,更新等场景下,传入一个数组作为条件, (*119)

$where是一个数组变量,一般为一维数组,某些需要使用IN操作时为二维数组, (*120)

$where为一个引用,执行过后会清理$where中的数据,因此必须传入一个变量名,执行后的$where变量将有后续用途, (*121)

例如 $where=['username'=>'name1','age'=>18];, (*122)

这样会筛选usernamename1并且age为 18 的用户, (*123)

默认的结合条件是AND,你可以在数组的最后面添加一个指定的结合符, (*124)

改用OR连接 $where=['username'=>'name1','age'=>18,'OR'];, (*125)

同时对于键(例如username)还可以添加一些修饰符和操作符., (*126)

键可以由三部分组成,以空格隔开, (*127)

第一段对应于数据库中的字段,第二段是修饰符,往往是NOT或空,第三段是操作符,可以是> , < , >= , <= , != , <> , IN , LIKE , REGEXP, (*128)

例如$where=['age >'=>18,'name LIKE'=>'user%'];, (*129)

同理,也可以使用NOT LIKE, (*130)

如果你需要使用IN操作符,也是可以的,给它的键值为一个数组, (*131)

$where=['id'=>[1,3,5,7]]; 数组作为参数默认就置为IN操作符, (*132)

将会等到 where id in (1,3,5,7),如果要使用NOT IN需要显式指明, (*133)

$where=['id NOT IN'=>[1,3,5,7]], (*134)

使用字段的引用和内置函数, (*135)

$where数组中的键值都会进行预处理操作,因此不能使用字段的引用和内置函数., (*136)

若要使用,可以在键的第一段,即数据库字段前加!定义符,代表要使用原始值., (*137)

$where=['!time < '=>'UNIX_TIMESTAMP()'], (*138)

使用!定义符后对应的键值须为定值,对于用户发送来的数据,使用!定义符前需要仔细过滤,仅能信任使用intval过滤后的值., (*139)

!time 或者 ! time ! time < !time < 等都是合法的,, (*140)

!time< ! time<是非法的,字段和操作符之间必须使用空格隔开, (*141)

NULL处理, (*142)

当条件的值为null时,自动构造为IS NULL语句., (*143)

例如 ['name'=>null] 转化为, (*144)

`name` IS NULL

如果需要IS NOT NULL需要显示申明 ['name IS NOT'=>null], (*145)

此时,使用原始值前缀!对此构造无任何影响, (*146)

['!name'=>null], (*147)

['!name IS NOT'=>null], (*148)

都是合法的., (*149)

EXISTSFIND_IN_SET, (*150)

封装一个函数使用原始值模式对where规则添加额外的条件, (*151)

final public static function extra(array $cond, array $where = [], string $filed = null): array
{
    $j = 'AND';
    foreach ($cond as $k => &$v) {
        if (is_array($v)) {
            $v = sprintf('%s IN (%s)', (str_contains($k, '.') || str_contains($k, '`')) ? $k : "`{$k}`", implode(',', $v));
        } else if (is_null($v)) {
            $v = sprintf('%s IS NULL', (str_contains($k, '.') || str_contains($k, '`')) ? $k : "`{$k}`");
        } else if (is_string($k)) {
            $v = sprintf("FIND_IN_SET('%s',%s)", trim($v, " \n\r\t\v\0\"'"), (str_contains($k, '.') || str_contains($k, '`')) ? $k : "`{$k}`");
            if (ctype_alnum(str_replace('_', '', $k))) {
                $filed ??= $k;
            }
        } else if (is_bool($v) || is_int($v) || is_float($v)) {
            $j = $v ? 'AND' : 'OR';
            unset($cond[$k]);
        }
    }
    if ($cond) {
        $where["! $filed IS NOT NULL AND"] = '(' . implode(" $j ", $cond) . ')';
    }
    return $where;
}

$cond添加键值对,键代表字段,值代表此字段对应FIND_IN_SET需要包含的值, (*152)

当没有键(键是数字),此时值为普通SQL语句,可以为EXISTS或其他子查询等, (*153)

如果$cond没有合适的字段key,则需要指定$filed的值, (*154)

self::find(self::extra(['type' => 3], ['id >' => 1]), 'messages')

构造出, (*155)

SELECT * FROM `messages` WHERE (`id` > :id_1 AND `type` IS NOT NULL AND (FIND_IN_SET('3',`type`)))

如果你需要FIND_IN_SET使用常量参数,或参数一为字段,需要用拼接模式, (*156)

self::find(self::extra(["FIND_IN_SET(`id`,'1,2,3')"], ['id >' => 1], 'id'), 'messages');

此时FIND_IN_SET就相当于查询ID是IN(1,2,3),构造出, (*157)

SELECT * FROM `messages` WHERE (`id` > :id_1 AND `id` IS NOT NULL AND (FIND_IN_SET(`id`,'1,2,3')))

性能比较:当子查询的表比较大时,使用EXISTS可能比使用IN性能更好, IN首先完成内层查询,然后在外层查询的过程中一一过滤;EXISTS需要先完成外层查询,然后对所有记录一一执行EXISTS子句进行过滤, (*158)

self::find(self::extra(["EXISTS(SELECT 1 FROM `contacts` WHERE contacts.`id`=messages.`cid` AND `name` <> 'group1' )"], ['id >' => 1], 'id'), 'messages');
self::find(self::extra(["messages.`cid` IN (SELECT `id` FROM `contacts` WHERE `name` <> 'group1')"], ['id >' => 1], 'id'), 'messages')
SELECT * FROM `messages` WHERE (`id` > :id_1 AND `id` IS NOT NULL AND (EXISTS(SELECT 1 FROM `contacts` WHERE contacts.`id`=messages.`cid` AND `name` <> 'group1' )))
SELECT * FROM `messages` WHERE (`id` > :id_1 AND `id` IS NOT NULL AND (messages.`cid` IN (SELECT `id` FROM `contacts` WHERE `name` <> 'group1')))

NOT EXISTS配合INSERT INTO ... SELECT ...还可以实现满足条件时插入, (*159)

HAVING, (*160)

简单的HAVING语句可以同上,修改condition后的参数,包含GROUP BY等复杂语句需要自己拼接, (*161)

final public static function findHaving(array $where = [], string $table = '', string $col = '*', array $orderLimit = [], $fetch = 'fetchAll')
{
    $sql = sprintf('SELECT %s FROM %s%s%s', $col, static::table($table), self::condition($where, "HAVING"), $orderLimit ? self::orderLimit($orderLimit) : '');
    return self::exec($sql, $where, $fetch);
}

SET 构造器

db::values(array &$data,bool $set=false,string $table='')

$data使用关联数组表示,默认生成VALUES()语句用于INSERT,将$set设置为true生成用于update的语句, (*162)

['name'=>'name1','pass'=>123], (*163)

数组的键也有一个前置定义符!,表示原始值,使用此定义符可以调用函数,引用字段等,插入原始值等., (*164)

['v'=>time(),'!t'=>'UNIX_TIMESTAMP()'] 添加了!则存储的是时间戳,不加!则是存储此字符串, (*165)

['!count'=>'count+1'] 使count的值加一, (*166)

['!count'=>'count+age'] 引用其他字段,count设置为count+age的和, (*167)

除非你要调用函数或引用字段,否则不建议你使用原始值,, (*168)

原始值没有引号包裹,也不是预处理字段,随意使用将会带来安全隐患., (*169)

注意, (*170)

同一个变量不可传入values()condition()两次,因为这些方法会修改传入值,第二次执行时,用到的值已经被第一次修改了, (*171)

可以使用$update=$insert,两个变量各自修改不会影响另一个, (*172)

ORDERLIMIT 构造器

db::orderLimit(array $orderLimit)

$orderLimit使用关联数组,键为数据库字段,键值为排序规则,ASCDESC,也可以使用布尔值代替,trueASC,falseDESC, (*173)

$orderLimit=['id'=>'DESC','name'=>'ASC'], (*174)

还可以使用LIMIT,添加一个整形的键值对, (*175)

$orderLimit=['id'=>'DESC','name'=>true,35=>20], (*176)

代表LIMIT 35,20, (*177)

直接使用$orderLimit=['id'=>'DESC','name'=>'ASC',5], (*178)

代表LIMIT 0,5, (*179)

如果要使用ORDER BY RAND(), (*180)

可使用$orderLimit=[''=>'RAND()']构造, (*181)

使用 ON DUPLICATE KEY UPDATE

db::insertOrUpdate(string $table, array $insert, array $update): bool实现了插入或者更新, (*182)

自己拼接的话, (*183)

$sql = sprintf('INSERT INTO %s ON DUPLICATE KEY UPDATE id=:id,name=:name', self::values($insert, false, static::table));
return self::exec($sql, $insert + $update);

需要$update=['id'=>1,'name'=>2];与自己写的SQL对应, (*184)

使用 INSERT DELAYED

DELAYED 仅适用于 MyISAM, MEMORY 和 ARCHIVE 表, (*185)

可采用如下方式构造, (*186)

$sql=sprintf('REPLACE DELAYED INTO %s',self::values($data,false,static::table));
$sql=sprintf('INSERT DELAYED INTO %s',self::values($data,false,static::table));
$sql=sprintf('INSERT DELAYED IGNORE INTO %s',self::values($data,false,static::table));

使用CASE WHEN

CASE WHEN 可以实现单条SQL语句将多个记录更新为不同的值, (*187)

CASE WHEN 可以实现对查询的数据对值分组转换等, (*188)

批量插入

可以使用prepare绑定数据循环., (*189)

如果数据表是InnoDB而不是MyISAM,还可以开启事务,进一步提升速度., (*190)

因为InnoDB默认是auto-commit mode,每条 SQL 都会当做一个事务自动提交,会带来额外开销., (*191)

INSERT INTO也可以使用IGNORE,REPLACE,ON DUPLICATE KEY UPDATE, (*192)

数据源, (*193)

$column = ['title', 'text', 'ids', 'enable'];
$data = [
    ['title1', 'text1', 'a,b', 1],
    ['title2', 'text2', 'a,b', 1],
    ['title3', 'text3', 'b,c', 0],
    ['title4', 'text4', 'b,c', 0]
];

db::insertMany(string $table, array $column, array $data): bool实现了事务批量插入, (*194)

更快的批量插入

使用单条 SQL 代替循环插入速度将会更快, (*195)

数据源和参数同上, (*196)

db::insertOnceMany(string $table, array $column, array $data, array $duplicateKeyUpdate = []): int 实现了单条SQL批量插入,并且可以指定重复键的更新策略, (*197)

批量插入中使用ON DUPLICATE KEY UPDATE仅需配置第四个参数$duplicateKeyUpdate格式同$column,需要是其子集, (*198)

如果数据量巨大,可能造成SQL语句太长,可以使用array_chunk切割$data分批调用, (*199)

嵌套的ANDOR

对WHERE 构造器传入二维数组,可以构造嵌套的ANDOR, (*200)

$where1=['age >'=>18,'sex'=>1];
$where2=['id >'=>20,'id <'=>40];
db::find([$where1, $where2, 'OR'], 'users');

构造出如下SQL, (*201)

SELECT * FROM users WHERE ((`age` > :age_1 AND `sex` = :sex_2) OR (`id` > :id_3 AND `id` < :id_4))

高级查询

如果你需要非常复杂的 SQL 查询,可能不能一次就利用方法完成,需要多次操作, (*202)

或者自己进行prepare并绑定., (*203)

使用db::query可以一次完成多个 SQL 操作,它是db::exec的批处理., (*204)

$sql1="SELECT 1";
$sql2="SELECT 2";
$sql3="SELECT 3";
$data1=$data2=$data3=[];
[$res1,$res2,$res3]=self::query([$sql1,$data1,'fetchAll'],[$sql2,$data2,'fetch'],[$sql3,$data3,true]);

每个参数都是数组, (*205)

数组内部,第一个元素要批处理的$sql 语句,第二个参数绑定的参数,第三个参数获取方式., (*206)

所有的 SQL 执行最终都会指向db::exec($sql,array $params=[],$fetch=''), (*207)

第三个参数fetch, (*208)

如果为空,代表非查询语句,返回的是影响的行数(无预处理exec())或者是否成功(有预处理stm->execute()), (*209)

如果是个string,返回结果集(例如fetchAll,无论是否走了预处理), (*210)

如果是true(仅当是查询语句,或者是预处理操作,才能置为true),返回一个PDOStatement(无论是否走了预处理),后续按何种方式获取结果集,自己任意操作., (*211)

>, (*212)

注意: params 为空则代表没有预处理参数,底层会直接调用 pdo->exec() 或 pdo->query() 如果不为空,则会先预处理,然后stm->execute(), (*213)

pdo->exec() 返回的是一个int值,代表被影响的行数, 例如runSql()函数, (*214)

stm->execute() 返回的是一个布尔值,代表是否操作成功, 使用 stm->rowCount() 才能取到执行DELETE、 INSERT、或 UPDATE 语句受影响的行数。, (*215)

如果你关心受影响的行数,在调用db::exec时需注意., (*216)

SQLITE 差异

insert ignore 和 replace 与MySQL的语法存在差异, (*217)

insert or replace:如果不存在就插入,存在就更新 insert or ignore:如果不存在就插入,存在就忽略, (*218)

insert函数可以新增如下对SQLITE的改写版, (*219)

final public static function sqliteInsert(array $data, string $table = '', bool $replace_or_ignore = null)
{
    $sql = sprintf('%s %sINTO %s', $replace_or_ignore ? 'INSERT OR REPLACE' : 'INSERT', $replace_or_ignore === false ? 'OR IGNORE ' : '', self::values($data, false, $table));
    return self::exec($sql, $data);
}

The Versions

29/07 2018

dev-master

9999999-dev https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.4.0

 

framework mvc fast

19/06 2018

dev-develop

dev-develop https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.4.0

 

framework mvc fast

24/03 2016

1.9.2

1.9.2.0 https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.4.0

 

framework mvc fast

18/11 2015

1.9.0

1.9.0.0 https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.4.0

 

framework mvc fast

21/05 2015

1.8.6-RC

1.8.6.0-RC https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.2.0

 

framework mvc fast

23/04 2015

1.8.4-RC

1.8.4.0-RC https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.2.0

 

framework mvc fast

23/04 2015

1.8.4-RC1

1.8.4.0-RC1 https://github.com/suconghou/mvc

a lightweight php mvc framework

  Sources   Download

MIT

The Requires

  • php >=5.2.0

 

framework mvc fast