PHP SPL标准库之接口(Interface)学习笔记

 更新时间:2016年11月25日 15:35  点击:1478
接口在php中用到的地方不多了,但接口又非常 的有用,下文我们就一起来看看PHP SPL标准库之接口(Interface)相关例子吧。


简介

SPL全称为(Standard PHP Library,PHP标准库),从官方文档上来看,SPL主要包含以下几块:
接口
数据结构
迭代器
异常
函数加强
ArrayObject
SqlFileInfo
 
一、接口

接口提供了对类的约束,在一个架构良好的类库中,通常都有设计非常好的接口定义,这里包括该抽象成接口的要抽象,同时避免接口滥用。在SPL里,主要的接口有以下几个:Countable,
OuterIterator,RecursiveIterator,SeekableIterator。

首先,这几个方法都继承自Iterator,复习下Iterator的定义:

1: Iterator extends Traversable {
2:
3: abstract public mixed current ( void )
4: abstract public scalar key ( void )
5: abstract public void next ( void )
6: abstract public void rewind ( void )
7: abstract public boolean valid ( void )
8: }

Countable明显是提供一个计数的约定,在Iterator之上,通过count方法获取集合中的数目。
OutIterator封装了Iterator,在此基础上添加了获取迭代器的方法。
RecursiceIterator在Iterator的基础上支持了迭代,新增检查和获取子节点的方法。
SeekIterator在Iterator基础上,新增了seek方法,用以获取指定位置的对象。

三、数据结构

SPL给PHP配置了一些加强的数据结构,使PHP看起来稍现代了点。添加的数据结构有以下几个:
双链表

队列
优先级队列

大顶堆
小顶堆
数组
定长数组
映射
对象映射

双链表(SplDoublyLinkedList),栈(SplStack),队列(SplQueue),优先级队列(SplPriorityQueue)都实现了Iterator接口,ArrayAccess接口,Countable接口。我们主要复习下ArrayAccess,我们以前使用的数组,可以看做是映射的 版本,而ArrayAccess,则是映射的版,也就是说,可以用这种方法来存取对象: arr[“one”]=1;  arr[obj1]=2;  继续看数据结构,双链表等在以上接口的基础上增加了pop,push等该接口常见的操作。
堆(SplHeap),大顶堆(SplMaxHeap),小顶堆(SplMinHeap)只继承了Iterator接口和Countable接口。值得一提的是,大顶堆和小顶堆是通过compare方法来将数据插入堆中,数据结构来维护堆的筛操作。
定长数组(SplFixedArray),常规的数组支持各种数据类型的键,并且长度可变,灵活的同时带来了性能的损耗。定长数组牺牲灵活性来换取性能。具体性能的测评可以看这里。

Coutable接口:
实现Countable接口的对象可用于count()函数计数。

class Mycount implements Countable
{
    public function count()
    {
        static $count = 0;
        $count++;
        return $count;
    }
}
 
$count = new Mycount();
$count->count();
$count->count();
 
echo count($count); //3
echo count($count); //4

说明:

调用count()函数时,Mycount::count()方法被调用
count()函数的第二个参数将不会产生影响

OuterIterator接口:
自定义或修改迭代过程。

//IteratorIterator是OuterIterator的一个实现类
class MyOuterIterator extends  IteratorIterator {
 
    public function current()
    {
        return parent::current() . 'TEST';
    }
}
 
foreach(new MyOuterIterator(new ArrayIterator(['b','a','c'])) as $key => $value) {
    echo "$key->$value".PHP_EOL;
}
/*
结果:
0->bTEST
1->aTEST
2->cTEST
*/

在实际运用中,OuterIterator极其有用:

$db = new PDO('mysql:host=localhost;dbname=test', 'root', 'mckee');
$db->query('set names utf8');
$pdoStatement = $db->query('SELECT * FROM test1', PDO::FETCH_ASSOC);
$iterator = new IteratorIterator($pdoStatement);
$tenRecordArray = iterator_to_array($iterator);
print_r($tenRecordArray);

RecursiveIterator接口:
用于循环迭代多层结构的数据,RecursiveIterator另外提供了两个方法:

RecursiveIterator::getChildren 获取当前元素下子迭代器
RecursiveIterator::hasChildren 判断当前元素下是否有迭代器

class MyRecursiveIterator implements RecursiveIterator
{
    private $_data;
    private $_position = 0;
 
    public function __construct(array $data) {
        $this->_data = $data;
    }
 
    public function valid() {
        return isset($this->_data[$this->_position]);
    }
 
    public function hasChildren() {
        return is_array($this->_data[$this->_position]);
    }
 
    public function next() {
        $this->_position++;
    }
 
    public function current() {
        return $this->_data[$this->_position];
    }
 
    public function getChildren() {
        print_r($this->_data[$this->_position]);
    }
 
    public function rewind() {
        $this->_position = 0;
    }
 
    public function key() {
        return $this->_position;
    }
}
 
$arr = array(0, 1=> array(10, 20), 2, 3 => array(1, 2));
$mri = new MyRecursiveIterator($arr);
 
foreach ($mri as $c => $v) {
    if ($mri->hasChildren()) {
        echo "$c has children: " .PHP_EOL;
        $mri->getChildren();
    } else {
        echo "$v" .PHP_EOL;
    }
 
}
/*
结果:
0
1 has children:
Array
(
    [0] => 10
    [1] => 20
)
2
3 has children:
Array
(
    [0] => 1
    [1] => 2
)
*/

SeekableIterator接口:
通过seek()方法实现可搜索的迭代器,用于搜索某个位置下的元素。

class  MySeekableIterator  implements  SeekableIterator  {
 
    private  $position = 0;
 
    private  $array  = array(
        "first element" ,
        "second element" ,
        "third element" ,
        "fourth element"
    );
 
    public function  seek ( $position ) {
        if (!isset( $this -> array [ $position ])) {
            throw new  OutOfBoundsException ( "invalid seek position ( $position )" );
        }
 
       $this -> position  =  $position ;
    }
 
    public function  rewind () {
        $this -> position  =  0 ;
    }
 
    public function  current () {
        return  $this -> array [ $this -> position ];
    }
 
    public function  key () {
        return  $this -> position ;
    }
 
    public function  next () {
        ++ $this -> position ;
    }
 
    public function  valid () {
        return isset( $this -> array [ $this -> position ]);
    }
}
 
try {
 
    $it  = new  MySeekableIterator ;
    echo  $it -> current (),  "\n" ;
 
    $it -> seek ( 2 );
    echo  $it -> current (),  "\n" ;
 
    $it -> seek ( 1 );
    echo  $it -> current (),  "\n" ;
 
    $it -> seek ( 10 );
 
} catch ( OutOfBoundsException $e ) {
    echo  $e -> getMessage ();
}
/*
结果:
first element
third element
second element
invalid seek position ( 10 )
*/

SplObserver和SplSubject接口:
SplObserver和SplSubject接口用来实现观察者设计模式,观察者设计模式是指当一个类的状态发生变化时,依赖它的对象都会收到通知并更新。使用场景非常广泛,比如说当一个事件发生后,需要更新多个逻辑操作,传统方式是在事件添加后编写逻辑,这种代码耦合并难以维护,观察者模式可实现低耦合的通知和更新机制。
看看SplObserver和SplSubject的接口结构:

//SplSubject结构 被观察的对象
interface SplSubject{
    public function attach(SplObserver $observer); //添加观察者
    public function detach(SplObserver $observer); //剔除观察者
    public function notify(); //通知观察者
}
 
//SplObserver结构 代表观察者
interface SplObserver{
    public function update(SplSubject $subject); //更新操作
}

看下面一个实现观察者的例子:

class Subject implements SplSubject
{
    private $observers = array();
 
    public function attach(SplObserver  $observer)
    {
        $this->observers[] = $observer;
    }
 
    public function detach(SplObserver  $observer)
    {
        if($index = array_search($observer, $this->observers, true)) {
            unset($this->observers[$index]);
        }
    }
 
    public function notify()
    {
        foreach($this->observers as $observer) {
            $observer->update($this);
        }
    }
 
 
}
 
class Observer1 implements  SplObserver
{
    public function update(SplSubject  $subject)
    {
        echo "逻辑1代码".PHP_EOL;
    }
}
 
class Observer2 implements  SplObserver
{
    public function update(SplSubject  $subject)
    {
        echo "逻辑2代码".PHP_EOL;
    }
}
 
 
$subject = new Subject();
$subject->attach(new Observer1());
$subject->attach(new Observer2());
 
$subject->notify();
/*
结果:
逻辑1代码
逻辑2代码
*/

在我们使用php实现文件下载时很多时间你会发现下载无法知道要下载文件的大但了,这个是开发者并未使用Accept-Length来获取下载文件大小了,下面我们一起来看看Accept-Length获取下载文件的大小例子
php中在做文件下载的时候,其中要加上这么一些header信息:
 
header("Content-type: application/octet-stream");

 

header("Accept-Ranges: bytes");

 

header("Accept-Length:".$fileSize);

 

header("Content-Disposition: attachment; filename=".$fileName);

 

我已经加了header("Accept-Length:".$fileSize);这句话,可是还是出现如下图的情况,获取不到下载文件的大小,虽然不影响正常的功能,但是为了追求完美,我要探个究竟。

 

 

 

Accept-Length下载获取不到文件大小

 

查手册,搜资料,最后才发现header("Accept-Length:***");这其实是一个误导,真不明白网上为什么千篇一律的都是Accept-Length。代潇瑞博客告诉大家,其实正确的应该是header("Content-Length:***"); 修改之后,果然就正确了,如下图的效果。

 

 

 

Content-Length下载获取到了文件大小

 

将Accept-Length改成Content-Length后,终于可以识别下载文件的大小了。
自动载入类在php中魔术方法了,我们可以通过php中的_autoload方法来实现了,下面一起来看一篇关于PHP类的自动载入程序代码,希望本文章对各位有帮助。


加入我们现在有两个PHP文件,内容如下:

Test1.php

<?php

class Test1 {

    public function func1() {

        return 'test1';

    }

}

?>

Test2.php

<?php

class Test2 {

    public function func2() {

        return 'test2';

    }

}

?>

然而在需要载入这两个文件时,传统的写法是这样的:

<?php
require ('Test1.php');
require ('Test2.php');
$TestObj1 = new Test1();
$TestObj2 = new Test2();
echo $TestObj1->func1().'<br/>';
echo $TestObj2->func2();
?>

现在我们使用PHP类的自动载入,只需要定义 __autoload() 方法既可将类自动载入,方法如下:

<?php
//define autoload function
function __autoload($class) {
    require __DIR__.'/'.$class.'.php';
}
$TestObj1 = new Test1();
$TestObj2 = new Test2();
echo $TestObj1->func1().'<br/>';
echo $TestObj2->func2();
?>

很方便吧,可是之后__autoload这个函数被废弃掉了,主要原因是因为,我们一个PHP的项目可能会依赖多个框架,如果我们每一个框架都拥有这个函数,那么程序就会报一个函数重复定义的致命错误。当然不用担心,在PHP5.3之后呢,官方提供了一个 spl_autoload_register() 函数来取代 __autoload,这个函数的特点是它允许你存在多个相同的载入函数,即使我写了多个载入,也不会出现任何问题,代码如下:

<?php
spl_autoload_register(autoload1);
spl_autoload_register(autoload2);
//define autoload function
function autoload1($class) {
    require __DIR__.'/'.$class.'.php';
}
function autoload2($class) {
    require __DIR__.'/'.$class.'.php';
}
$TestObj1 = new Test1();
$TestObj2 = new Test2();
echo $TestObj1->func1().'<br/>';
echo $TestObj2->func2();
?>

 

这种方法会更先进一些,也是我们采用的最主要的方法。博主最近开发的项目使用的是ThinkPHP框架,就在框架的核心文件Think.class.php中找到它的自动载入函数,拷过来给大家看下。

static public function start() {

    // 注册AUTOLOAD方法

    spl_autoload_register('Think\Think::autoload');

}

/**

 * 类库自动加载

 * @param string $class 对象类名

 * @return void

 */

public static function autoload($class) {

    // 检查是否存在映射

    if(isset(self::$_map[$class])) {

        include self::$_map[$class];

    } elseif (false !== strpos($class,'\\')){

        $name = strstr($class, '\\', true);

        if(in_array($name,array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$name)){

        // Library目录下面的命名空间自动定位

        $path = LIB_PATH;

        }else{

            // 检测自定义命名空间 否则就以模块为命名空间

            $namespace  =   C('AUTOLOAD_NAMESPACE');

            $path = isset($namespace[$name])? dirname($namespace[$name]).'/' : APP_PATH;

        }

        $filename = $path . str_replace('\\', '/', $class) . EXT;

        if(is_file($filename)) {

            // Win环境下面严格区分大小写

            if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($filename)), $class . EXT)){

                return ;

            }

            include $filename;

        }

    }elseif (!C('APP_USE_NAMESPACE')) {

        // 自动加载的类库层

        foreach(explode(',',C('APP_AUTOLOAD_LAYER')) as $layer){

            if(substr($class,-strlen($layer))==$layer){

                if(require_cache(MODULE_PATH.$layer.'/'.$class.EXT)) {

                    return ;

                }

            }

        }

        // 根据自动加载路径设置进行尝试搜索

        foreach (explode(',',C('APP_AUTOLOAD_PATH')) as $path){

            if(import($path.'.'.$class))

                // 如果加载类成功则返回

                return ;

        }

    }

}

 

更多的相关知识大家可以自行去搜索,或者查看相关手册。

标签云是用来做相关文章集合的功能,我们可以把很多文章都集合到些,很多做seo优化的朋友都会给网站做一些seo标签了,下面我们来看一个完整的php标签云制作过程。
1.数据表的结构:

 

创建建两张数据表,结构如下:

 

标签tag表:

 

 

文章mood表:

 

 

其中mood表中的tag字段,以tag表的id字段+“,”+tag表的id字段,
 

 

2.查询方法:

 

例如:如果需要某篇文章中包含tag表中的id为1,2,3的tagname,也就是id为1,2,3的标签,

 

则在添加文章的时候用

 

$result=implode(“,”, $_POST[‘tagid’]);//把获取的checkbox的数组用逗号进行分割

 

$_POST[‘tagid’]为获取前台的复选框的数组,前台html部分代码为:

 

<input type=”checkbox” name=”tagid[]” value=”{$tag.id}” id=”{$tag.id}”><label for=”{$tag.id}”>{$tag.tagname}</label>//这里是thinkphp的写法,原生的写法大同小异

 

这样存储文章的时候,只需要mood表中的tag=$result即可。

 

数据已经存好了,接下来我们需要实现的是,点击相应的标签查询出所有包含该标签的文章。

 

如果我们需要显示某篇文章所包含的所有标签,我们要先获取该片文章的id,查询出该篇文章的tag,

 

用分割函数

 

$taglist = explode(‘,’,$source); //$source为文章的tag值,例如:把tag=“1,2,3”分割为一个数组

 

然后在前台可以这样写:

 

for($index=0;$index<count($taglist);$index++){ 
$tagsa=$tagdata->where(‘id=%d’,$taglist[$index])->select();
echo “<a id=’tag’ href=’location/tag/”.$tagsa[0][‘id’].”‘>”.($tagsa[0][‘tagname’]).”</a>&nbsp&nbsp&nbsp” ;
}

 

循环输出tagname,url传tag表的id值,接下来只需要在接收url值的地方写一个模糊查询的sql,文章表的tag like %id%。

 

 注:以上的查询语句都是thinkphp的语法。

 

这样用模糊查询会出现一个问题,因为例如:文章表的其中一个tag字段可能包含1,5    另一个tag字段可能包含10,23

 

如果查询tag like %1%的时候会查询出  tag字段为1,5   和   tag字段为10,23   的两篇文章。即使like条件为%1,%或者%,1,%也是不行的。

 

因此这里我的写法是在前台写php代码,用两个嵌套的for循环来解决,如下:

 

$map[‘tag’] = array(‘like’,’%’.tagid.’%’);
//dump($selecttag[$i][‘id’]);$arr_mood=$mood->where($map)->select();for($a=0;$a<count($arr_mood);$a++){
$source=$arr_mood[$a][‘tag’];
$taglist = explode(‘,’,$source);

for($index=0;$index<count($taglist);$index++){

if(tagid==$taglist[$index]){//当传过来的tagid在文章的tag字段中存在,则输出。
dump($arr_mood[$a][‘title’]);//这里可以用echo输出至前台
}

}

}

 

鉴于博主是php新手,所以代码有粗糙的地方,请见谅了
优先级通常是讲运行算了,在php中各种运算符是非常的多了,在这里我就来为各位整理一份面试公司可能会常出的优先级例子,具体如下。


先看看题目

echo '1'.print(2)+3;

正确的结果应该是

511对于这个答案,我说“!@##¥%¥%……”,没办法答案确实没错。
那么我们来分析一下为什么会是这个答案,如标题所言这是一道坑爹的的优先级的PHP题目,那就按优先级的思路的分析(反推)。

1、先执行print

print(2)+3;//等同于print(2+3),这时缓冲区输出5。别问我为什么,手册里说的

2、print是一个函数,有返回结果,int类型

print(5);//结果等于1,这时候echo '1'.1,这个时候缓冲区又输出了11

3、最终的结果(按输出顺序):511

补充一些关于PHP优先级知识:

运算符优先级 


下表从低到高列出了运算符的优先级。 
  结合方向    运算符
  左      ,
  左      or
  左      xor
  左      and
  右      print
  右      = += -= *= /= .= %= &= |= ^=                      ~=   <<=  >>= 
  左      ? :
  左      ||
  左      &&
结合方向        运算符
  左      |
  左      ^
  左      &
  无      == != === !==
  无      <  <= > >= 
  左      <<  >>
  左      +  -  .
  左      *  /   %
  右      ! ~ ++ -- (int) (float) (string) (array) (object) @
  右      [
  无      new

[!--infotagslink--]

相关文章

  • c# 三种方法调用WebService接口

    这篇文章主要介绍了c# 三种方法调用WebService接口的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-07
  • vue接口请求加密实例

    这篇文章主要介绍了vue接口请求加密实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
  • php上传图片学习笔记与心得

    我们在php中上传文件就必须使用#_FILE变量了,这个自动全局变量 $_FILES 从 PHP 4.1.0 版本开始被支持。在这之前,从 4.0.0 版本开始,PHP 支持 $HTTP_POST_FILES 数组。这...2016-11-25
  • C#简单了解接口(Interface)使用方法

    这篇文章主要介绍了C#简单了解接口(Interface)使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-12-08
  • SpringBoot接口接收json参数解析

    这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
  • Smarty模板学习笔记之Smarty简介

    1、简介Smarty是一个使用PHP写出来的模板PHP模板引擎,是目前业界最著名的PHP模板引擎之一。它分离了逻辑代码和外在的内容,提供了一种易于管理和使用的方法,用来将原本与HTML代码混杂在一起PHP代码逻辑分离。简单的讲,目...2014-05-31
  • C# Rx的主要接口深入理解

    这篇文章主要介绍了C# Rx的主要接口深入理解的相关资料,需要的朋友可以参考下...2020-06-25
  • Feign接口方法返回值设置方式

    这篇文章主要介绍了Feign接口方法返回值设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-08
  • 如何设计一个安全的API接口详解

    在日常开发中,总会接触到各种接口,前后端数据传输接口,第三方业务平台接口,下面这篇文章主要给大家介绍了关于如何设计一个安全的API接口的相关资料,需要的朋友可以参考下...2021-08-12
  • php怎么写api接口?php写api接口的实例代码

    php怎么写api接口?本文介绍了php写api接口的实例代码,有兴趣的同学可以参考一下。 http://localhost/openUser.php?act=get_user_list&type=json在这里openUser.php...2017-07-06
  • vue设置全局访问接口API地址操作

    这篇文章主要介绍了vue设置全局访问接口API地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-14
  • vue配置多代理服务接口地址操作

    这篇文章主要介绍了vue配置多代理服务接口地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-08
  • php Observer观察者模式之学习笔记

    当我们在星际中开地图和几家电脑作战的时候,电脑的几个玩家相当于结盟,一旦我们出兵进攻某一家电脑,其余的电脑会出兵救援。 那么如何让各家电脑知道自己的盟友被攻击了...2016-11-25
  • C#处理和对接HTTP接口请求的方法

    下面通过四步给大家介绍了c#处理和对接http接口请求的方法,分步骤介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起看下吧...2020-06-25
  • Java接口DAO模式代码原理及应用详解

    这篇文章主要介绍了Java接口DAO模式代码原理及应用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-11-03
  • c# 接口使用实例

    这篇文章主要介绍了c#接口使用的实例,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-17
  • SpringData Repository接口用法解析

    这篇文章主要介绍了SpringData Repository接口用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-08-27
  • 微信小程序通过api接口将json数据展现到小程序示例

    这篇文章主要介绍了微信小程序通过api接口将json数据展现到小程序示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2017-01-23
  • 详解c# 接口IDisposable的用法

    这篇文章主要介绍了详解c# 接口IDisposable的用法,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2020-12-08
  • 深入浅析Restful接口的两种使用方式

    restful接口常用的两种方式是get和post.接下来通过本文给大家介绍Restful接口的两种使用方式,本文给大家介绍的非常详细,需要的朋友参考下吧...2020-06-25