
加油,乌克兰

今天浏览项目代码,在项目自动加载注册的路由方法发现前辈判断拼接的控制器文件是否存在时,用的是file_exists($file),而我个人风格更偏向于is_file(),为了弄清楚两个函数哪个性能更优,查阅了官方手册和一些博客文章。
PHP手册描述如下:
file_exists — 检查文件或目录是否存在
is_file — 判断给定文件名是否为一个正常的文件
由此我们知道,在功能上file_exists=is_file+is_dir,相当于后两者的集合,既可以判断文件是否存在,又可判断目录是否存在,那么这两个都可以判断文件是否存在的函数其性能哪个占优呢?
写程序验证一下(程序见附件):
分别执行1000次,记录所需时间。
文件存在(当前目录)
is_file:0.4570ms
file_exists:2.0640ms
文件存在(绝对路径3层/www/hx/a/)
is_file:0.4909ms
file_exists:3.3500ms
文件存在(绝对路径5层/www/hx/a/b/c/)
is_file:0.4961ms
file_exists:4.2100ms
文件不存在(当前目录)
is_file:2.0170ms
file_exists:1.9848ms
文件不存在(绝对路径5层/www/hx/a/b/c/)
is_file:4.1909ms
file_exists:4.1502ms
目录存在
file_exists:2.9271ms
is_dir:0.4601ms
目录不存在
file_exists:2.9719ms
is_dir:2.9359ms
is_file($file)
file_exists($file)
当$file是目录时,is_file返回false,file_exists返回true
文件存在的情况下,is_file比file_exists要快得多;
要检测文件所在的目录越深,速度差越多,但至少快4倍。
文件不存在的情况下,is_file比file_exists要慢一点点,但可以忽略不计。
目录存在的情况下,is_dir比file_exists要快得多;
目录不存在的情况下,is_dir比file_exists要慢一点点,但可以忽略不计。
结论:
如果要判断文件是否存在,用函数 is_file(),
如果要判断目录是否存在,用函数 is_dir(),
好像没地方需要用file_exists了,不确定传入的参数是文件还是目录的时候用。
—————————————-测试代码————————————————-
function runtime($t1){
return number_format((microtime(true) – $t1)*1000, 4).’ms’;
}
$times = 1000;
$t1 = microtime(true);
for($i=0;$i<$times;$i++){
is_file(‘/www/hx/www.9enjoy.com/config.php’);
}
echo ‘<br>is_file:’.runtime($t1);
$t2 = microtime(true);
for($i=0;$i<$times;$i++){
file_exists(‘/www/hx/www.9enjoy.com/config.php’);
}
echo ‘<br>file_exists:’.runtime($t2);
/*
$t3 = microtime(true);
for($i=0;$i<$times;$i++){
is_dir(‘/www/hx/www.9enjoy.com/’);
}
echo ‘<br>is_dir:’.runtime($t3);
*/
前几天写api,前端发过来的参数约定是数字1-3,按照开发文档,我写了如下代码,
if (!in_array($param, range(0, 3))) { $vo['error_code'] = 101; return $vo; }
后来自己单元测试,发现发送模拟参数’1abc’和’abc’,居然没返回错误码(报错),经查阅PHP手册,得知该函数还有第三个参数,是否进行参数类型比较,默认为FALSE,这就导致字符串’1abc’在和整型int比较时,由于PHP是弱类型语言,会发生隐式类型转换,所以这里’1abc’转换为’1’,同理‘abc’转换为字符串类型’0’,从而返回true;
要避免这个坑,有以下两个方法
1.自行进行强制类型验证
if (!is_int($param) || !in_array($param, range(0, 3))) { $vo['error_code'] = 101; return $vo; }
2.填充in_array()第三个参数为True,函数内部则会进一步检查类型是否相同
if (!in_array($param, range(1, 3),True)) { $vo['error_code'] = 101; return $vo; }
主要用在函数名不确定,参数不确定的情况下,它体现了一种通过传递不同参数来调用不同方法的思想
1.直接调用方法
function say($word){
echo $word;
}
call_user_func_array(‘say’,array(‘Hello PHP’));
/*第一个参数为要调用的方法名,字符串形式,第二个参数为要传给方法的参数,数组形式*/
/*
*这如果say()方法不存在,通过call_user_func_array调用后只会返回false,
*而如果采用直接调用的形式:say(),则会直接报错
*/
2.调用对象中的方法
class S{
public function __construct(){
}
public static function say1($word){
echo $word;
}
public function say($word){
echo $word;
}
}
/*使用方式一:无需实例化调用类的静态方法*/
call_user_func_array(array(‘S’,’say1′),array(‘Hello PHP’));
/*使用方式二:不实例化调用类的非静态方法*/
call_user_func_array(array(‘S’,’say2′),array(‘Hello PHP’));
/*这里不会对自动实例化类,如果调用的方法中有$this语句,会报错,一般不这么用*/
/*使用方式三:实例化后调用类的非静态方法*/
$s = new S();
call_user_func_array(array($s,’say2′),array(‘Hello PHP’));
call_user_func_array(array(&$s,’say2′),array(‘Hello PHP’));/*&这个符号有或没有貌似没差别*/
call_user_func_array(array(new S(),’say2′),array(‘Hello PHP’));
这个也算是自己在编码中碰到的一个坑
1、键名为数字(数字型字符串)时,array_merge()不会覆盖掉原来的值,但“+”合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉
$arr1 = array('a','b'); $arr2 = array('c', 'd'); var_dump($arr1+$arr2); //输出 array(2) { [0]=> string(1) "a" [1]=> string(1) "b" } var_dump(array_merge($arr1, $arr2)); //输出 array(4) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" [3]=> string(1) "d" } 2、键名为字符(非数字型字符)时,+仍然把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉,但array_merge()此时会覆盖掉前面相同键名的值
$arr1 = array(0 => 'a', 'ab' => 'b'); $arr2 = array('0' => 'c', 'ab' => 'd'); var_dump($arr1+$arr2); //输出 array(2) { [0]=> string(1) "a" ["ab"]=> string(1) "b" } var_dump(array_merge($arr1, $arr2)); array(3) { [0]=> string(1) "a" ["ab"]=> string(1) "d" [1]=> string(1) "c" }
注:文章转载自http://www.laravel-vue.xyz
PHP 反射机制,对类、接口、函数、方法和扩展进行反向工程的能力。
分析类,接口,函数和方法的内部结构,方法和函数的参数,以及类的属性和方法。
反射中常用的几个类:
分析类:
class Student { public $id; public $name; const MAX_AGE = 200; public static $likes = []; public function __construct($id, $name = 'li') { $this->id = $id; $this->name = $name; } public function study() { echo 'learning...'; } private function _foo() { echo 'foo'; } protected function bar($to, $from = 'zh') { echo 'bar'; } } |
$ref = new ReflectionClass('Student'); // 判断类是否可实例化 if ($ref->isInstantiable()) { echo '可实例化'; } // 获取类构造函数 // 有返回 ReflectionMethod 对象,没有返回 NULL $constructor = $ref->getConstructor(); print_r($constructor); // 获取某个属性 if ($ref->hasProperty('id')) { $attr = $ref->getProperty('id'); print_r($attr); } // 获取属性列表 $attributes = $ref->getProperties(); foreach ($attributes as $row) { // 这里的 $row 为 ReflectionProperty 的实例 echo $row->getName() , "\n"; } // 获取静态属性,返回数组 $static = $ref->getStaticProperties(); print_r($static); // 获取某个常量 if ($ref->hasConstant('MAX_AGE')) { $const = $ref->getConstant('MAX_AGE'); echo $const; } // 获取常量,返回数组 $constants = $ref->getConstants(); print_r($constants); // 获取某个方法 if ($ref->hasMethod('bar')) { $method = $ref->getMethod('bar'); print_r($method); } // 获取方法列表 $methods = $ref->getMethods(); foreach ($methods as $key => $value) { // 这里的 $row 为 ReflectionMethod 的实例 echo $value->getName() . "\n"; } |
if ($ref->hasProperty('name')) { $attr = $ref->getProperty('name'); // 属性名称 echo $attr->getName(); // 类定义时属性为真,运行时添加的属性为假 var_dump($attr->isDefault()); // 判断属性访问权限 var_dump($attr->isPrivate()); var_dump($attr->isProtected()); var_dump($attr->isPublic()); // 判断属性是否为静态 var_dump($attr->isStatic()); } |
if ($ref->hasMethod('bar')) { $method = $ref->getMethod('bar'); echo $method->getName(); // isAbstract 判断是否是抽象方法 //isConstructor 判断是否是构造方法 //isDestructor 判断是否是析构方法 //isFinal 判断是否是 final 描述的方法 //isPrivate 判断是否是 private 描述的方法 //isProtected 判断是否是 protected 描述的方法 //isPublic 判断是否是 public 描述的方法 //isStatic 判断是否是 static 描述的方法 // 获取参数列表 $parameters = $method->getParameters(); foreach ($parameters as $row) { // 这里的 $row 为 ReflectionParameter 实例 echo $row->getName(); echo $row->getClass(); // 检查变量是否有默认值 if ($row->isDefaultValueAvailable()) { echo $row->getDefaultValue(); } // 获取变量类型 if ($row->hasType()) { echo $row->getType(); } } } |
$fun = new ReflectionFunction('demo'); echo $fun->getName(); $parameters = $fun->getParameters(); foreach ($parameters as $row) { // 这里的 $row 为 ReflectionParameter 实例 echo $row->getName(); echo $row->getClass(); // 检查变量是否有默认值 if ($row->isDefaultValueAvailable()) { echo $row->getDefaultValue(); } // 获取变量类型 if ($row->hasType()) { echo $row->getType(); } } |
下面用一个简单的示例:如果用反射实例化类。
file: Student.php
class Student { public $id; public $name; public function __construct($id, $name) { $this->id = $id; $this->name = $name; } public function study() { echo 'learning.....'; } } |
一般情况下,实例化类的时候,直接使用 new
,但是我们现在不用这种方法,我们使用反射来实现。
file: index.php
require 'student.php'; function make($class, $vars = []) { $ref = new ReflectionClass($class); // 检查类 Student 是否可实例化 if ($ref->isInstantiable()) { // 获取构造函数 $constructor = $ref->getConstructor(); // 没有构造函数的话,直接实例化 if (is_null($constructor)) { return new $class; } // 获取构造函数参数 $params = $constructor->getParameters(); $resolveParams = []; foreach ($params as $key => $value) { $name = $value->getName(); if (isset($vars[$name])) { // 判断如果是传递的参数,直接使用传递参数 $resolveParams[] = $vars[$name]; } else { // 没有传递参数的话,检查是否有默认值,没有默认值的话,按照类名进行递归解析 $default = $value->isDefaultValueAvailable() ? $value->getDefaultValue() : null; if (is_null($default)) { if ($value->getClass()) { $resolveParams[] = make($value->getClass()->getName(), $vars); } else { throw new Exception("{$name} 没有传值且没有默认值"); } } else { $resolveParams[] = $default; } } } // 根据参数实例化 return $ref->newInstanceArgs($resolveParams); } else { throw new Exception("类 {$class} 不存在!"); } } ## 情况一 try { $stu = make('Student', ['id' => 1]); print_r($stu); $stu->study(); } catch (Exception $e) { echo $e->getMessage(); } ## 情况二 try { $stu = make('Student', ['id' => 1, 'name' => 'li']); print_r($stu); $stu->study(); } catch (Exception $e) { echo $e->getMessage(); } |
上面两种情况很明显第一种,缺少参数 name
,无法实例化成功,第二种情况就可以实例化成功。
那么我们如果将类 Student
的构造函数修改为:
public function __construct($id, $name = 'zhang') { $this->id = $id; $this->name = $name; } |
这样设置 name
有默认值的情况下,那么第一种情况也可以实例化成功。
第三种情况:如果在类的构造函数中有其他类为参数的情况下,那么也可以解析:
public function __construct($id, $name, Study $study) { $this->id = $id; $this->name = $name; $this->study = $study; } |
那么这种情况下,在分析类的构造函数参数的时候,如果没有传递参数的话,就会递归调用 make
方法处理 Study
类,如果类存在的话,实例化。
file: study.php
// 我们这里不写构造函数,测试下没有构造函数的情况 class Study { public function show() { echo 'show'; } } |
将 Student
类的方法 study
修改为:
1 2 3 4 |
public function study() { $this->name . ' ' . $this->study->show(); } |
下面测试:
try { $stu = make('Student', ['id' => 1]); print_r($stu); $stu->study(); } catch (Exception $e) { echo $e->getMessage(); } |
PHP 的反射是一个很用的功能,我这里只能很简单的讲解了一点皮毛,详细介绍和用法可参看 官方手册。
本文由 yangweijie 翻译自clen php code,团建用,欢迎大家指正。
摘录自 Robert C. Martin的Clean Code 书中的软件工程师的原则 ,适用于PHP。 这不是风格指南。 这是一个关于开发可读、可复用并且可重构的PHP软件指南。
并不是这里所有的原则都得遵循,甚至很少的能被普遍接受。 这些虽然只是指导,但是都是Clean Code作者多年总结出来的。
Inspired from clean-code-javascript
Bad:
$ymdstr = $moment->format('y-m-d');
Good:
$currentDate = $moment->format('y-m-d');
Bad:
getUserInfo();
getClientData();
getCustomerRecord();
Good:
getUser();
我们会读比写要多的代码。通过是命名易搜索,让我们写出可读性和易搜索代码很重要。
Bad:
// What the heck is 86400 for?
addExpireAt(86400);
Good:
// Declare them as capitalized `const` globals.
interface DateGlobal {
const SECONDS_IN_A_DAY = 86400;
}
addExpireAt(DateGlobal::SECONDS_IN_A_DAY);
Bad:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
saveCityZipCode($matches[1], $matches[2]);
Good:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);
list(, $city, $zipCode) = $matchers;
saveCityZipCode($city, $zipCode);
明确比隐性好。
Bad:
$l = ['Austin', 'New York', 'San Francisco'];
foreach($i=0; $i<count($l); $i++) {
oStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// 等等`$l` 又代表什么?
dispatch($l);
}
Good:
$locations = ['Austin', 'New York', 'San Francisco'];
foreach($i=0; $i<count($locations); $i++) {
$location = $locations[$i];
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch($location);
});
如果你的class/object 名能告诉你什么,不要把它重复在你变量名里。
Bad:
$car = [
'carMake' => 'Honda',
'carModel' => 'Accord',
'carColor' => 'Blue',
];
function paintCar(&$car) {
$car['carColor'] = 'Red';
}
Good:
$car = [
'make' => 'Honda',
'model' => 'Accord',
'color' => 'Blue',
];
function paintCar(&$car) {
$car['color'] = 'Red';
}
###使用参数默认值代替短路或条件语句。 Bad:
function createMicrobrewery($name = null) {
$breweryName = $name ?: 'Hipster Brew Co.';
// ...
}
Good:
function createMicrobrewery($breweryName = 'Hipster Brew Co.') {
// ...
}
限制函数参数个数极其重要因为它是你函数测试容易点。有超过3个可选参数参数导致一个爆炸式组合增长,你会有成吨独立参数情形要测试。
无参数是理想情况。1个或2个都可以,最好避免3个。再多旧需要加固了。通常如果你的函数有超过两个参数,说明他多做了一些事。 在参数少的情况里,大多数时候一个高级别对象(数组)作为参数就足够应付。
Bad:
function createMenu($title, $body, $buttonText, $cancellable) {
// ...
}
Good:
class menuConfig() {
public $title;
public $body;
public $buttonText;
public $cancellable = false;
}
$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = true;
function createMenu(MenuConfig $config) {
// ...
}
这是迄今为止软件工程里最重要的一个规则。当函数做超过一件事的时候,他们就难于实现、测试和理解。当你隔离函数只剩一个功能时,他们就容易被重构,然后你的代码读起来就更清晰。如果你光遵循这条规则,你就领先于大多数开发者了。
Bad:
function emailClients($clients) {
foreach ($clients as $client) {
$clientRecord = $db->find($client);
if($clientRecord->isActive()) {
email($client);
}
}
}
Good:
function emailClients($clients) {
$activeClients = activeClients($clients);
array_walk($activeClients, 'email');
}
function activeClients($clients) {
return array_filter($clients, 'isClientActive');
}
function isClientActive($client) {
$clientRecord = $db->find($client);
return $clientRecord->isActive();
}
Bad:
function addToDate($date, $month) {
// ...
}
$date = new \DateTime();
// It's hard to to tell from the function name what is added
addToDate($date, 1);
Good:
function addMonthToDate($month, $date) {
// ...
}
$date = new \DateTime();
addMonthToDate(1, $date);
Bad:
function parseBetterJSAlternative($code) {
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach($regexes as $regex) {
foreach($statements as $statement) {
// ...
}
}
$ast = [];
foreach($tokens as $token) {
// lex...
}
foreach($ast as $node) {
// parse...
}
}
Good:
function tokenize($code) {
$regexes = [
// ...
];
$statements = split(' ', $code);
$tokens = [];
foreach($regexes as $regex) {
foreach($statements as $statement) {
$tokens[] = /* ... */;
});
});
return tokens;
}
function lexer($tokens) {
$ast = [];
foreach($tokens as $token) {
$ast[] = /* ... */;
});
return ast;
}
function parseBetterJSAlternative($code) {
$tokens = tokenize($code);
$ast = lexer($tokens);
foreach($ast as $node) {
// parse...
});
}
尽你最大的努力来避免重复的代码。重复代码不好,因为它意味着如果你修改一些逻辑,那就有不止一处地方要同步修改了。
想象一下如果你经营着一家餐厅并跟踪它的库存: 你全部的西红柿、洋葱、大蒜、香料等。如果你保留有多个列表,当你服务一个有着西红柿的菜,那么所有记录都得更新。如果你只有一个列表,那么只需要修改一个地方!
经常你容忍重复代码,因为你有两个或更多有共同部分但是少许差异的东西强制你用两个或更多独立的函数来做相同的事。移除重复代码意味着创造一个处理这组不同事物的一个抽象,只需要一个函数/模块/类。
抽象正确非常重要,这也是为什么你应当遵循SOLID原则(奠定Class基础的原则)。坏的抽象可能比重复代码还要糟,因为要小心。在这个前提下,如果你可以抽象好,那就开始做把!不要重复你自己,否则任何你想改变一件事的时候你都发现在即在更新维护多处。
Bad:
function showDeveloperList($developers) {
foreach($developers as $developer) {
$expectedSalary = $developer->calculateExpectedSalary();
$experience = $developer->getExperience();
$githubLink = $developer->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
function showManagerList($managers) {
foreach($managers as $manager) {
$expectedSalary = $manager->calculateExpectedSalary();
$experience = $manager->getExperience();
$githubLink = $manager->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
Good:
function showList($employees) {
foreach($employees as $employe) {
$expectedSalary = $employe->calculateExpectedSalary();
$experience = $employe->getExperience();
$githubLink = $employe->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
}
Bad:
$menuConfig = [
'title' => null,
'body' => 'Bar',
'buttonText' => null,
'cancellable' => true,
];
function createMenu(&$config) {
$config['title'] = $config['title'] ?: 'Foo';
$config['body'] = $config['body'] ?: 'Bar';
$config['buttonText'] = $config['buttonText'] ?: 'Baz';
$config['cancellable'] = $config['cancellable'] ?: true;
}
createMenu($menuConfig);
Good:
$menuConfig = [
'title' => 'Order',
// User did not include 'body' key
'buttonText' => 'Send',
'cancellable' => true,
];
function createMenu(&$config) {
$config = array_merge([
'title' => 'Foo',
'body' => 'Bar',
'buttonText' => 'Baz',
'cancellable' => true,
], $config);
// config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu($menuConfig);
Bad:
function createFile(name, temp = false) {
if (temp) {
touch('./temp/'.$name);
} else {
touch($name);
}
}
Good:
function createFile($name) {
touch(name);
}
function createTempFile($name) {
touch('./temp/'.$name);
}
一个函数做了比获取一个值然后返回另外一个值或值们会产生副作用如果。副作用可能是写入一个文件,修改某些全局变量或者偶然的把你全部的钱给了陌生人。
现在,你的确需要在一个程序或者场合里要有副作用,像之前的例子,你也许需要写一个文件。你想要做的是把你做这些的地方集中起来。不要用几个函数和类来写入一个特定的文件。用一个服务来做它,一个只有一个。
重点是避免常见陷阱比如对象间共享无结构的数据,使用可以写入任何的可变数据类型,不集中处理副作用发生的地方。如果你做了这些你就会比大多数程序员快乐。
Bad:
// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
$name = preg_split('/ /', $name);
}
splitIntoFirstAndLastName();
var_dump($name); // ['Ryan', 'McDermott'];
Good:
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName($name) {
return preg_split('/ /', $name);
}
$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName(name);
var_export($name); // 'Ryan McDermott';
var_export($newName); // ['Ryan', 'McDermott'];
在大多数语言中污染全局变量是一个坏的实践,因为你可能和其他类库冲突并且你api的用户不明白为什么直到他们获得产品的一个异常。让我们看一个例子:如果你想配置一个数组,你可能会写一个全局函数像config()
,但是可能和试着做同样事的其他类库冲突。这就是为什么单例设计模式和简单配置会更好的原因。
Bad:
function config() {
return [
'foo': 'bar',
]
};
Good:
class Configuration {
private static $instance;
private function __construct($configuration) {/* */}
public static function getInstance() {
if(self::$instance === null) {
self::$instance = new Configuration();
}
return self::$instance;
}
public function get($key) {/* */}
public function getAll() {/* */}
}
$singleton = Configuration::getInstance();
Bad:
if ($fsm->state === 'fetching' && is_empty($listNode)) {
// ...
}
Good:
function shouldShowSpinner($fsm, $listNode) {
return $fsm->state === 'fetching' && is_empty(listNode);
}
if (shouldShowSpinner($fsmInstance, $listNodeInstance)) {
// ...
}
Bad:
function isDOMNodeNotPresent($node) {
// ...
}
if (!isDOMNodeNotPresent($node)) {
// ...
}
Good:
function isDOMNodePresent($node) {
// ...
}
if (isDOMNodePresent($node)) {
// ...
}
这看起来像一个不可能任务。当人们第一次听到这句话是都会这么说。 “没有一个if声明
” 答案是你可以使用多态来达到许多case语句里的任务。第二个问题很常见, “那么为什么我要那么做?” 答案是前面我们学过的一个整洁代码原则:一个函数应当只做一件事。当你有类和函数有很多if
声明,你自己知道你的函数做了不止一件事。记住,只做一件事。
Bad:
class Airplane {
// ...
public function getCruisingAltitude() {
switch (this.type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
}
Good:
class Airplane {
// ...
}
class Boeing777 extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getPassengerCount();
}
}
class AirForceOne extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude();
}
}
class Cessna extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
PHP是弱类型的,这意味着你的函数可以接收任何类型的参数。 有时候你为这自由所痛苦并且在你的函数渐渐尝试类型检查。有很多方法去避免这么做。第一种是考虑API的一致性。
Bad:
function travelToTexas($vehicle) {
if ($vehicle instanceof Bicycle) {
$vehicle->peddle($this->currentLocation, new Location('texas'));
} else if ($vehicle instanceof Car) {
$vehicle->drive($this->currentLocation, new Location('texas'));
}
}
Good:
function travelToTexas($vehicle) {
$vehicle->move($this->currentLocation, new Location('texas'));
}
如果你正使用基本原始值比如字符串、整形和数组,你不能用多态,你仍然感觉需要类型检测,你应当考虑类型声明或者严格模式。 这给你了基于标准PHP语法的静态类型。 手动检查类型的问题是做好了需要好多的废话,好像为了安全就可以不顾损失可读性。保持你的PHP 整洁,写好测试,做好代码回顾。做不到就用PHP严格类型声明和严格模式来确保安全。
Bad:
function combine($val1, $val2) {
if (is_numeric($val1) && is_numeric(val2)) {
return val1 + val2;
}
throw new \Exception('Must be of type Number');
}
Good:
function combine(int $val1, int $val2) {
return $val1 + $val2;
}
僵尸代码和重复代码一样坏。没有理由保留在你的代码库中。如果从来被调用过,见鬼去!在你的版本库里是如果你仍然需要他的话,因此这么做很安全。
Bad:
function oldRequestModule($url) {
// ...
}
function newRequestModule($url) {
// ...
}
$req = new newRequestModule();
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
Good:
function newRequestModule($url) {
// ...
}
$req = new newRequestModule();
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
##有问题反馈 在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流
[这篇文章主要介绍了php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证的方法,结合实例形式分析了PHP使用Header函数调用登录验证及PHP_AUTH_PW和PHP_AUTH_USER进行验证处理的相关技巧,需要的朋友可以参考下]
本文实例讲述了php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证的方法。分享给大家供大家参考,具体如下:
在php中,可以使用Header函数做一些有趣的事情,用户验证就是其中一个很有意思的功能。具体用法:
Header("WWW-Authenticate: Basic realm="USER LOGIN""); Header("HTTP/1.0 401 Unauthorized");
在页首设计这两个Header函数,页面在载入前会出现一个登录框,要求输入用户名和密码。习惯了在页面登录的我们,是否觉得这样的登录很原始,又很新奇呢?
为了获取从这个对话框中传来的用户名和密码,需要用到php提供的两个特殊变量$PHP_AUTH_USER和$PHP_AUTH_PW,要这样使用这两个特殊变量好像需要在php.ini中设置相关的选项,不然就只能像下面这样引用:
$_SERVER['PHP_AUTH_USER'] $_SERVER['PHP_AUTH_PW']
获取到用户提交上来的用户名和密码之后,要怎样处理逻辑就跟我们一般的程序处理没有什么区别了。下面提供两个例程供参考:
<?php if(!isset($PHP_AUTH_USER)) { Header("WWW-authenticate: basic realm="XXX""); Header("HTTP/1.0 401 Unauthorized"); $title="Login Instructions"; ?> <blockquote> In order to enter this section of the web site, you must be an XXX subscriber. If you are a subscriber and you are having trouble logging in, please contact <a href="mailto:[email protected]">[email protected]</a>. </blockquote> <?php exit; } else { mysql_pconnect("localhost","nobody","") or die("Unable to connect to SQL server"); mysql_select_db("xxx") or die("Unable to select database"); $user_id=strtolower($PHP_AUTH_USER); $password=$PHP_AUTH_PW; $query = mysql_query("select * from users where user_id='$user_id' and password='$password'"); if(!mysql_num_rows($query)) { Header("WWW-authenticate: basic realm="XXX""); Header("HTTP/1.0 401 Unauthorized"); $title="Login Instructions"; ?> <blockquote> In order to enter this section of the web site, you must be an XXX subscriber. If you are a subscriber and you are having trouble logging in, please contact <a href="mailto:[email protected]">[email protected]</a>. </blockquote> <?php exit; } $name=mysql_result($query,0,"name"); $email=mysql_result($query,0,"email"); mysql_free_result($query); } ?>
另外一个参考的例程:
<?php //assume user is not authenticated $auth = false; $user = $_SERVER['PHP_AUTH_USER']; $pass = $_SERVER['PHP_AUTH_PW']; if ( isset($user) && isset($pass) ) { //connect to db include 'db_connect.php'; //SQL query to find if this entered username/password is in the db $sql = "SELECT * FROM healthed_workshop_admin WHERE user = '$PHP_AUTH_USER' AND pass = '$PHP_AUTH_PW'"; //put the SQL command and SQL instructions into variable $result = mysql_query($sql) or die('Unable to connect.'); //get number or rows in command; if more than 0, row is found $num_matches = mysql_num_rows($result); if ($num_matches !=0) { //matching row found authenticates user $auth = true; } } if (!$auth) { header('WWW-Authenticate: Basic realm="Health Ed Presentation Admin"'); header('HTTP/1.0 401 Unauthorized'); echo 'You must enter a valid username & password.'; exit; } else { echo 'Success!'; } ?> 扩展:
PHP 的 HTTP 认证机制仅在 PHP 以 Apache 模块方式运行时才有效,因此该功能不适用于 CGI 版本。在 Apache 模块的 PHP 脚本中,可以用 header() 函数来向客户端浏览器发送“Authentication Required”信息,使其弹出一个用户名/密码输入窗口。当用户输入用户名和密码后,包含有 URL 的 PHP 脚本将会再次和预定义变量 PHP_AUTH_USER、PHP_AUTH_PW 和 AUTH_TYPE 一起被调用,这三个变量分别被设定为用户名,密码和认证类型。预定义变量保存在 $_SERVER 或者 $HTTP_SERVER_VARS 数组中。系统仅支持“基本的”认证
<?php $authorization = false; if($_SERVER['PHP_AUTH_USER'] == "admin" && $_SERVER['PHP_AUTH_PW'] == "admin888"){ echo "login"; $authorization = true; exit; } if(!$authorization){ header("WWW-Authenticate:Basic realm='Private'"); header('HTTP/1.0 401 Unauthorized'); print "You are unauthorized to enter this area."; } ?>