php

合并数组 array_merge和+对数组操作的区别
1.在数组的键值为数字形式时:
array_merge不会对数据产生覆盖,重新进行索引;
‘+’在后面的数组中与前面数组的键相同时,舍弃后面的数组。

<?php
$arrone = array(‘qwer’, ‘qaz’);
$arrtwo = array(‘qwerqwer’, ‘qazqaz’);
$arrtwo2 = array(‘qwerqwer’, 5=>’qazqaz’);



var_dump(array_merge($arrone, $arrtwo));
//array(0=>string 'qwer', 1=>string 'qaz', 2=>string 'qwerqwer', 3=>string 'qazqaz')

var_dump(array_merge($arrone, $arrtwo2));
//array(0=>string 'qwer', 1=>string 'qaz', 2=>string 'qwerqwer', 3=>string 'qazqaz')

var_dump($arrone + $arrtwo);
//array(0=>string 'qwer', 1=>string 'qaz')

var_dump($arrone + $arrtwo2);
//array(0=>string 'qwer', 1=>string 'qaz', 5=>string 'qazqaz')


2.在数组的键值为字符串形式时:
array_merge会对数据产生覆盖;
‘+’在后面的数组中与前面数组的键相同时,舍弃后面的数组。



<?php
$arrone = array(‘qwer’=>’qwer’, ‘qaz’=>’qaz’);
$arrtwo = array(‘qwer’=>’qwerqwer’, ‘qaz’=>’qazqaz’);



var_dump(array_merge($arrone, $arrtwo));
//array('qwer'=>string 'qwerqwer', 'qaz'=>string 'qazqaz')

var_dump($arrone + $arrtwo);
//array('qwer'=>string 'qwer', 'qaz'=>string 'qaz')


魔术方法(Magic methods)
PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用。 魔术方法包括:



__construct(),类的构造函数
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__construct()和__destruct()
构造函数和析构函数应该不陌生,他们在对象创建和消亡时被调用。例如我们需要打开一个文件,在对象创建时打开,对象消亡时关闭



<?php
class FileRead
{
protected $handle = NULL;



function __construct(){
$this->handle = fopen(...);
}

function __destruct(){
fclose($this->handle);
} } ?> 这两个方法在继承时可以扩展,例如:


<?php
class TmpFileRead extends FileRead
{
function __construct(){
parent::__construct();
}



function __destruct(){
parent::__destruct();
} } ?> __call()和__callStatic() 在对象中调用一个不可访问方法时会调用这两个方法,后者为静态方法。这两个方法我们在可变方法(Variable functions)调用中可能会用到。


<?php
class MethodTest
{
public function __call ($name, $arguments) {
echo “Calling object method ‘$name’ “. implode(‘, ‘, $arguments). “\n”;
}



public static function __callStatic ($name, $arguments) {
echo "Calling static method '$name' ". implode(', ', $arguments). "\n";
} }


$obj = new MethodTest;
$obj->runTest(‘in object context’);
MethodTest::runTest(‘in static context’);
?>
__get(),__set(),__isset()和__unset()
当get/set一个类的成员变量时调用这两个函数。例如我们将对象变量保存在另外一个数组中,而不是对象本身的成员变量



<?php
class MethodTest
{
private $data = array();



public function __set($name, $value){
$this->data[$name] = $value;
}

public function __get($name){
if(array_key_exists($name, $this->data))
return $this->data[$name];
return NULL;
}

public function __isset($name){
return isset($this->data[$name])
}

public function unset($name){
unset($this->data[$name]);
} } ?> __sleep()和__wakeup() 当我们在执行serialize()和unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复。例子如下:


<?php
class Connection
{
protected $link;
private $server, $username, $password, $db;



public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}

private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}

public function __sleep()
{
return array('server', 'username', 'password', 'db');
}

public function __wakeup()
{
$this->connect();
} } ?> __toString() 对象当成字符串时的回应方法。例如使用echo $obj;来输出一个对象


<?php
// Declare a simple class
class TestClass
{
public function __toString() {
return ‘this is a object’;
}
}



$class = new TestClass();
echo $class;
?>
这个方法只能返回字符串,而且不可以在这个方法中抛出异常,否则会出现致命错误。



__invoke()
调用函数的方式调用一个对象时的回应方法。如下



<?php
class CallableClass
{
function __invoke() {
echo ‘this is a object’;
}
}
$obj = new CallableClass;
var_dump(is_callable($obj));
?>
__set_state()
调用var_export()导出类时,此静态方法会被调用。



<?php
class A
{
public $var1;
public $var2;



public static function __set_state ($an_array) {
$obj = new A;
$obj->var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
} }


$a = new A;
$a->var1 = 5;
$a->var2 = ‘foo’;
var_dump(var_export($a));
?>
__clone()
当对象复制完成时调用。例如在设计模式详解及PHP实现:单例模式一文中提到的单例模式实现方式,利用这个函数来防止对象被克隆。



<?php
public class Singleton {
private static $_instance = NULL;



// 私有构造方法 
private function __construct() {}

public static function getInstance() {
if (is_null(self::$_instance)) {
self::$_instance = new Singleton();
}
return self::$_instance;
}

// 防止克隆实例
public function __clone(){
die('Clone is not allowed.' . E_USER_ERROR);
} } ?> 魔术常量(Magic constants) PHP中的常量大部分都是不变的,但是有8个常量会随着他们所在代码位置的变化而变化,这8个常量被称为魔术常量。


LINE,文件中的当前行号
FILE,文件的完整路径和文件名
DIR,文件所在的目录
FUNCTION,函数名称
CLASS,类的名称
TRAIT,Trait的名字
METHOD,类的方法名
NAMESPACE,当前命名空间的名称
这些魔术常量常常被用于获得当前环境信息或者记录日志。



一、没有使用命名空间的几种实现
test/oneClass.php
class oneClass{



public function show(){
echo "这里是oneClass.php的show方法<br/>";
}


}
test/twoClass.php
<?php



class twoClass{



public function show(){
echo "这里是twoClass.php的show方法<br/>";
}


}
下面7种方式都可以实现自动加载,结果都为:



这里是oneClass.php的show方法
这里是twoClass.php的show方法
方法一:index.php 使用__autoload()魔术方法实现自动加载
<?php
//7.2以后使用这个提示一个警告,Deprecated: __autoload() is deprecated, use spl_autoload_register() instead
function __autoload($classname){
include ‘./test/’.$classname.’.php’;
}



//调用类库如果找不到会自动执行__autoload()
$one = new oneClass();
$one->show();
$two = new twoClass();
$two->show();
运行结果
Deprecated: __autoload() is deprecated, use spl_autoload_register() instead in /Users/lidong/Desktop/wwwroot/test/April/autoload1/index.php on line 5
这里是oneClass.php的show方法
这里是twoClass.php的show方法
总结:在PHP7.2以后使用__autoload()会报一个警告,7.2之前这种方式是没提示的.这种方式,是调用一个找不到的类会自动取调用__autoload()方法然后在方法里面执行include引用,实现自动加载。
方法二:index2.php 使用spl_autoload_register()方法实现自动加载,创建自定义register方法调用
<?php



function register($classname){
include “./test/{$classname}.php”;
}



spl_autoload_register(“register”);



$one = new oneClass();
$one->show();
$two = new twoClass();
$two->show();
方法三:index3.php 使用spl_autoload_register()方法,不定义register方法直接使用回调
<?php



spl_autoload_register(function($classname){
include “./test/{$classname}.php”;
});



$one = new oneClass();
$one->show();
$two = new twoClass();
$two->show();
方法四:index4.php 使用spl_autoload_register()方法,调用类的register方法实现自动加载
class autoLoad{
public static function register($classname){
include “./test/{$classname}.php”;
}
}



spl_autoload_register([“autoLoad”,”register”]);



$one = new oneClass();
$one->show();
$two = new twoClass();
$two->show();
二、使用命名空间的几种实现
test2/oneClass.php
<?php



namespace auto\test2;
class oneClass{



public function show(){
echo "这里是oneClass.php的show方法<br/>";
}


}
test2/twoClass.php
<?php
namespace auto\test2;
class twoClass{



public function show(){
echo "这里是twoClass.php的show方法<br/>";
}


}
方法五:index5.php,使用spl_autoload_register(),调用加载类的register方法,转化传递过来的命名空间实现自动加载
<?php



class autoLoad{
public static function register($classname){
$arr = explode(‘\’, $classname);
include “./test2/{$arr[2]}.php”;
}
}



spl_autoload_register([“autoLoad”,”register”]);



$one = new auto\test2\oneClass();
$one->show();
$two = new auto\test2\twoClass();
$two->show();
方法六:index6.php 跟方法五类似,区别是use方法调用类实例化时可以直接使用类名,实现自动加载
<?php



use auto\test2\oneClass;
use auto\test2\twoClass;



class autoLoad{
public static function register($classname){
$arr = explode(‘\’, $classname);
include “./test2/{$arr[2]}.php”;
}
}



spl_autoload_register([“autoLoad”,”register”]);



$one = new oneClass();
$one->show();
$two = new twoClass();
$two->show();
方法七:index7.php 与方法五和六思路一致,只不过加载类放在外部不是引用在统一文件,要点就是命名空间定义的类,要使用也要先include,实现自动加载
autoLoad.php
<?php



namespace auto;
class autoLoad{
public static function register($classname){
$arr = explode(‘\’, $classname);
include “./test2/{$arr[2]}.php”;
}
}
index7.php
<?php
use auto\test2\oneClass;
use auto\test2\twoClass;



include “./autoLoad.php”;



spl_autoload_register([“auto\autoLoad”,”register”]);



$one = new oneClass();
$one->show();
$two = new twoClass();
$two->show();
总结:所有的自动加载思想都是调用一个没引用的类库会,PHP会自动执行的一个加载方法,这个方法有可能是类的方法也有可能是普通方法,但不管怎么样都最终使用include执行文件包含,只不过命名空间需要转化下获取类名。另外值得注意的是,如果是一个php的框架自动加载实现也基本一致,只不过他会根据不同文件夹下面的定义判断后include来实现不同文件夹下文件的引用,来实现整个框架的自动加载。



类的静态属性和静态方法,需要通过什么样的方式去访问?(对象可以访问静态方法,不能访问静态成员?)



参考:https://laravel-china.org/articles/3652/static-static-attribute-and-static-method-call-in-php



(1)、静态属性不需要实例化即可调用。因为静态属性存放的位置是在类里,调用方法为”类名::属性名”;



(2)、静态方法不需要实例化即可调用。同上



(3)、静态方法不能调用非静态属性。因为非静态属性需要实例化后,存放在对象里;



(4)、静态方法可以调用非静态方法,使用 self 关键词。php里,一个方法被self:: 后,它就自动转变为静态方法;php日志中会有一条提示,php不赞成这么使用,第一次看到这种类型,看的比较多的是php Fatal、php warning这些,PHP Deprecated:  Non-static method Human::say() should not be called statically in /home/map/lihuiqin/script/tmp/staticFunc.php on line 18



(5)、调用类的静态函数时不会自动调用类的构造函数。



• this ,self和parent的区别是什么?



this是指向对象实例的一个指针,在实例化的时候来确定指向;self是对类本身的一个引用,一般用来指向类中的静态变量;parent是对父类的引用,一般使用parent来调用父类的构造函数。



• 定义常量的方式有几种?都有什么区别? (define和const的区别是什么?)



(1).const用于类成员变量的定义,一经定义,不可修改。define不可用于类成员变量的定义,可用于全局常量。



(2).const可在类中使用,define不能。



(3).const不能在条件语句中定义常量。



(4).const采用一个普通的常量名称,define可以采用表达式作为名称。



(5).const只能接受静态的标量,而define可以采用任何表达式。



(6).const定义的常量时大小写敏感的,而define可通过第三个参数(为true表示大小写不敏感)来指定大小写是否敏感。



• PDO是什么?有什么作用?



PHP DATA OBJECT,数据库访问抽象层,统一各种数据库的访问接口。



PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力;与ADODB和MDB2相比,PDO更高效。目前而言,实现“数据库抽象层”任重而道远,使用PDO这样的“数据库访问抽象层”是一个不错的选择。



PHP与MySQL的连接有三种API接口,分别是:PHP的MySQL扩展 、PHP的mysqli扩展 、PHP数据对象(PDO)



1.mysql扩展是设计开发允许php应用与MySQL数据库交互的早期扩展,针对MySQL4.1.3或者更早版本设计的,但并不支持后期MySQL服务端提供的一些特性。由于太古老,又不安全,所以已被后来的mysqli完全取代;



2.mysqli扩展,我们有时称之为MySQL增强扩展,可以用于使用 MySQL4.1.3或更新版本中新的高级特性。其特点为:面向对象接口 、prepared语句支持、多语句执行支持、事务支持 、增强的调试能力、嵌入式服务支持 、预处理方式完全解决了sql注入的问题。不过其也有缺点,就是只支持mysql数据库。如果你要是不操作其他的数据库,这无疑是最好的选择。



3.PDO是PHP Data Objects的缩写,是PHP应用中的一个数据库抽象层规范。PDO提供了一个统一的API接口可以使得你的PHP应用不去关心具体要连接的数据库服务器系统类型,也就是说,如果你使用PDO的API,可以在任何需要的时候无缝切换数据库服务器,比如从Oracle 到MySQL,仅仅需要修改很少的PHP代码。其功能类似于JDBC、ODBC、DBI之类接口。同样,其也解决了sql注入问题,有很好的安全性。不过他也有缺点,某些多语句执行查询不支持。



• php 与 nginx 和apache之间关系是什么?



nginx和apache都是web服务器,



apache是通过mod_php来解析php,nginx是通过php-fpm(fast-cgi)来解析php



1.CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编写者。CGI就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。



2.php-cgi只是解释PHP脚本的程序而已,php-cgi只是个CGI程序,他自己本身只能解析请求,返回结果,不会进程管理



3.fastcgi是一个协议,Fastcgi是用来提高CGI程序性能的,php-fpm实现了这个协议



4.PHP-FPM,是一个实现了Fastcgi的程序,被PHP官方收了。php-fpm的管理对象是php-cgi,修改了php.ini配置文件后,php-fpm能够平滑重启,php-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度。



• 如何实现php的数组?



hashtable



• include和require有什么异同点,还有哪些函数可以加载外表类库com_load, dotnet_load



1.include()用时加载,require是一开始就加载



1.fread
  string fread ( int $handle , int $length )



  fread() 从 handle 指向的文件中读取最多 length 个字节。该函数在读取完最多 length 个字节数,或到达 EOF 的时候,或(对于网络流)当一个包可用时,或(在打开用户空间流之后)已读取了 8192 个字节时就会停止读取文件,视乎先碰到哪种情况。



  fread() 返回所读取的字符串,如果出错返回 FALSE。



<?php
$filename = “/usr/local/something.txt”;
$handle = fopen($filename, “r”);//读取二进制文件时,需要将第二个参数设置成’rb’



//通过filesize获得文件大小,将整个文件一下子读到一个字符串中
$contents = fread($handle, filesize ($filename));
fclose($handle); ?>


  如果所要读取的文件不是本地普通文件,而是远程文件或者流文件,就不能用这种方法,因为,filesize不能获得这些文件的大小。此时,你需要通过feof()或者fread()的返回值判断是否已经读取到了文件的末尾。



  例如:



<?php
$handle = fopen(‘http://www.baidu.com’, ‘r’);
$content = ‘’;
while(!feof($handle)){
$content .= fread($handle, 8080);
}
echo $content;
fclose($handle);
?>
或者:



<?php
$handle = fopen(‘http://www.baidu.com’, ‘r’);
$content = ‘’;
while(false != ($a = fread($handle, 8080))){//返回false表示已经读取到文件末尾
$content .= $a;
}
echo $content;
fclose($handle);
?>



2.fgets
  string fgets ( int $handle [, int $length ] )



  fgets()从 handle 指向的文件中读取一行并返回长度最多为 length - 1 字节的字符串。碰到换行符(包括在返回值中)、EOF 或者已经读取了 length - 1 字节后停止(看先碰到那一种情况)。如果没有指定 length,则默认为 1K,或者说 1024 字节。



<?php
$handle = fopen(‘./file.txt’, ‘r’);
while(!feof($handle)){
echo fgets($handle, 1024);
}
fclose($handle);
?>
  Note: length 参数从 PHP 4.2.0 起成为可选项,如果忽略,则行的长度被假定为 1024。从 PHP 4.3 开始,忽略掉 length 将继续从流中读取数据直到行结束。如果文件中的大多数行都大于 8KB,则在脚本中指定最大行的长度在利用资源上更为有效。从 PHP 4.3 开始本函数可以安全用于二进制文件。早期的版本则不行。



3.fgetss
  string fgetss ( resource $handle [, int $length [, string $allowable_tags ]] )



  跟fgets功能一样,但是fgetss会尝试从读取的文本中去掉任何 HTML 和 PHP 标记,可以用可选的第三个参数指定哪些标记不被去掉。



<?php
$handle = fopen(‘./file.txt’, ‘r’);
while(!feof($handle)){
echo fgetss($handle, 1024, ‘
’);
}
fclose($handle);
?>



4.file
  array file ( string $filename [, int $use_include_path [, resource $context ]] )
  将文件内容读入一个数组中,数组的每一项对应文件中的一行,包括换行符在内。不需要行结束符时可以使用 rtrim() 函数过滤换行符。



<?php
$a = file(‘./file.txt’);
foreach($a as $line => $content){
echo ‘line ‘.($line + 1).’:’.$content;
}
?>
5.readfile
  int readfile ( string $filename [, bool $use_include_path [, resource $context ]] )



  读入一个文件并写入到输出缓冲。返回从文件中读入的字节数。如果出错返回 FALSE 并且除非是以 @readfile() 形式调用,否则会显示错误信息。



<?php
$size = readfile(‘./file.txt’);
echo $size;
?>



6.file_get_contents
  string file_get_contents ( string $filename [, bool $use_include_path [, resource $context [, int $offset [, int $maxlen ]]]] )



  将文件读入一个字符串。第三个参数$context可以用来设置一些参数,比如访问远程文件时,设置超时等等。



  另外,file_get_contents相对于以上几个函数,性能要好得多,所以应该优先考虑使用file_get_contents。但是readfile貌似比file_get_contents性能好一点(?),因为它不需要调用fopen。



<?php
$ctx = stream_context_create(array(
‘http’ => array(
‘timeout’ => 1 //设置超时
)
)
);
echo file_get_contents(“http://www.baidu.com/”, 0, $ctx);
?>
7.fpassthru
  int fpassthru ( resource $handle )



  将给定的文件指针从当前的位置读取到 EOF 并把结果写到输出缓冲区。



<?php
header(“Content-Type:text/html;charset=utf-8”);
$handle = fopen(‘./test2.php’, ‘r’);
fseek($handle, 1024);//将指针定位到1024字节处
fpassthru($handle);
?>



几个注意事项:



  1. 鼓励在处理二进制文件时使用 b 标志,即使系统并不需要,这样可以使脚本的移植性更好。



  2. allow_url_fopen选项激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象例如文件。默认的封装协议提供用 ftp 和 http 协议来访问远程文件,一些扩展库例如 zlib 可能会注册更多的封装协议。出于安全性考虑,此选项只能在 php.ini 中设置。



  3. 如果要打开有特殊字符的URL (比如说有空格),就需要使用 urlencode() 进行 URL 编码。



php的数组比较好,既有C的风格(可以[]索引),又有java的追加新元素。
索引起来也兼具了链表和字典的特点,用起来很方便。



通常用unset()方法删除数组元素,但是这种方式很郁闷的是,删除完了,数组的索引不会变(简单的说就是后边的不会递补上来)



又查了查,发现系统居然没有提供能删除数组中指定元素,索引还能自动排序的函数,



好吧,没有就自己写一个
(PS:这里主要是借助了array_splice()这个系统函数来实现的
 * array_splice()
 * p1:要删除的数组
 * p2:删除value的key
 * p3:(非必须)从这个key这个元素的位置开始,往后连续删除几个元素
 * p4:(非必须)是一个素组,把这个数组的值补到删除的空位上去



/*



$arr:要操作的数组



$value:要删除的元素



return:删除成功返回true,失败返回false



*/



function remove_value_from_array(&$arr , $value){
   $index = array_search($value,$arr);
    if($index === false){
        return false;
    }
    if(!empty(array_splice($arr,$index,1))){
        return true;
    }else{
        return false;
    }
}



1、变量名区分大小写



<?php



$abc = ‘abcd’;



echo $abc; //输出 ‘abcd’



echo $aBc; //无输出



echo $ABC; //无输出



2、常量名默认区分大小写,通常都写为大写



<?php



define(“ABC”,”Hello World”);



echo ABC; //输出 Hello World



echo abc; //输出 abc



php.ini配置项指令区分大小写



如 file_uploads = 1 不能写成 File_uploads = 1



3、函数名、方法名、类名不区分大小写



但推荐使用与定义时相同的名字



<?php



function show(){



echo “Hello World”;



}



show(); //输出 Hello World 推荐写法



SHOW(); //输出 Hello World



<?php



class cls{



static function func(){



echo “hello world”;



}



}



Cls::FunC(); //输出hello world



4、魔术常量不区分大小写,推荐大写



包括:LINEFILEDIRFUNCTIONCLASSMETHODNAMESPACE



<?php



echo line; //输出 2



echo LINE; //输出 3



5、NULL、TRUE、FALSE不区分大小写



<?php



$a = null;



$b = NULL;



$c = true;



$d = TRUE;



$e = false;



$f = FALSE;



var_dump($a == $b); //输出 boolean true



var_dump($c == $d); //输出 boolean true



var_dump($e == $f); //输出 boolean true



总结:



在PHP中,函数名(自定义和内置的函数)、方法名、类名、关键字不区分大小写;但变量名区分大小写。



在PHP中,自定义的函数名,类名,以及内置的函数,关键字是不区分大小写的,比如:



class,Class,CLASS,while,While,ECHO,echo,NULL,Null



都是一样的。



但是PHP中,变量的名字是区分大小写的,比如:



$name,$Name



就是两个不同的变量名。



empty() 判断一个变量是否为“空”,isset() 判断一个变量是否已经设置。正是这种所谓的“顾名思义”,令我开始时走了些弯路:当一个变量值等于0时,empty()也会成立(True),因而会发生 一些意外。原来,empty() 和 isset() 虽然都是变量处理函数,它们都用来判断变量是否已经配置,它们却是有一定的区别:empty还会检测变量是否为空、为零。当一个变量值为0,empty() 认为这个变量同等于空,即相当于没有设置。



empty 判断变量是都为空;



is_null 判断变量是否为NULL;



isset 判断变量是否已存在(已定义);



unset 把变量删除(释放)掉;



foo()和@foo()之间有什么区别?
PHP 支持一个错误控制运算符:@。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。
注意: @ 运算符只对表达式有效。对新手来说一个简单的规则就是:如果能从某处得到值,就能在它前面加上 @ 运算符。



例如,可以把它放在变量,函数和include()调用,常量,等等之前。不能把它放在函数或类的定义之前,也不能用于条件结构例如if 和 foreach 等。



php——–对象(object) 与 数组(array) 的转换
php开发中常常用到数组,sql数据都是数组,数组和对象用的也是比较多的,常常相互转化,数组是PHP的灵魂,非常强大,面向对象编程也是挺方便的。



复制代码
/**



  • 数组 转 对象
    *

  • @param array $arr 数组


  • @return object
    */
    function array_to_object($arr) {
    if (gettype($arr) != ‘array’) {
    return;
    }
    foreach ($arr as $k => $v) {
    if (gettype($v) == ‘array’ || getType($v) == ‘object’) {
    $arr[$k] = (object)array_to_object($v);
    }
    }



    return (object)$arr;
    }





/**



  • 对象 转 数组
    *

  • @param object $obj 对象


  • @return array
    */
    function object_to_array($obj) {
    $obj = (array)$obj;
    foreach ($obj as $k => $v) {
    if (gettype($v) == ‘resource’) {
    return;
    }
    if (gettype($v) == ‘object’ || gettype($v) == ‘array’) {
    $obj[$k] = (array)object_to_array($v);
    }
    }



    return $obj;
    }





self:类内指针,就是写在哪个类里边,就调用哪个类里边的方法或属性。



static:使用的这个类会被子类覆盖,使用的是子类的方法或属性,就是说父类访问的是子类的方法或属性。



self 、static 都用于静态类中
self :指代当前所在的类
static:指代当前从堆内存中提取出来,访问的是当前实例化的那个类



后期静态绑定



自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。



准确说,后期静态绑定工作原理是存储了在上一个“非转发调用”(non-forwarding call)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。所谓的“转发调用”(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。可用 get_called_class() 函数来得到被调用的方法所在的类名,static:: 则指出了其范围。



该功能从语言内部角度考虑被命名为“后期静态绑定”。“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用。
self:: 的限制



使用 self:: 或者 CLASS 对当前类的静态引用,取决于定义当前方法所在的类:



Example #1 self:: 用法
<?php
class A {
public static function who() {
echo CLASS;
}
public static function test() {
self::who();
}
}



class B extends A {
public static function who() {
echo CLASS;
}
}



B::test();
?>



以上例程会输出:



A



后期静态绑定的用法



后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。



Example #2 static:: 简单用法
<?php
class A {
public static function who() {
echo CLASS;
}
public static function test() {
static::who(); // 后期静态绑定从这里开始
}
}



class B extends A {
public static function who() {
echo CLASS;
}
}



B::test();
?>



以上例程会输出:



B



Note:

在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。


Example #3 非静态环境下使用 static::
<?php
class A {
private function foo() {
echo “success!\n”;
}
public function test() {
$this->foo();
static::foo();
}
}



class B extends A {
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}



class C extends A {
private function foo() {
/* original method is replaced; the scope of the new one is C */
}
}



$b = new B();
$b->test();
$c = new C();
$c->test(); //fails
?>



以上例程会输出:



success!
success!
success!



Fatal error: Call to private method C::foo() from context ‘A’ in /tmp/test.php on line 9



Note:

后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。

Example #4 转发和非转发调用
<?php
class A {
public static function foo() {
static::who();
}

public static function who() {
echo __CLASS__."\n";
}
}

class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}

public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}

C::test();
?>

以上例程会输出:

A
C
C


this,self::,static::的区别
self::



  代表当前类,可访问类内静态属性、静态方法、非静态方法和常量。但不能访问非静态属性












  访问方式:self::[静态属性 静态方法 非静态方法 常量],如self::$a (self不带$,变量带$)


this



  代表当前类对象,不可访问类内静态属性、静态方法和常量。










  访问方式:$this->[变量 方法],如$this->a (this带$,变量不带$)


  



static::



  与self::相比,self::代表调用的方法是对象被实现的类(子类),而static::调用的是基类(父类)



复制代码
<?php
  class Person{
    public static function name(){
      echo ‘父类’.’
’;
    }
    public static function callself(){
      self::name();
    }
    public static function callstatic(){
      static::name();
    }
  }



  class Man extends Person{
    public static function name(){
      echo ‘子类’.’
’;
    }
  }
  
  Man::callself();  // 父类
  Man::callstatic(); // 子类



问】在php中定义常量时,const与define的区别?



【答】使用const使得代码简单易读,const本身就是一个语言结构,而define是一个函数。另外const在编译时要比define快很多。



(1).const用于类成员变量的定义,一经定义,不可修改。define不可用于类成员变量的定义,可用于全局常量。



(2).const可在类中使用,define不能。



(3).const不能在条件语句中定义常量。



例如:



if (…){



const FOO = ‘BAR’; // 无效的invalid



}



if (…) {



define(‘FOO’, ‘BAR’); // 有效的valid



}



(4).const采用一个普通的常量名称,define可以采用表达式作为名称。



const FOO = ‘BAR’;



for ($i = 0; $i < 32; ++$i) {



define(‘BIT_’ . $i, 1 « $i);



}



(5).const只能接受静态的标量,而define可以采用任何表达式。



例如:



const BIT_5 = 1 « 5; // 无效的invalid



define(‘BIT_5’, 1 « 5); // 有效的valid



(6).const定义的常量时大小写敏感的,而define可通过第三个参数(为true表示大小写不敏感)来指定大小写是否敏感。



例如:



define(‘FOO’, ‘BAR’, true);



echo FOO; // BAR



echo foo; // BAR



天在Stackoverflow又看到一个很有趣的文章,所以翻译过后摘了过来。文章是由PHP开发成员之一的NikiC写的,权威性自然毋庸置疑



正文



在PHP5.3中,有两种方法可以定义常量:



使用const关键字
使用define()方法
const FOO = ‘BAR’;
define(‘FOO’,’BAR’);
这两种方式的根本区别在于const会在代码编译时定义一个常量,而define则是在代码运行时才定义一个常量。这就使得const会有以下几个缺点:



const不能在条件语句中使用。如果要定义一个全局变量,const必须要处于整个代码的最外层:
if (…) {
const FOO = ‘BAR’; // 无效的
}
// but
if (…) {
define(‘FOO’, ‘BAR’); // 有效的
}
你可以能会问为什么我要这么做?一个最平常的例子是当你在检测一个常量是否已经被定义时:
if (!defined(‘FOO’)) {
define(‘FOO’, ‘BAR’);
}
const只能用来声明变量(如数字、字符串,或者true, false, null, FILE),而define()还能接受表达式。不过在PHP5.6之后const也可以接受常量的表达式了:
const BIT_5 = 1 « 5; // 在PHP5.6之后有效,之前无效
define(‘BIT_5’, 1 « 5); // 一直有效
const的常量命名只能用直白的文本,而define()允许你用任何表达式来对常量命名。这样我们就可以执行以下操作:
for ($i = 0; $i < 32; ++$i) {
define(‘BIT_’ . $i, 1 « $i);
}
const定义的常量是大小写敏感的,但是define允许你将其第三个参数设置为true来关闭其对大小写的敏感:
define(‘FOO’, ‘BAR’, true);
echo FOO; // BAR
echo foo; // BAR
以上就是你需要注意的几点。那么现在我来说明以下,为什么不涉及以上情况下,我个人总是习惯使用const:



const更加易读、美观。
const默认在当前的namespace下定义常量,而使用define则需要你写明整个namespace的完整路径:
namespace A\B\C;
// 如果要定义常量 A\B\C\FOO:
const FOO = ‘BAR’;
define(‘A\B\C\FOO’, ‘BAR’);
自从PHP5.6后,使用const数组也能被定义为常量。而define目前是不支持这一功能的,但是该功能会在PHP7中被实现:
const FOO = [1, 2, 3]; // 在PHP 5.6中有效
define(‘FOO’, [1, 2, 3]); // 在PHP 5.6无效, 在PHP 7.0有效
因为const在编译时就被执行了,所以它在速度上要比define快一点。
尤其是在使用define定义大量常量时,PHP的运行速度会变得非常慢。人们甚至发明了诸如apc_load_constantshide来避免这个问题



与define相比,const能使定义常量的效率提高一倍(在配置有XDebug的开发机器上,这个差异还会更大)。但是在查询时间上,两者是没有区别的(因为二者用的都是同一个查询表)



最后需要注意的一点是,const可以在class和interface当中使用 ,而define是做不到这一点的:
class Foo {
const BAR = 2; // 有效
}
class Baz {
define(‘QUX’, 2); // 无效



PDO扩展为PHP访问数据库定义了一个轻量级的、一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,都可以通过一致的函数执行查询和获取数据。PDO随PHP5.1发行,在PHP5.0的PECL扩展中也可以使用。



PDO主要是用来对数据库进行访问的。PDO扩展为PHP访问数据库定义了一个轻量级的一致接口,不同数据库在访问时,采用相同方法名称,解决了连接数据库不统一问题。PDO扩展自身并不能实现任何数据库功能,必须使用一个具体数据库的PDO驱动来访问数据库服务。



pdo特点: 性能:比传统和原生访问数据库方式的效率高。



运行时扩展:pdo是模块化的。能在项目运行时加载数据库的驱动。



pdo的安装问题:



找到扩展配置位置,添加extension=php_pdo_mysql.dll。 如果代码存在,则把前面的分号注释去掉即可。extension=php_pdo.dll同理



重启apache



pdo的分类:



PDO类 : 主要用户数据库连接,发送sql语句
PDOStatement类 : 主要用来解释结果集,实现预处理,事物处理
PDOException类 : 主要用于捕获PDO异常



创建PDO对象:



变量名 = new PDO(‘数据库源’,’用户名’,’密码’);



例如:$db = new PDO(‘mysql:host=localhost;port=3306;charset=utf8;dbname=stu’,’root’,’root’);



exec()方法执行一条 SQL 语句,并返回受影响的行数,建议用于增删改的SQL执行操作



query()方法执行一条SQL语句,并返回结果集,建议用于查询的SQL执行操作



对应query()方法,可以通过setFetchMode(PDO::FETCH_ASSOC) 设置获取结果集的返回类型



setFetchMode方法的一个PDO参数可以有三个值:



PDO::FETCH_ASSOC 只返回关联数组。
PDO::FETCH_NUM 只返回数字下标数组。
PDO::FETCH_BOTH 默认的,关联数组+数字下标的数组。



fetch()方法从结果集中获取下一行 ,一次只能返回一行数据,获取不到数据返回false,通过参数可以设置结果集的返回类型



  参数一共有以下几个:



PDO::FETCH_ASSOC:返回一个索引为结果集列名的数组



PDO::FETCH_BOTH(默认):返回一个索引为结果集列名和以0开始的列号的数组



PDO::FETCH_BOUND:返回 TRUE ,并分配结果集中的列值给 PDOStatement::bindColumn() 方法绑定的 PHP 变量。



PDO::FETCH_CLASS:返回一个请求类的新实例,映射结果集中的列名到类中对应的属性名。



PDO::FETCH_INTO:更新一个被请求类已存在的实例,映射结果集中的列到类中命名的属性



PDO::FETCH_LAZY:结合使用 PDO::FETCH_BOTH 和 PDO::FETCH_OBJ,创建供用来访问的对象变量名



PDO::FETCH_NUM:返回一个索引为以0开始的结果集列号的数组



PDO::FETCH_OBJ:返回一个属性名对应结果集列名的匿名对象



fetchAll()方法返回一个包含结果集中所有行的数组 ,通过参数可以设置结果集的返回类型。想要获取结果集中单独一列的唯一值,需要将 PDO::FETCH_COLUMN 和 PDO::FETCH_UNIQUE 按位或。想要返回一个根据指定列把值分组后的关联数组,需要将 PDO::FETCH_COLUMN 和 PDO::FETCH_GROUP 按位或。



通过参数可以设置结果集的返回类型:



PDO::FETCH_COLUMN:返回指定以0开始索引的列。



PDO::FETCH_CLASS:返回指定类的实例,映射每行的列到类中对应的属性名。



PDO::FETCH_FUNC:将每行的列作为参数传递给指定的函数,并返回调用函数后的结果。



rowCount()方法 返回受上一个 SQL 语句影响的行数



prepare()方法生成一个PDOStatement对象,查询不会立即执行,而是预处理



例如: $stmt = $db->prepare(“select * from test”);



execute()方法执行一条预处理语句



PDO的预处理:



预处理功能就是在SQL语句结构与形式相同的情况下,只有参数不同所采用的一种数据处理机制,其极大的减少了带宽的浪费。



参数绑定:



bindParam => 绑定一个参数到指定的变量名。绑定一个PHP变量到用作预处理的SQL语句中的对应命名占位符或问号占位符。这个方法只能绑定变量。



bindValue => 把一个值绑定到一个参数 。绑定一个值到用作预处理的 SQL 语句中的对应命名占位符或问号占位符。



特别注意:参数标识符。对于使用命名占位符的预处理语句,应是类似 :name 形式的参数名。对于使用问号占位符(?)的预处理语句,应是以1开始索引的参数位置。



PDO中的事务处理功能:
① 开启事务
PDO::beginTransaction
② 提交事务
PDO::commit
③ 回滚事务
PDO::rollBack



PDO异常处理:



异常处理用于在指定的错误(异常)情况发生时改变脚本的正常流程。这种情况称为异常。



PDO异常捕获:



try{



$db = new PDO(“mysql:host=localhost;dbname=test”,”root”,”root”);



}catch(PDOException $e){ //捕获得错误。



echo $e->getMessage(); //错误信息



}



PDO属性的获取与设置:
setAttribute => 设置数据库句柄属性。



getAttribute => 获取数据库连接的属性值



一些可用的通用属性:



PDO::ATTR_CASE:强制列名为指定的大小写。



PDO::CASE_LOWER:强制列名小写。



PDO::CASE_NATURAL:保留数据库驱动返回的列名。



PDO::CASE_UPPER:强制列名大写。



PDO::ATTR_ERRMODE:错误报告。



PDO::ERRMODE_SILENT: 仅设置错误代码。



PDO::ERRMODE_WARNING: 引发 E_WARNING 错误



PDO::ERRMODE_EXCEPTION: 抛出 exceptions 异常。



PDO::ATTR_ORACLE_NULLS (在所有驱动中都可用,不仅限于Oracle): 转换 NULL 和空字符串。



PDO::NULL_NATURAL: 不转换。



PDO::NULL_EMPTY_STRING: 将空字符串转换成 NULL。



PDO::NULL_TO_STRING: 将 NULL 转换成空字符串。



PDO::ATTR_STRINGIFY_FETCHES: 提取的时候将数值转换为字符串。



PDO::ATTR_STATEMENT_CLASS: 设置从PDOStatement派生的用户提供的语句类。 不能用于持久的PDO实例。



PDO::ATTR_TIMEOUT: 指定超时的秒数。并非所有驱动都支持此选项,这意味着驱动和驱动之间可能会有差异。比如,SQLite等待的时间达到此值后就放弃获取可写锁,但其他驱动可能会将此值解释为一个连接或读取超时的间隔。 需要 int 类型。



PDO::ATTR_AUTOCOMMIT (在OCI,Firebird 以及 MySQL中可用): 是否自动提交每个单独的语句。



PDO::ATTR_EMULATE_PREPARES 启用或禁用预处理语句的模拟。 有些驱动不支持或有限度地支持本地预处理。使用此设置强制PDO总是模拟预处理语句(如果为 TRUE ),或试着使用本地预处理语句(如果为 FALSE)。如果驱动不能成功预处理当前查询,它将总是回到模拟预处理语句上。 需要 bool 类型。



PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (在MySQL中可用): 使用缓冲查询。



PDO::ATTR_DEFAULT_FETCH_MODE: 设置默认的提取模式。



Category lang