关于yii:小编推荐Yii20Vue20前后端分离框架通用后台源码

yii-vue-cms 是一个yii2 + vue + element-ui的后盾极速开发框架,前后端拆散。 可通过gii实现主动生成页面(反对文本、富文本、单图、多图、生成),疾速开发。根底性能有: 权限治理(实现了页面路由 + 接口权限管制, 接口权限可随便敞开)、图片相册、图片上传、富文本、导出。网址: http://119.91.46.222:86/index.html 账号:test admin 明码:123456 装置教程接口服务部署装置依赖,因为家喻户晓的起因,国外的网站连贯速度很慢。因而装置的工夫可能会比拟长,咱们倡议应用国内镜像 (阿里云)。composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/根目录执行composer update导入数据库,创立yii-vue-cms_db数据库 将yii-vue-cms.sql导入数据库中配置接口域名,将配置的域名指向yii-vue-cms/admin/web目录 如果域名为yii-vue-cms.com (此时间接拜访此域名是没内容的这只是作为接口)前端vue我的项目部署cd vueadmin执行npm install install失败 1、删除node_modules 2、npm cache clean --force 3、npm installinstall失败 1、删除node_modules 2、删除package-lock.josn 3、npm install批改接口域名,将vueadmin/src/components/js/request.js里的域名换成下面配置好的域名装置胜利后 执行 npm run dev 关上http://localhost:8080 即可打包 执行npm run build 会生成一个dist文件 将dist文件放在yii-vue-cms/admin/web下, 此时拜访yii-vue-cms.com/dist/生成页面下面配置好之后 关上http://139.155.45.209:85/index.php/gii 或http://139.155.45.209:85/gii 到gii页面 生成Model如表fx_test 类名即为TestNamespace 填 commonmodels勾选 Use Table Prefix 、 Generate Labels from DB Comments 、 Use Schema Name模板 Code Template 抉择咱们自定义的模板 mymodel 生成 CRUD Generatormodel class 填 commonmodelsTestController Class 填 adminv1TestControllerview path 填 @vue/src/page/test 模板 Code Template 抉择咱们自定义的模板 mymodel点击生成后,会在对应目录下生成文件,此时先将index.php、 list.php等 改成.vue文件 保留, 而后格式化一下创立菜单在菜单权限--》 菜单列表 增加对应的路由,保留后,从新登录即可看到菜单分为路由 和 接口 须要填写对应的内容, 如果不须要接口权限,在admin/config/params.php 里敞开即可

April 28, 2023 · 1 min · jiezi

关于yii:Yii-实现乐观锁和悲观锁

一:在Yii中实现乐观锁乐观锁(optimistic locking)体现出大胆、求实的态度。应用乐观锁的前提是, 理论利用当中,发生冲突的概率比拟低。他的设计和实现间接而简洁。 目前Web利用中,乐观锁的应用占有绝对优势。因而在Yii为ActiveReocrd乐观锁反对 1:在yii中实现乐观锁步骤、1):给须要加锁的表增加一个字段,用于示意版本号,这里我个别选手version字段作为版本号字段,留神,如果你须要加锁的表曾经生成Model了,那么对应表的Model要将你增加的版本号字段(version)信息退出Model 2):在更新表中字段时,应用 try ... catch 看看是否能捕捉一个 yii\db\StaleObjectException 异样,如果捕捉到yii\db\StaleObjectException 异样,阐明在本次批改这个记录的过程中, 该记录曾经被批改过了,作出相应提醒 2:Yii中实现乐观锁1):在yii中申明指定字段为版本号 版本号是实现乐观锁的基本所在。所以第一步,咱们要通知Yii,哪个字段是版本号字段,申明版本号的办法由yii\db\BaseActiveRecord(vendor/yiisoft/yii2/db/BaseActiveRecord)中的optimisticLock办法负责 public function optimisticLock(){ return null;}这个办法返回 null ,示意不应用乐观锁,如果咱们须要应用乐观锁的话,咱们须要在咱们的须要加锁的表的Model中重载optimisticLock办法 public function optimisticLock(){ return 'version';}如上阐明以后的ActiveRecord中,有一个 version 字段,能够为乐观锁所用 2:实现乐观锁咱们在Model中设置了版本号后,这时候咱们的更新和删除都是乐观锁操作了,与失常操作数据库的形式统一 try { $crowd = Crowd::findOne(['crowd_id' => 12]); $crowd->status = 1; $crowd->save();} catch (\Exception $e) { return false;}在更新过程中,咱们会调用到 yii\db\BaseActiveRecord::updateInternal()办法,此办法外面就具备解决乐观锁的代码 protected function updateInternal($attributes = null) { if (!$this->beforeSave(false)) { return false; } // 获取等下要更新的字段及新的字段值 $values = $this->getDirtyAttributes($attributes); if (empty($values)) { $this->afterSave(false, $values); return 0; } // 把原来ActiveRecord的主键作为等下更新记录的条件,也就是说,等下更新的,最多只有1个记录。 $condition = $this->getOldPrimaryKey(true); // 获取版本号字段的字段名,比方 version $lock = $this->optimisticLock(); // 如果 optimisticLock() 返回的是 null,那么,不启用乐观锁。 if ($lock !== null) { // 这里的 $this->$lock ,就是 $this->version 的意思; 这里把 version+1 作为要更新的字段之一。 $values[$lock] = $this->$lock + 1; // 这里把旧的版本号作为更新的另一个条件 $condition[$lock] = $this->$lock; } $rows = static::updateAll($values, $condition);// 如果曾经启用了乐观锁,然而却没有实现更新,或者更新的记录数为0; // 那就阐明是因为 version 不匹配,记录被批改过了,于是抛出异样。 if ($lock !== null && !$rows) { throw new StaleObjectException('The object being updated is outdated.'); } if (isset($values[$lock])) { $this->$lock = $values[$lock]; } $changedAttributes = []; foreach ($values as $name => $value) { $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null; $this->_oldAttributes[$name] = $value; } $this->afterSave(false, $changedAttributes); return $rows; }在删除过程中,咱们会调用到 yii\db\BaseActiveRecord::delete()办法,此办法外面就具备解决乐观锁的代码 ...

November 1, 2022 · 2 min · jiezi

关于yii:php框架哪个好

php开发框架,罕用的大略是Yii、TP5、Laravel等等···· Yii是一个基于组件开发的用做大型web 利用的高性能PHP框架,它将 Web 编程中的可重用性施展到极致,可能显著减速开发过程。Yii(读作“易”),代表简略(easy)、高效(efficient)、可扩大(extensible)。 Codelgniter 以它的简略易用,性能和速度而闻名。这款PHP开发框架对于共享主机的用户而言很现实,如果你想有一个小型覆盖面的框架。它提供了简略的解决方案,还有扩大的知识库,通过视频教程,用户指南和wiki来提供反对。 ThinkPHP是一个性能卓越并且功能丰富的轻量级PHP开发框架,自身具备很多的原创个性,并且提倡大道至简,开发由我的开发理念,用起码的代码实现更多的性能,主旨就是让WEB利用开发更简略、更疾速。 框架就是用来开发的工具,大厂会有本人封装的框架,一些组件,包含公共办法能够不便复用,节约开发成本,那么你们感觉哪个框架好用呢??? 欢送大佬们留言探讨斧正~

August 24, 2021 · 1 min · jiezi

关于yii:yii-速查手册

生成urluse yii\helpers\Url;$url = Url::to(['post/view', 'id' => 100]);Url::to(['post/view', 'id' => 100, '#' => 'content'],true); 带域名http://yii.api.shanliwawa.top:80/post/view?id=100#content// 主页URL:/index.php?r=site%2Findexecho Url::home();// 根URL,如果程序部署到一个Web目录下的子目录时十分有用echo Url::base();申请$request = Yii::$app->request;$get = $request->get('id'); $id = $request->get('id', 1); 不存在为1$name = $request->post('name'); api中$params = $request->bodyParams;// 返回参数 "id"$param = $request->getBodyParam('id');if ($request->isAjax) { /* 该申请是一个 AJAX 申请 */ }if ($request->isGet) { /* 申请办法是 GET */ }if ($request->isPost) { /* 申请办法是 POST */ }if ($request->isPut) { /* 申请办法是 PUT */ }$userHost = Yii::$app->request->userHost;客户主机$userIP = Yii::$app->request->userIP;客户ip跳转yii\web\Response::redirect($url) $this->redirect('http://example.com/new', 301);//控制器中session$session = Yii::$app->session;// 获取session中的变量值,以下用法是雷同的:$language = $session->get('language');$language = $session['language'];$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;// 设置一个session变量,以下用法是雷同的:$session->set('language', 'en-US');$session['language'] = 'en-US';$_SESSION['language'] = 'en-US';// 删除一个session变量,以下用法是雷同的:$session->remove('language');unset($session['language']);unset($_SESSION['language']);// 查看session变量是否已存在,以下用法是雷同的:if ($session->has('language')) ...if (isset($session['language'])) ...if (isset($_SESSION['language'])) ...// 遍历所有session变量,以下用法是雷同的:foreach ($session as $name => $value) ...foreach ($_SESSION as $name => $value) ...cookie// 从 "request" 组件中获取 cookie 汇合(yii\web\CookieCollection)$cookies = Yii::$app->request->cookies;// 获取名为 "language" cookie 的值,如果不存在,返回默认值 "en"$language = $cookies->getValue('language', 'en');// 另一种形式获取名为 "language" cookie 的值if (($cookie = $cookies->get('language')) !== null) { $language = $cookie->value;}// 可将 $cookies 当作数组应用if (isset($cookies['language'])) { $language = $cookies['language']->value;}// 判断是否存在名为"language" 的 cookieif ($cookies->has('language')) ...if (isset($cookies['language'])) ...// 从 "response" 组件中获取 cookie 汇合(yii\web\CookieCollection)$cookies = Yii::$app->response->cookies;// 在要发送的响应中增加一个新的 cookie$cookies->add(new \yii\web\Cookie([ 'name' => 'language', 'value' => 'zh-CN',]));// 删除一个 cookie$cookies->remove('language');// 等同于以下删除代码unset($cookies['language']);日志Yii::error('日志文件');数据库查问多条$posts = Yii::$app->db->createCommand('SELECT * FROM post')->queryAll();查问一条$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1')->queryOne();返回一列$titles = Yii::$app->db->createCommand('SELECT title FROM post')->queryColumn();返回标量$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post')->queryScalar();绑定参数$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')->bindValue(':id', $_GET['id'])->bindValue(':status', 1)->queryOne();非查问Yii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1') ->execute();Yii::$app->db->createCommand()->insert('user', ['name' => 'Sam','age' => 30,])->execute();Yii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();Yii::$app->db->createCommand()->delete('user', 'status = 0')->execute();数据库模型操作one(): 依据查问后果返回查问的第一条记录。all(): 依据查问后果返回所有记录。count(): 返回记录的数量。sum(): 返回指定列的总数。average(): 返回指定列的平均值。min(): 返回指定列的最小值。max(): 返回指定列的最大值。scalar(): 返回查问后果的第一行中的第一列的值。column(): 返回查问后果中的第一列的值。exists(): 返回一个值,该值批示查问后果是否有数据。where(): 增加查问条件with(): 该查问应执行的关系列表。indexBy(): 依据索引的列的名称查问后果。asArray(): 以数组的模式返回每条记录。Customer::find()->one(); 此办法返回一条数据;Customer::find()->all(); 此办法返回所有数据;Customer::find()->count(); 此办法返回记录的数量;Customer::find()->average(); 此办法返回指定列的平均值;Customer::find()->min(); 此办法返回指定列的最小值 ;Customer::find()->max(); 此办法返回指定列的最大值 ;Customer::find()->scalar(); 此办法返回值的第一行第一列的查问后果;Customer::find()->column(); 此办法返回查问后果中的第一列的值;Customer::find()->exists(); 此办法返回一个值批示是否蕴含查问后果的数据行;Customer::find()->asArray()->one(); 以数组模式返回一条数据;Customer::find()->asArray()->all(); 以数组模式返回所有数据;Customer::find()->where($condition)->asArray()->one(); 依据条件以数组模式返回一条数据;Customer::find()->where($condition)->asArray()->all(); 依据条件以数组模式返回所有数据;Customer::find()->where($condition)->asArray()->orderBy('id DESC')->all(); 依据条件以数组模式返回所有数据,并依据ID倒序;rediscomposer require yiisoft/yii2-redis// 获取 redis 组件$redis = Yii::$app->redis;// 判断 key 为 username 的是否有值,有则打印,没有则赋值$key = 'username';if ($val = $redis->get($key);) { var_dump($val);} else { $redis->set($key, 'marko'); $redis->expire($key, 5);}

July 24, 2021 · 2 min · jiezi

关于yii:Yii2学习笔记

控制器控制器周期beforeAction/** * action 运行的前置操作 * * 比拟适宜统一化的数据验证,申请数据的承受等 * @param \yii\base\Action $action action 类 * @return bool 返回ture 控制器周期才会走上来 */public function beforeAction($action){ var_dump($action->id, $action->controller->id); return parent::beforeAction($action); // TODO: Change the autogenerated stub}afterAction/** * action 后置操作 * * 比拟适宜返回最初的日志收集 * @param \yii\base\Action $action action 类 * @param mixed $result * @return mixed */public function afterAction($action, $result){ return parent::afterAction($action, $result); // TODO: Change the autogenerated stub}模型AR 模型 罕用办法查问数据// 查问单条$one = Post::find()->where(['id' => 1])->one();// 或者$one = Post::finOne(1);// 查问多条$posts = Post::find()->where(['status' => 1])->all();// 或者$posts = Post::findAll(['status' => 1]);// 通过sql查问$posts = Post::findBySql('SELECT * FROM `posts` WHERE `status` = :status', [':status' => 1])->all();查问条件的设定where 查问反对的参数字符串格局 例如: 'status=1'// 对应的SQL: WHERE status = 1$query->where('status = 1');// 应用字符串形式,能够绑定参数$query->where('status = :status', [':status' => 1])哈希格局 例如: ['status' => 1, 'type' => 1]哈希格局最适宜用来指定多个 AND 串联起来的简略的"等于断言"子条件。 ...

September 25, 2020 · 7 min · jiezi

关于yii:从零开始搭建完整的电影全栈系统四restfulApi用户的认证授权及用户注册

在配置文件main.php中设置用户认证类,并正文掉cookies和session配置,因为Api客户端和WEB网站不同,通常不能应用cookies和session维持登录状态。 'user' => [ 'identityClass' => 'common\models\User', 'enableAutoLogin' => true, 'enableSession' => false, //'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true], ], /*'session' => [ // this is the name of the session cookie used for login on the backend 'name' => 'advanced-backend', ],*/实现Api用户认证(登录性能)复制一份common/models/UserLoginForm到common/models中并改名为ApiLoginForm,同时将remember me 和vitifyCode等相干性能勾销,并重写login办法。代码如下: <?phpnamespace api\models;use common\models\User;use Yii;use yii\base\Model;/** * Login form */class ApiLoginForm extends Model{ public $username; public $password; //public $rememberMe = true; public $vitifyCode; private $_user; public function attributeLabels() //属性labels { return [ 'username' => '用户名', 'password' => '明码', //'rememberMe' => '记住我', //'vitifyCode' => '验证码', ]; } /** * {@inheritdoc} */ public function rules() { return [ // username and password are both required [['username', 'password'], 'required'], // rememberMe must be a boolean value //['rememberMe', 'boolean'], // password is validated by validatePassword() ['password', 'validatePassword'], //['vitifyCode', 'captcha'], //验证码验证 ]; } /** * Validates the password. * This method serves as the inline validation for password. * * @param string $attribute the attribute currently being validated * @param array $params the additional name-value pairs given in the rule */ public function validatePassword($attribute, $params) { if (!$this->hasErrors()) { $user = $this->getUser(); if (!$user || !$user->validatePassword($this->password)) { $this->addError($attribute, 'Incorrect username or password.'); } } } /** * Logs in a user using the provided username and password. * * @return bool whether the user is logged in successfully */ public function login() { if ($this->validate()) {// return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); $access_token = $this->_user->generateAccessToken(); $this->_user->expire_at=time()+3600*7*24;//设定access_token过期工夫 $this->_user->save(); Yii::$app->user->login($this->_user,3600*7*24); return $access_token; } else { return false; } } /** * Finds user by [[username]] * * @return User|null */ protected function getUser() { if ($this->_user === null) { $this->_user = User::findByUsername($this->username); } return $this->_user; }}在common/models User中实现generateAccessToken,代码如下: ...

September 11, 2020 · 3 min · jiezi

关于yii:Yii实现微信公众号的场景二维码

在Yii中实现场景二维码这里我应用的是easywechat插件,装置easywechat插件 composer require jianyan74/yii2-easy-wechatgithub地址:https://github.com/jianyan74/yii2-easy-wechat easywechat文档地址:https://www.easywechat.com/docs/master/overview 生成场景二维码前提: 微信的场景二维码性能次要是生成一个微信二维码,而后在手机应用微信扫描此二维码时,会触发微信告诉,所以咱们在生成场景二维码之前进行微信的服务端验证 1:服务端验证 $app = Yii::$app->wechat->getApp(); $server = $app->server; $response = $server->serve(); $response->send(); exit(); 服务端验证代码实现之后在微信公众号进行服务端验证即可 2:生成场景二维码 $app = Yii::$app->wechat->getApp();$app->server->push(function ($message) use ($app) { switch ($message['MsgType']){ case 'event': //扫码事件:SCAN 订阅事件:subscribe if (isset($message['Event']) && ($message['Event'] == 'SCAN' || $message['Event'] == 'subscribe')) { $openId = $message['FromUserName'];//扫面用户的openID //获取参数 if ($message['Event'] == 'SCAN') { $code= $message['EventKey']; } else { $code= str_replace('qrscene_', '', $message['EventKey']); } //发送图文音讯 $items = [ new NewsItem([ 'title'=> '图文题目', 'description' => '图文形容', 'url' => '图文链接', 'image'=> '图文图片, ]), ]; return new News($items); } break; default: break; }});$server = $app->server;$response = $server->serve();$response->send();exit();依据如上就能够实现场景二维码 ...

August 28, 2020 · 1 min · jiezi

php-使用phpofficephpspreadsheet拓展操作excel

一:安装phpoffice/phpspreadsheet composer require phpoffice/phpspreadsheet二:phpoffice/phpspreadsheet API接口详解 PhpSpreadsheet提供了丰富的API接口,可以设置诸多单元格以及文档属性,包括样式、图片、日期、函数等等诸多应用,总之你想要什么样的Excel表格,PhpSpreadsheet都能做到 在使用phpoffice/phpspreadsheet的API接口前,确保引入了正确的文件并实例化 use PhpOffice\PhpSpreadsheet\Spreadsheet;//引入文件$spreadsheet = new PhpOffice\PhpSpreadsheet\Spreadsheet();//创建一个新的excel文档$sheet = $spreadsheet->getActiveSheet();//获取当前操作sheet的对象1:设置字体: $sheet->getStyle('A7:B7')->getFont()->setBold(true)->setName('Arial') ->setSize(10);//将A7至B7两单元格设置为粗体字,Arial字体,10号字$sheet->getStyle('B1')->getFont()->setBold(true);//将B1单元格设置为粗体字2:设置颜色 $sheet->getStyle('A1')->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);//将A1单元格文字颜色设为红色3:设置列宽 $sheet->getColumnDimension('A')->setWidth(20);//将A列的宽度设为20(字符)$sheet->getColumnDimension('B')->setAutoSize(true);//将B列的宽度设为自动宽度$sheet->getDefaultColumnDimension()->setWidth(12);//设置默认列宽为124:设置行高 $sheet->getRowDimension('10')->setRowHeight(100);//将第十行的高度设为100pt$sheet->getDefaultRowDimension()->setRowHeight(15);//设置默认行高为155:对齐 $sheet->getStyle('A:D')->getAlignment() ->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER) //设置垂直居中 ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER) //设置水平居中 ->setWrapText(true); //设置自动换行6:合并单元格 $sheet->mergeCells('A1:D2');//A1到D2合并为一个单元格7:将合并后的单元格拆分 $sheet->unmergeCells('A1:D2');//将合并后的单元格拆分。8:使用applyFromArray实现单元格样式设置 //样式变量$style = [//设置字体样式'font' => [ 'name' => 'Arial', 'bold' => true, 'italic' => false, 'underline' => Font::UNDERLINE_DOUBLE, 'strikethrough' => false, 'color' => [ 'rgb' => '808080' ] ],//设置边框线样式'borders' => [ //allBorders所有的边框线样式 //左边框线 'bottom' => [ 'borderStyle' => Border::BORDER_DASHDOT, 'color' => [ 'rgb' => '808080' ] ], //上边框线 'top' => [ 'borderStyle' => Border::BORDER_DASHDOT, 'color' => [ 'rgb' => '808080' ] ]],//对齐样式'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true,],//是否使用前缀'quotePrefix' => true];$sheet->getStyle('A1:D1')->applyFromArray($style);9:设置工作表标题 ...

July 15, 2019 · 3 min · jiezi

Access-to-debugger-is-denied-due-to-IP-address-restriction

有人在线上看到自己 runtime/logs/app.log 這个文件看见這个吗?弄出了這个错误。请问我已经有传递csrf了,然后页面访问也正常为什么会打印這个log [warning][yii\debug\Module::checkAccess] Access to debugger is denied due to IP address restriction2017-05-15 10:23:35 [183.253.136.149][8][-][warning][yii\debug\Module::checkAccess] Access to debugger is denied due to IP address restriction. The requesting IP address is 183.253.136.1492017-05-15 10:23:35 [183.253.136.149][8][-][info][application] $_POST = [

July 10, 2019 · 1 min · jiezi

PHP-Yii下REQUEST获取不到前端post过来的数据

场景:最近在搭建PHP服务的时候,遇到一个问题,选用框架是YII,通过POSTMAN提交数据,提交方式选择POST,其中Body主要有以下三个选项1、form-data2、x-www-form-urlencoded3、raw 当选择1、form-data时,提交能正常接收到POST数据,但是选择raw+JSON(application/json)时,POST接收数据为空, 这个问题出现的原因是什么呢?需要我们从YII实现的原理说起 1.通过 curl 发送 json 格式的数据,譬如代码: <?phpfunction http_post_json($url, $jsonStr){ $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charset=utf-8', 'Content-Length: ' . strlen($jsonStr) ) ); $response = curl_exec($ch); //$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $response;}$api_url = 'http://fecshop.appapi.fancyecommerce.com/44.php';$post_data = [ 'username' => 'terry', 'password' => 'terry4321'];然后在接收端,使用$_POST接收,发现打印是空的 原因是,PHP 默认只识别 application/x-www.form-urlencoded 标准的数据类型,因此接收不到,只能通过 //第一种方法$post = $GLOBALS['HTTP_RAW_POST_DATA'];//第二种方法$post = file_get_contents("php://input");来接收 ...

July 8, 2019 · 3 min · jiezi

RageFrame-一个-Yii2-AdminLET-免费开源多商户通用后台管理系统

RageFrame 2.0为二次开发而生,让开发变得更简单 项目地址:https://github.com/jianyan74/... 前言这是一款现代化、快速、高效、便捷、灵活、方便扩展的应用开发骨架。 RageFrame创建于2016年4月16日,一个基于Yii2高级框架的快速开发引擎,目前正在成长中,目的是为了集成更多的基础功能,不在为相同的基础功能重复制造轮子,开箱即用,让开发变得更加简单。 2018年9月10日 2.0版本正式上线,经过1.0版本一年多的开源反馈磨合,以更加优秀的形态出现。对1.0的版本进行了重构优化完善,更好的面向开发者进行二次开发。2.3.x版本更是优化了底层突出了服务层,分离业务逻辑,支持多商户。 特色极强的可扩展性,应用化,模块化,插件化机制敏捷开发。极致的插件机制,系统内的系统,安装和卸载不会对原来的系统产生影响,强大的功能完全满足各阶段的需求,支持用户多端访问(后台、微信、Api、前台等),系统中的系统。极完善的RBAC权限控制管理、无限父子级权限分组、可自由分配子级权限,且按钮/链接/自定义内容/插件等都可加入权限控制。只做基础底层内容,不会在上面开发过多的业务内容,满足绝大多数的系统二次开发。多入口模式,多入口分为 backend(后台)、frontend(PC前端),wechat(微信),api(对内接口),oauth2 server(对外接口),不同的业务,不同的设备,进入不同的入口。对接微信公众号且支持小程序,使用了一款优秀的微信非官方SDK Easywechat 4.x,开箱即用,预置了绝大部分功能,大幅度的提升了微信开发效率。整合了第三方登录,目前有QQ、微信、微博、GitHub等等。整合了第三方支付,目前有微信支付、支付宝支付、银联支付,二次封装为网关多个支付一个入口一个出口。整合了RESTful API,支持前后端分离接口开发和app接口开发,可直接上手开发业务。一键切换云存储,本地存储、腾讯COS、阿里云OOS、七牛云存储都可一键切换,且增加其他第三方存储也非常方便。全面监控系统报错,报错日志写入数据库,方便定位错误信息。快速高效的Servises(服务层),遵循Yii2的懒加载方式,只初始化使用到的组件服务。丰富的表单控件(时间、日期、时间日期、日期范围选择、颜色选择器、省市区三级联动、省市区勾选、单图上传、多图上传、单文件上传、多文件上传、百度编辑器、图表、多文本编辑框、地图经纬度选择器、图片裁剪上传、TreeGrid)和组件(二维码生成、Curl、IP地址转地区),快速开发,不必在为基础组件而担忧。完善的文档和辅助类,方便二次开发与集成。思维导图 开始之前具备 PHP 基础知识具备 Yii2 基础开发知识具备 开发环境的搭建仔细阅读文档,一般常见的报错可以自行先解决,解决不了在来提问\如果要做小程序或微信开发需要明白微信接口的组成,自有服务器、微信服务器、公众号(还有其它各种号)、测试号、以及通信原理(交互过程)如果需要做接口开发(RESTful API)了解基本的 HTTP 协议,Header 头、请求方式(GET\POST\PUT\PATCH\DELETE)等能查看日志和Debug技能一定要仔细走一遍文档Demo地址:http://demo2.rageframe.com/ba... 账号:demo 密码:123456 官网http://www.rageframe.com 文档安装文档 · 本地文档 · 更新历史 · 常见问题 问题反馈在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流 QQ群:655084090 Github:https://github.com/jianyan74/... 特别鸣谢感谢以下的项目,排名不分先后 Yii:http://www.yiiframework.com EasyWechat:https://www.easywechat.com Bootstrap:http://getbootstrap.com AdminLTE:https://adminlte.io 版权信息RageFrame遵循Apache2开源协议发布,并提供免费使用。 本项目包含的第三方源码和二进制文件之版权信息另行标注。 版权所有Copyright © 2016-2019 by RageFrame www.rageframe.com All rights reserved。

July 1, 2019 · 1 min · jiezi

浅析Yii20的行为Behavior

概念理解:使用行为(behavior)可以在不修改现有类的情况下,对类的功能进行扩充。 通过将行为绑定到一个类,可以使类具有行为本身所定义的属性和方法,就好像类本来就有这些属性和方法一样。 而不需要写一个新的类去继承或包含现有类。在功能上类似于 Traits ,达到类似于多继承的目的。行为的实现demo<?phpnamespace common\components;use yii\base\Component;// 待绑定行为的类class MyClass extends Component{ }<?phpnamespace common\components;use yii\base\Behavior;// 定义一个行为类class BehaviorTest extends Behavior{ const EVENT_AFTER_SAVE = 'eventAfterAttach'; public $_val = '我是BehaviorTest里面的公有属性_val'; public function getOutput() { echo '我是BehaviorTest里面的公有方法getOutput'; } public function events() { return [ self::EVENT_AFTER_SAVE => 'afterAttach' ]; } public function afterAttach() { echo '事件已触发'; }}// 在控制器或者命令行下调用$myClass = new MyClass();$myBehavior = new BehaviorTest();// 将行为绑定到 MyClass 的实例$myClass->attachBehavior('test', $myBehavior);// MyClass 实例调用行为类中的属性echo $myClass->_val;// MyClass 实例调用行为类中的方法$myClass->getOutput();// MyClass 实例触发行为类中定义的事件$myClass->trigger(BehaviorTest::EVENT_AFTER_SAVE);行为的绑定原理我们先来看一下 $myClass->attachBehavior('test', $myBehavior); 行为绑定的时候做了哪些事情?好的,又是我们的老朋友yii\base\Component了 ...

May 28, 2019 · 2 min · jiezi

浅析Yii20的属性Property

概念理解:第一次看深入理解Yii2.0的时候,我也是懵逼的,属性不就是类的属性吗,有什么好说的。后来才知道Yii框架对成员变量和属性做了区分,那类的成员变量和属性到底是什么关系又有什么区别呢?先看一下比较书面化的说法,"成员变量是就类的结构构成而言的概念,而属性是就类的功能逻辑而言的概念,两者紧密联系又相互区别"。说白点就是属性是业务上抽象出来的概念,并且属性的代码实现依赖于成员变量,但不能说属性就是成员变量,(好像也不是很直白...)并且属性在代码上可以做到控制读写权限,而成员变量就不可以了。属性的实现步骤继承自 yii\base\BaseObject 。声明一个用于保存该属性的私有成员变量。提供getter或setter函数,或两者都提供,用于访问、修改上面提到的私有成员变量。 如果只提供了getter,那么该属性为只读属性,只提供了setter,则为只写。属性的实现demo<?phpnamespace common\components;use yii\base\BaseObject;class Article extends BaseObject{ //成员变量 private $_val = '我是初始值'; //控制属性读权限的方法 public function getTitle() { return $this->_val; } //控制属性写权限的方法 public function setTitle($value) { $this->_val = $value; }}$obj = new Article();//获取属性文章标题的值$obj->title; //设置属性文章标题的值$obj->title = 'mysql从入门到删库'; 注意:上述demo中,$_val 就是成员变量 title 就是一个抽象出来的业务属性。从Yii2.0.13以后推荐继承的是 yii\base\BaseObject,而不是 yii\base\Object 因为在 php7.2 以后 Object 是保留字,不能再作为类名那么Yii是怎么通过getTitle()和setTitle()函数来控制属性title的读写的呢?其实很简单,通过__get()和__set()俩个魔术方法来实现的。 属性的实现原理下面是类BaseObject里面对于__set()和__get()魔术方法的实现。 public function __get($name) { $getter = 'get' . $name; if (method_exists($this, $getter)) { return $this->$getter(); } elseif (method_exists($this, 'set' . $name)) { throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); } throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); } public function __set($name, $value) { $setter = 'set' . $name; if (method_exists($this, $setter)) { $this->$setter($value); } elseif (method_exists($this, 'get' . $name)) { throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name); } else { throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name); } } 当我们读取类Article实例里面title值的时候,会去遍历类Article里面的属性,找不到匹配的属性的时候,就会自动触发__get()魔术方法[注:当调用当前环境下未定义或不可见的类属性时自动触发],在__get()方法里再去调用getTitle()方法。设置属性title同理。[注:由于PHP对于类方法不区分大小写,即大小写不敏感, $obj->getTitle() 和 $obj->gettitle() 是调用相同的函数] ...

May 26, 2019 · 1 min · jiezi

ThinkPHP51-源码浅析二自动加载机制

继 生命周期的第二篇,大家尽可放心,不会随便鸽文章的第一篇中,我们提到了入口脚本,也说了,里面注册了自动加载的功能 本文默认你有自动加载和命名空间的基础。如果没有请 看此篇文章 php 类的自动加载与命名空间自动加载机制php 的自动加载是 Loader 类中实现的,这个类在 base.php 中被引入 //base .php// 载入Loader类require __DIR__ . '/library/think/Loader.php';// 注册自动加载Loader::register();我们程序在这里执行了 Loader 中静态方法 ,同时这也是一个全部的类register() 我们进入 Loader.php ,按照上面执行顺序看看其核心是什么? register()方法执行流程 注册系统自动加载此方法行数过长,我们一点一点来分析 // 注册系统自动加载 spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);这就是注册我们的自动加载函数,$autoload 这个变量是传的参数,考虑到你可以自己实现自己的加载类,为了方便拓展,TP可以让你自己实现自己的类加载方法。 如果不了解这个函数的同学,请看文章最顶部的那个连接,上面有详细讲解。 Composer自动加载支持$rootPath = self::getRootPath(); self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR; // Composer自动加载支持 if (is_dir(self::$composerPath)) { if (is_file(self::$composerPath . 'autoload_static.php')) { require self::$composerPath . 'autoload_static.php'; // 获取当前加载的所有类 $declaredClass = get_declared_classes(); $composerClass = array_pop($declaredClass); foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) { if (property_exists($composerClass, $attr)) { self::${$attr} = $composerClass::${$attr}; } } } else { self::registerComposerLoader(self::$composerPath); } }为了支持 composer 拓展,在自动注册时候,把composer 也顺带一起注册了,方便对拓展的调用。 ...

May 23, 2019 · 3 min · jiezi

浅析Yii20的事件Event

概念理解:在某一个事件(trigger)发生的时候,触发预先设定(on)的代码,这是代码解耦的一种方式。事件按照级别分为三类1. 实例级别绑定事件、触发事件的类继承的是Component,只在当前示例中运行,不与类的其他实例发生关系,也不与其他类、其他实例发生关系。 代码示例:<?phpnamespace common\components;use yii\base\Component;class EventTest extends Component{ const EVENT_SAY_HELLO = 'say_hello'; public function sayHello($event) { echo $event->data; $event->handled = true; } public function sayWorld($event) { echo $event->data; }}1.绑定事件:$event = new EventTest();$event->on(EventTest::EVENT_SAY_HELLO, [$event, 'sayHello'], 'Hello!');$event->on(EventTest::EVENT_SAY_HELLO, [$event, 'sayWorld'], 'World!');2.解除事件:$event->off(EventTest::EVENT_SAY_HELLO, [$event, 'sayHello']);4.触发事件:$event->trigger( EventTest::EVENT_SAY_HELLO);2. 类级别绑定事件、触发事件的类继承的是Event,类级别事件会被类自身、类的实例、后代类、后代类实例的同一事件所触发,影响范围更大 代码示例:<?phpnamespace common\components;use yii\base\Event;class EventTest extends Event{ const EVENT_SAY_HELLO = 'say_hello'; public function sayHello($event) { echo $event->data; } public function sayWorld($event) { echo $event->data; }}1.绑定事件:$event = new EventTest();$event->on('\common\components\EventTest', EventTest::EVENT_SAY_HELLO, [$event, 'sayHello'], 'Hello!');$event->on('\common\components\EventTest', EventTest::EVENT_SAY_HELLO, [$event, 'sayWorld'], 'World!');2.解除事件:$event->off('\common\components\EventTest', EventTest::EVENT_SAY_HELLO, [$event, 'sayWorld']); 触发事件:$event->trigger('\common\components\EventTest', EventTest::EVENT_SAY_HELLO);类级别参数比实例级别多一个参数:类名注意:解除事件如果不传 handle,则会解除该事件下的所有 handle。在其中一个 handle 里面加上 $event->handled = true 也会阻止后续 handle 的触发,如上代码。3. 全局级别利用Application实例在整个应用的生命周期中全局可访问的特性,来实现这个全局事件的。在任意需要的时候,都可以触发全局事件,也可以在任意必要的时候绑定,或解除一个事件。 ...

May 23, 2019 · 1 min · jiezi

安装composer后报错procopen-fork-failed-Cannot-allocate-memory

1.问题描述:在linux服务器使用composer部署yii项目时,出现“proc_open(): fork failed - Cannot allocate memory” 也就是提示“提示内存不足”,我们可以通过创建swap分区解决这个问题。 2.解决方法:先运行 free -m 看下空间是多少在命令行环境依次运行以下三条命令dd if=/dev/zero of=/var/swap.1 bs=1M count=1024mkswap /var/swap.1swapon /var/swap.1举例:[以下是我在服务器运行的结果]dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 //第一条指令1024+0 records in //这个是返回的内容1024+0 records out //这个是返回的内容1073741824 bytes (1.1 GB) copied, 10.0607 s, 107 MB/s //这个是返回的内容mkswap /var/swap.1 //第二条指令Setting up swapspace version 1, size = 1048572 KiB //这个是返回的内容no label, UUID=f6280c41-21b4-4039-bc3e-c26284b47b0c //这个是返回的内容swapon /var/swap.1 //第三条指令swapon: /var/swap.1: insecure permissions 0644, 0600 suggested. //这个是返回的内容解释:dd 从/dev/zero设备复制出一个1G大小的文件/var/swap.1mkswap 格式化/var/swap.1swapon 将swap分区挂在到文件系统然后输入free -m 查看内存使用量信息最后再次执行composer update就成功了参考的博客有:http://www.zfsphp.com/31.html ...

May 15, 2019 · 1 min · jiezi

Yii-modules模块配置

在开发过程中,一个项目中,项目的功能可能会分为多个模块,例如,如果我们的项目为电商的管理控制系统的话,这时候我们的项目可能就会分为销售模块,采购模块,财务模块等等,这时候我们如果可以将项目按照一个个模块区分开来,在一个目录下专门写指定模块的代码,这样的话,我们的项目结构是不是就什么的清楚明了呢?在Yii中,我们可以使用modules来实现代码模块话 一:modules配置 在配置文件中main.php配置 //这里我配置了一个销售的模块和采购模块'modules' => [ 'sell'=> [ 'class' => 'frontend\modules\sell\Module', ], 'purchase'=> [ 'class' => 'frontend\modules\purchase\Module', ], ],二:创建模块目录(这里我只创建sell模块,其他模块创建方式相同) 上面我配置的modules实在frontend中,所有在frontend目录下创建一个modules目录,然后在modules目录下创建一个sell目录,在sell目录下有如下文件及文件夹 1:module.php(模块入口文件) <?phpnamespace frontend\modules\sell;class Module extends \common\components\Module{ public $controllerNamespace = 'frontend\modules\sell\controllers';}2:controllers目录 此目录下放置的是此模块所有的控制器方法 3:models目录 此目录放置的是此模块所需的模型 4:view目录 此目录放置的是此模块的视图文件 sell目录下文件结构如下: 根据如上方法我们就完成了modules配置使用

May 14, 2019 · 1 min · jiezi

PHP的apc扩展导致引入文件错误

最近遇到一个非常奇怪的bug,在主机PHP代码版本回退的过程中,导致备机服务不可用。经过各种复现和文档查询,发现是PHP的apc扩展在和rsync同时使用时,会导致无法正确的处理缓存文件,最终影响服务。解决方案官方也有提供,加上一行配置:# php.ini[apc]apc.stat_ctime=1下面我们来说明下这个问题出现的机制。关键点:使用了PHP+apc扩展+rsync主从同步机制故障表现:引入时找不到文件平台服务上线更新后,访问平台服务时报错信息:Warning: include(Yii.php): failed to open stream: No such file or directory in /home/disk4/htdocs/oss_debug/protected/lib/Yii/framework/YiiBase.php on line 421Warning: include(): Failed opening ‘Yii.php’ for inclusion (include_path=’.:/home/work/lnmp/weblib/phplib:/home/work/lnmp/lib/php’) in /home/disk4/htdocs/oss_debug/protected/lib/Yii/framework/YiiBase.php on line 421Fatal error: Class ‘Yii’ not found in /home/disk4/htdocs/oss_debug/index.php on line 42这里的提示信息表明,问题出现在YiiBase.php文件中,在421行引入Yii.php时找不到该文件,而这里的include为相对路径,当前的引入路径为.:/home/work/lnmp/weblib/phplib:/home/work/lnmp/lib/php,多个引入路径以:分割,所以这里会在./,/home/work/lnmp/weblib/phplib,/home/work/lnmp/lib/php三个目录下查找该文件,分别检索了一下,发现确实均不存在该文件。但是在正常的服务下,却并不会查找该文件。具体为什么会去查找该文件,我猜测是先判断Yii类是否存在,不存在就去引入Yii.php,而Yii类在yii.php文件中有定义,因此猜测是没有正确引入yii.php导致。# yii.php<?phprequire(dirname(FILE).’/YiiBase.php’);class Yii extends YiiBase{}这个问题没有深究,因为最后发现故障跟这个点无关。复现一个小问题:改变目录后无法服务你只需要将你的服务目录换个名字即可复现,如你当前的服务目录是/home/work/lnmp/htdocs/oss/,你将它重名为/home/work/lnmp/htdocs/oss2,这个时候你就会发现服务受到了影响:# 访问 domain.com/oss2/index.phpWarning: file_get_contents(/home/work/lnmp/htdocs/oss/version): failed to open stream: No such file or directory in /home/work/lnmp/htdocs/oss/index.php on line 26Warning: require_once(/home/work/lnmp/htdocs/oss/protected/lib/Yii/framework/yii.php): failed to open stream: No such file or directory in /home/work/lnmp/htdocs/oss/index.php on line 38Fatal error: require_once(): Failed opening required ‘/home/work/lnmp/htdocs/oss6/protected/lib/Yii/framework/yii.php’ (include_path=’.:/home/work/lnmp/weblib/phplib:/home/work/lnmp/lib/php’) in /home/work/lnmp/htdocs/oss6/index.php on line 38可以看到当我们访问oss2目录时,程序却依然在尝试读取oss目录下的文件,这时文件自然不存在,因此报错。那么这是为什么呢?原因是我们使用了PHP的apc扩展。PHP的服务过程学习过计算机原理的同学,都了解语言分为编译型语言和解释型语言,由于语言是人来编写的,而机器无法直接执行,因此,在代码被执行前需要经历一个编译成机器可以识别的操作码的过程。编译型语言在执行前提前编译好,然后发布;解释型语言先发布,在执行时即时编译。因此我们常说编译型语言的性能好,主要就是快在这个地方。PHP属于解释型语言,常规的执行流程是:Nginx转发请求给PHP主进程主进程引入代码文件PHP解释器会先将代码切分为Token生成抽象语法树生成机器可以直接执行的操作码PHP虚拟机执行操作码如果文件有引入其他文件,循环执行上述2-6步骤执行完成,返回结果可以看到每次请求过来,都会对文件做一次编译和缓存,那么这样会非常影响效率,为了保证PHP的灵活性,同时提升效率,我们需要对编译好的操作码进行缓存。这就是apc扩展做的事情:判断文件是否有更新如果更新,重新编译并缓存否则,直接读取缓存的操作码apc扩展apc扩展文档Alternative PHP Cache (APC 可选 PHP 缓存) 是一个开放自由的 PHP opcode 缓存。它的目标是提供一个自由、 开放,和健全的框架,用于缓存、优化 PHP 中间代码。该扩展也提供了一些内置的方法,可以用于手动设置或清空缓存。清空缓存的方法:apc_clear_cache()。调用这个方法后可以解决因apc缓存过期文件导致的bug。另外,我们需要关注的几个配置项:apc.stat integer是否启用脚本更新检查。 改变这个指令值要非常小心。 默认值 On 表示APC在每次请求脚本时都检查脚本是否被更新, 如果被更新则自动重新编译和缓存编译后的内容。但这样做对性能有不利影响。 如果设为 Off 则表示不进行检查,从而使性能得到大幅提高。 但是为了使更新的内容生效,你必须重启Web服务器(译者注:如果采用cgi/fcgi类似的,需重启cgi/fcgi进程)。 生产服务器上脚本文件很少更改, 可以通过禁用本选项获得显著的性能提升。这个指令对于include/require的文件同样有效。但是需要注意的是, 如果你使用的是相对路径,APC就必须在每一次include/require时都进行检查以定位文件。 而使用绝对路径则可以跳过检查,所以鼓励你使用绝对路径进行include/require操作。apc.stat_ctime integer验证ctime(创建时间)可以避免SVN或者rsync带来的问题,确保自上次缓存统计inode没有改变。APC通常只检查mtime(修改时间)。apc.file_update_protection integer当你在一个运行中的服务器上修改文件时,你应当执行原子操作。 也就是先写进一个临时文件,然后将该文件重命名(mv)到最终的名字。 文本编辑器以及 cp, tar 等程序却并不是这样操作的,从而导致有可能缓冲了残缺的文件。 默认值 2 表示在访问文件时如果发现修改时间距离访问时间小于 2 秒则不做缓冲。 那个不幸的访问者可能得到残缺的内容,但是这种坏影响却不会通过缓存扩大化。 如果你能确保所有的更新操作都是原子操作,那么可以用 0 关闭此特性。 如果你的系统由于大量的IO操作导致更新缓慢,你就需要增大此值。可以看到,apc扩展可能会导致两个问题:rsync/svn配合使用时存在无法正确处理文件缓存的问题可能读到残缺文件,导致影响部分人的请求针对这两个问题,也分别提供了解决方案:# php.ini[apc]# 启动ctime检查stat_ctime=1# 默认值为2,变大这个值file_update_protection=5虽然文档中有说明,但还是有很多人会遇到这种问题,可以参考:stack overflow问题:Problems with APC on publish官方Issue:apc.include_once_override turn on issue在遇到这个问题时,除了上面的配置解决问题,还可以:PHP代码中执行apc_clear_cache()重启php-fpm进程另外,我们可以将apc扩展安装时包含的apc.php文件放到web服务目录下,就可以可视化的观察apc扩展的缓存情况。服务使用了rsync同步这次故障的一个关键因素是使用了rsync同步,我的服务架构是:导致这个问题的原因探究具体为什么在apc扩展跟rsync同时使用会产生这个bug,我没有看源码,不太了解,但我做了一些大胆的猜测,下面的内容不够清楚和正确,希望大家能给我更精确的指导:这里可以看出文件是怎么检查是否有更新的,而问题也就出现在这一部分,没有办法判断文件是否被更新,同时正确读取到缓存的文件。参考资料PHP手册 - APC运行时配置:https://www.php.net/manual/zh…stack overflow - Problems with APC on publish:https://stackoverflow.com/que…PHP官方issue - apc.include_once_override turn on issue:https://bugs.php.net/bug.php?…php可选缓存APC:https://www.cnblogs.com/hf805…关于上线系统的一些想法 (for php):http://bikong0411.github.io/2…如何刷新APC类加载器缓存?:http://cn.voidcc.com/question…rsync文件同步服务:https://xdays.me/rsync%E6%96%…APC’s Include Once Override breaks install:https://www.drupal.org/projec…《PHP 7底层设计和源码实现》 ...

April 12, 2019 · 1 min · jiezi

php mongodb模糊搜索

以前一直使用mysql数据库,模糊搜索like关键字就能搞定。最近接入了mongodb平台,一时无法适应 ,踩了一些坑,在此记录下来,希望对其他人能够有用。1.mongodb对于普通非文本所有字段如何进行模糊搜索答案:使用正则表达式。对于过去经常使用mysql的同学可能不太适合,因为一想到正则表达式,我们就会想到对性能的影响,通常是能不用就不用,但是mongodb除了文本索引只能使用正则表达式进行模糊搜索。2.如何在php中使用正则表达式答案:MongoDBBSONRegex(php7使用了mongodb扩展),mongoregex(php7以前使用mongo扩展)3.两者在使用细节上的区别答案:MongoDBBSONRegex在构造regex对象时传入的字符串不需要前后的斜线,选项通过第二个参数传入$regex = new MongoRegex("/^$search/");$regex = new \MongoDB\BSON\Regex("^{$search}", ‘i’);当然了,如果要使用全文本索引mongodb也是支持的

April 2, 2019 · 1 min · jiezi

说说PHP框架的MVC架构

前言在说 MVC 架构之前,先说说PHP框架吧。很多很多学完PHP语言的人,面对的就是PHP各种各样的框架。什么TP啊、Yii啊、CI啊,还有很流行的laravel啊等等。他们的大部分都会说自己是基于 MVC 的架构,接着你得试着去理解 MVC 的逻辑,并尝试着用这样的逻辑去构建一个网站,然后会说 MVC 真香~面试很多 PHP 的面试中,可能会问关于 MVC 的问题,比如 MVC 到底是什么意思,怎样理解这种架构。然而很多人的理解是 model 是模型,他对应着数据库中的表结构;view 对应着页面,用于展示;controller 主要用来写各种逻辑,关联数据和页面的显示。以上回答基本上没有问题,但一个网站的结构真的有那么简单么?显然不是设计在说之前,首先让我们了解一下设计模式的一种:中介者模式。一个形象的理解就是:港行插头和国行插头的转接头。在 MVC 架构中 controller 就是这个转接头。它只负责把 model 中的数据转接给 view,对于访问者来说,他们是看不到 model 中保存的真实数据的。从另外一个角度来说,这种中介者模式可以很好的将两层数据进行友好的通信。爬坑这种模式真的那么好么?随着业务逻辑的越来越复杂,会发现 controller 中的代码越来越多,甚至自己都不愿去调整和优化冗余代码。但从宏观上来说,网站无非是请求多一些,表单多一些,页面多一些啊,其他也没什么了,为什么会这样呢?没错,就是因为这样或那样的东西比较多,导致 controller 中每个方法都很长,那么能想到的解决方法就是拆分。如果用过 yii 框架,那么你会知道最简单的办法是加一个请求form层,代码如下:class AuthController { public function login() { $FLogin = new loginForm(); $FLogin->save(); }}// 一般在独立的文件夹中class loginForm { public function __construct() { $post = $_POST; } public function save() { }}以上的就是解决 controller 中 form 表单的问题,这个问题基本上能缓解很多代码问题。发散从解决 form 层来看,其实有很多类似的问题都能解决。我们知道前端有个叫做 vue.js 的框架,它里面提到一个概念叫做 MVVM 模型。其实在展现复杂页面的时候,后端在对外输出数据时,完全也可以采用这玩意进行数据输出。至于如何建立这样的一个模型,那就具体得看业务逻辑了。这里简单拿用户中心举个例子,因为往往这里不仅仅需要一个表的数据:class AuthController { public function userCenterAction() { return new userVM(); }}class userVM { public $user; public $orders; public $other; public function __construct() { $this->user = $this->getUser(); $this->orders = $this->getOrders(); $this->handle(); } private function getUser() { return NULL; } private function getOrders() { return NULL; } private function handle() { }}以上代码中,有个 VM 层,可以将相关获取数据的代码放在各自的方法中,然后在 handle 方法中自由组合。这样在 controller 中的代码也非常便于管理。再想想,有没有可以封装的其他层呢?其实是有的,比如 request 层,还有经常被框架封装好的 validate 层,还有 laravel 中比较流行的 Middleware 层等等。只能说系统越复杂,层越多。每个复杂系统的背后都蕴含着高级开发工程师和架构师的设计思路。以上说那么多,不知道读者能否理解这些东西,就拿以上代码来说,里面就蕴含着另一种设计模式:建造者模式。总结代码写多了,也就知道其中蕴含的道理了。当一个新框架诞生后,关注点从学习这个框架,慢慢变成了这个框架是如何设计的,解决什么样的问题。哪些地方用了比较好的技术和方法,从中能收获到什么。一些地方的设计思路是什么样的,有么有更好的设计,为何我能想到,对方想不到呢,是不是我遗漏了什么。前几年使用过各种 PHP 框架,小到 CI,大到 Symfony。不用那么多框架,也体会不到这些东西。学习编程其实和英语一样,没什么捷径可以走。多写,多想,多练……以上 ...

March 13, 2019 · 1 min · jiezi

Yii2.0 RESTful API 认证教程【令牌验证】

最近在做RESTful API认证功能,记录整个过程,方便以后查看。本文参照了 https://segmentfault.com/a/1190000016368603部分内容,感谢该作者的分享,以下内容根据我的项目实际情况进行了调整。认证介绍和Web应用不同,RESTful APIs 通常是无状态的, 也就意味着不应使用 sessions 或 cookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过 sessions 或 cookies 维护, 常用的做法是每个请求都发送一个秘密的 access token 来认证用户, 由于 access token 可以唯一识别和认证用户,API 请求应通过 HTTPS 来防止man-in-the-middle (MitM) 中间人攻击.认证方式HTTP 基本认证 :access token 当作用户名发送,应用在access token可安全存在API使用端的场景, 例如,API使用端是运行在一台服务器上的程序。请求参数: access token 当作API URL请求参数发送,例如 https://example.com/users?acc…, 由于大多数服务器都会保存请求参数到日志, 这种方式应主要用于JSONP 请求,因为它不能使用HTTP头来发送 access tokenOAuth 2 : 使用者从认证服务器上获取基于 OAuth2 协议的 access token, 然后通过 HTTP Bearer Tokens 发送到 API 服务器。上方进行简单介绍,内容来自 Yii Framework 2.0 权威指南实现步骤继续上一篇 的内容(这里暂时使用默认User数据表,正式环境请分离不同的数据表来进行认证)需要添加的数据内容继上篇的 User 数据表,我们还需要增加一 个 access_token 和 expire_at 的字段,进入项目根目录打开控制台输入以下命令:./yii migrate/create add_column_access_token_to_user./yii migrate/create add_column_expire_at_to_user打开 你的项目目录 /console/migrations/m181224_075747_add_column_access_token_user.php 修改如下内容: public function up() { $ret = $this->db->createCommand(“SELECT * FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ‘user’ AND column_name = ‘access_token’”)->queryOne();//判断user表是否有’access_token’这个字段 if (empty($ret)) { $this->addColumn(‘user’, ‘access_token’, $this->string(255)->defaultValue(NULL)->comment(‘令牌’)); } } public function down() { $this->dropColumn(‘user’, ‘access_token’); return true; }打开 你的项目目录 /console/migrations/m181224_092333_add_column_expire_at_user.php 修改如下内容: public function up() { $ret = $this->db->createCommand(“SELECT * FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ‘user’ AND column_name = ’expire_at’”)->queryOne(); if (empty($ret)) { $this->addColumn(‘user’, ’expire_at’, $this->integer(11)->defaultValue(NULL)->comment(‘令牌过期时间’)); } } public function down() { $this->dropColumn(‘user’, ’expire_at’); return true; }执行迁移命令./yii migrate配置打开 api\config\main.php配置 user 应用组件:‘user’ => [ ‘identityClass’ => ‘api\models\User’, ’enableAutoLogin’ => true, ’enableSession’=>false, //‘identityCookie’ => [’name’ => ‘_identity-api’, ‘httpOnly’ => true], ],将 session 组件注释掉,或删掉// ‘session’ => [// // this is the name of the session cookie used for login on the backend// ’name’ => ‘advanced-api’,// ],编写 api\models\User.php 实现认证类,继承 IdentityInterface将 common\models\User 类拷贝到 api\models\ 目录下,修改命名空间为 api\models<?phpnamespace api\models;use Yii;use yii\base\NotSupportedException;use yii\behaviors\TimestampBehavior;use yii\db\ActiveRecord;use yii\web\IdentityInterface;…class User extends ActiveRecord implements IdentityInterface{ … …}将 commonmodelsLoginForm.php 类拷贝到 apimodels* 目录下,修改命名空间,并重写 login* 方法:<?phpnamespace api\models;use Yii;use yii\base\Model;……const EXPIRE_TIME = 604800;//令牌过期时间,7天有效public function login(){ if ($this->validate()) { //return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); if ($this->getUser()) { $access_token = $this->_user->generateAccessToken(); $this->_user->expire_at = time() + static::EXPIRE_TIME; $this->_user->save(); Yii::$app->user->login($this->_user, static::EXPIRE_TIME); return $access_token; } } return false;}上方代码给 User 模型添加了一个 generateAccessToken() 方法,因此我们到 api\models\User.php 中添加此方法namespace api\models;use Yii;use yii\base\NotSupportedException;use yii\behaviors\TimestampBehavior;use yii\db\ActiveRecord;use yii\web\IdentityInterface;use yii\web\UnauthorizedHttpException;……class User extends ActiveRecord implements IdentityInterface{ … … /** * 生成accessToken字符串 * @return string * @throws \yii\base\Exception / public function generateAccessToken() { $this->access_token=Yii::$app->security->generateRandomString(); return $this->access_token; }}接下来在api\controllers\新加一个控制器 命名为 UserController 并继承 yii\rest\ActiveController,编写登录 Login 方法,具体代码如下:namespace api\controllers;use api\models\LoginForm;use yii\rest\ActiveController;use yii;class UserController extends ActiveController{ public $modelClass = ‘api\models\User’; public function actions() { $action= parent::actions(); // TODO: Change the autogenerated stub unset($action[‘index’]); unset($action[‘create’]); unset($action[‘update’]); unset($action[‘delete’]); } public function actionIndex() { //你的代码 } /* * 登陆 * @return array * @throws \yii\base\Exception * @throws \yii\base\InvalidConfigException / public function actionLogin() { $model = new LoginForm(); if ($model->load(Yii::$app->getRequest()->getBodyParams(), ‘’) && $model->login()) { return [ ‘access_token’ => $model->login(), ]; } else { return $model->getFirstErrors(); } }}最后新增一条 URL 规则打开 api\config\main.php 修改 components 属性,添加下列代码:‘urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’, ’extraPatterns’=>[ ‘POST login’=>’login’, ], ], ],]使用一个调试工具来进行测试 http://youdomain/users/login 记住是POST 请求发送,假如用POSTMAN有问题的话指定一下 Content-Type:application/x-www-form-urlencoded 。ok,不出意外的话,相信你已经可以收到一个access_token 了,接下来就是如何使用这个token,如何维持认证状态,达到不携带这个token将无法访问,返回 401维持认证状态实现认证步骤:在你的 REST 控制器类中配置 authenticator 行为来指定使用哪种认证方式在你的 user identity class 类中实现 yiiwebIdentityInterface::findIdentityByAccessToken() 方法.具体实现方式如下:打开之前的 User 控制器( api\controllers\UserController.php ),增加以下内容:use yii\helpers\ArrayHelper;use yii\filters\auth\QueryParamAuth;…//此处省略一些代码了 public function behaviors() { return ArrayHelper::merge(parent::behaviors(), [ ‘authenticatior’ => [ ‘class’ => QueryParamAuth::className(), //实现access token认证 ’except’ => [’login’], //无需验证access token的方法,注意区分$noAclLogin ] ]); }… 实现 findIdentityByAccessToken() 方法:打开 api\models\User.php 重写 findIdentityByAccessToken() 方法…use yii\web\UnauthorizedHttpException;…class User extends ActiveRecord implements IdentityInterface{ … … /* * {@inheritdoc} */ public static function findIdentityByAccessToken($token, $type = null) {// throw new NotSupportedException(’“findIdentityByAccessToken” is not implemented.’); $user = static::find()->where([‘access_token’ => $token, ‘status’ => self::STATUS_ACTIVE])->one(); if (!$user) { return false; } if ($user->expire_at < time()) { throw new UnauthorizedHttpException(’the access - token expired ‘, -1); } else { return $user; } } …}打开 api\controllers\UserController.php ,增加 Test方法,用于测试令牌验证 public function actionTest() { return [‘status’=>‘success’]; }修改 api\config\main.php ‘urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’, //‘pluralize’ => false, //设置为false 就可以去掉复数形式了 ’extraPatterns’=>[ ‘GET test’=>’test’, ‘POST login’=>’login’, ], ], ], ]接下来访问一下你的域名 http://youdomain/users/test,不携带任何参数是不是返回 401了?ok,这里介绍两种访问方式,一种是URL访问,另一种是通过header 来进行携带http://youdomain/users/test?a…传递 header 头信息Authorization:Bearer YYdpiZna0hJGhjsfqwxUeHEgLDfHEjB- 注意 Bearer 和你的token中间是有 一个空格的,很多同学在这个上面碰了很多次 以上就是基于YII2.0 RESTful 认证的内容。本文参照了 https://segmentfault.com/a/1190000016368603部分内容,感谢该作者的分享,以上内容根据我的项目实际情况进行了调整。 ...

December 25, 2018 · 3 min · jiezi

Yii2.0 RESTful API 基础配置教程[转载]

最近在做Yii2.0 RESTful API功能,找了好久的资料,才找到这类的教程,感谢该作者,以下内容根据我的项目实际情况做了一定的修改。安装yii2.0安装 Composer 后,您可以通过在 Web 可访问的文件夹下运行以下命令来 安装Yii应用程序模板:composer create-project –prefer-dist yiisoft/yii2-app-basic basic初始化高级模板cd advanced./init输入"./init"后会出现以下内容Yii Application Initialization Tool v1.0Which environment do you want the application to be initialized in? [0] Development [1] Production Your choice [0-1, or “q” to quit] 0 Initialize the application under ‘Development’ environment? [yes|no] yes Start initialization …修改数据库连接属性打开 commonconfigmain-local.php,配置数据库连接信息’db’ => [ ‘class’ => ‘yii\db\Connection’, ‘dsn’ => ‘mysql:host=127.0.0.1;dbname=yii’, ‘username’ => ‘root’, ‘password’ => ‘root’, ‘charset’ => ‘utf8’, ],执行 migrate 数据库迁移./yii migrate拷贝backend目录,命名为api打开apiconfigmain.php 修改id,controllerNamespace等,将所有backend替换为api:return [ ‘id’ => ‘app-api’, ‘basePath’ => dirname(DIR), ‘controllerNamespace’ => ‘api\controllers’,]打开apiconfigmain.php开启url路由美化规则’urlManager’ => [ ’enablePrettyUrl’ => true, ‘showScriptName’ => false, ‘rules’ => [ ], ],打开commonconfigbootstrap.php添加以下别名Yii::setAlias(’@api’, dirname(dirname(DIR)) . ‘/api’);为什么要单独创建API应用单独创建API应用,目的是便于维护,可以避免以下问题配置的冲突控制器的命名不便url美化规则冲突分工明确frontend为前台目录;backend为后台目录;api为api目录接下来打开 apicontrollers 新建一个User控制器,继承 yiirestActiveController,命名为 UserController,代码如下:<?phpnamespace api\controllers;use yii\rest\ActiveController;class UserController extends ActiveController{ public $modelClass = ‘common\models\User’; }配置URL规则为刚才的 user控制器添加url美化规则打开 apiconfigmain.php 修改 components属性,添加下列代码:…‘urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’ ], ],]…ok,到此就成了一个 符合 RESTful 风格的API看起来在控制器了什么也没有写,只是指定了一个模型,但是她的背后完成了很多的功能哦,列表如下:GET /users: 逐页列出所有用户HEAD /users: 显示用户列表的概要信息POST /users: 创建一个新用户GET /users/123: 返回用户 123 的详细信息HEAD /users/123: 显示用户 123 的概述信息PATCH /users/123: and PUT /users/123: 更新用户123DELETE /users/123: 删除用户123OPTIONS /users: 显示关于末端 /users 支持的动词OPTIONS /users/123: 显示有关末端 /users/123 支持的动词如何访问呢你可以使用 curl命令进行访问,命令如下:curl -i -H “Accept:application/json” “http://localhost/users"命令行下还是比较麻烦的,也不方便测试,推荐使用 API测试工具这类的工具有很多,我就不一一列举了,这里推荐 Postman,很好很强大,Chorme也有插件,可以安装,这里我推荐直接下载软件安装调试,比较方便你可能发现了 访问任何路由地址都是加的s,users , 为什么呢? 资源,你要理解 资源二字,既然是资源肯定是个集合,肯定有一大堆,所以要加上复数,我是这么理解的。你说我就是不想加上s,我就想采用http://localhost/user 这种方式来进行访问,好吧,可以,满足你,只是不推荐继续打开配置文件apiconfigmain.php修改刚才添加的 urlManager 如下:‘urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’, ‘pluralize’ => false, //设置为false 就可以去掉复数形式了 ], ],]加入 ‘pluralize’ => false, 就表示去掉复数形式了,再次强调不推荐ok,在控制器中我们没有写任何一句代码,他就给我们生成许多方法,但是有时候我们可能需要修改一些代码,来达到我们想要的效果,比如连表查询,然后再返回数据接下来我们就实现这样的功能:打开刚才新建的user控制器, 重写 action方法:<?phpnamespace api\controllers;use yii\rest\ActiveController;class UserController extend extends ActiveController{ public $modelClass = ‘common\models\User’; public function actions() { $action= parent::actions(); // TODO: Change the autogenerated stub unset($action[‘index’]); unset($action[‘create’]); unset($action[‘update’]); unset($action[‘delete’]); } public function actionIndex() { //你的代码 } }这样我们就可以重写他的代码了。哈哈我们再新建一个自己的 action<?phpnamespace api\controllers;use yii\rest\ActiveController;class UserController extends ActiveController{ public $modelClass = ‘common\models\User’; public function actions() { $action= parent::actions(); // TODO: Change the autogenerated stub unset($action[‘index’]); unset($action[‘create’]); unset($action[‘update’]); unset($action[‘delete’]); } public function actionIndex() { //你的代码 } public function actionTest() //假如是get请求 { //业务逻辑 } }然后试着访问一下 http://localhost/users/test,报错?找不到?报错就对了,那是因为我们没有设置其他路由访问修改 apiconfigmain.php’urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’, //‘pluralize’ => false, //设置为false 就可以去掉复数形式了 ’extraPatterns’=>[ ‘GET send-email’=>’test’ ], ], ],]接下来重新访问就没有问题了,ps:你自己编写的任何 action 都要在 extraPatterns 进行配置再次感谢以下:https://segmentfault.com/a/11… ...

December 24, 2018 · 2 min · jiezi

在yii2中,让你action参数支持POST数据的小方法

我们先来看一段代码class RaController extends Controller { public $enableCsrfValidation = false; public function actionSay($username = ‘’,$city = ‘’){ echo “{$username} 来自 {$city}”; }}这里actionSay对应的url为index.php?r=ra/say,而 $username 和 $city 值的获取来自于url的参数,比如index.php?r=ra/say&username=abei2017&city=洛阳总结 在yii2中,action参数都是来自于GET。但是有的时候你可能需要让action的参数来自于POST请求,怎么办?重载runAction即可,yii2为控制器提供了runAction方法,它负责生成一个具体的Action对象并传递参数,我们可以通过复写它来实现,你可以看下yii2的生命周期来对其进行更好的了解。那就开始干吧~,对上面的代码复写runActionclass RaController extends Controller { public $enableCsrfValidation = false; public function runAction($id, $params = []){ $params = ArrayHelper::merge(Yii::$app->request->post(),$params); return parent::runAction($id, $params); } public function actionSay($username = ‘’,$city = ‘’){ echo “{$username} 来自 {$city}”; }}复写了runAction后,它将作用于此控制器的所有action,当然你也可以通过runAction的$id来作用于某个action。比如public function runAction($id, $params = []){ if($id == ‘say’){ $params = ArrayHelper::merge(Yii::$app->request->post(),$params); } return parent::runAction($id, $params);}现在去试试吧,对say动作提交POST数据username和city,成功接收。 ...

September 26, 2018 · 1 min · jiezi