PHP 7: 真实世界的应用开发
  • 前言
  • 模块一
    • 第一章、建立基础
      • PHP 7 安装注意事项
      • 使用内置的 PHP web 服务器
      • 创建一个 MySQL 测试数据库
      • 安装 PHPUnit
      • 实现类的自动加载
      • 抓取一个网站
      • 建立一个深度网络扫描器
      • 创建一个 PHP 5 到 PHP 7 代码转换器
    • 第二章、使用 PHP 7 高性能特性
      • 了解抽象语法树
      • 理解句法分析中的差异
      • 理解 foreach() 处理中的差异
      • 使用 PHP 7 增强功能提高性能
      • 遍历海量文件
      • 将电子表格上传到数据库
      • 递归目录迭代器
    • 第三章、使用 PHP 函数
      • 函数开发
      • 数据类型提示
      • 使用返回值数据类型
      • 使用迭代器
      • 使用生成器编写自己的迭代器
    • 第四章、使用 PHP 面向对象程序设计
      • 类的开发
      • 类的扩展
      • 使用静态属性和方法
      • 使用命名空间
      • 定义可见性
      • 使用接口
      • 使用特性
      • 实现匿名类
    • 第五章、与数据库的交互
      • 使用PDO连接数据库
      • 构建一个 OOP SQL 查询生成器
      • 处理分页
      • 定义实体以匹配数据库表
      • 将实体类与RDBMS查询绑定
      • 将二次查找嵌入到查询结果中
      • 实现jQuery DataTables的PHP查找
    • 第六章、建立可扩展的网站
      • 创建通用表单元素生成器
      • 创建一个HTML单选元素生成器
      • 创建一个HTML选择元素生成器
      • 实现表单工厂
      • 链式 $_POST 过滤器
      • 链式 $_POST 验证器
      • 将验证绑定到表单
    • 第七章、访问Web服务
      • 在PHP和XML之间转换
      • 创建一个简单的REST客户端
      • 创建一个简单的REST服务器
      • 创建一个简单的SOAP客户端
      • 创建一个简单的SOAP服务器
    • 第八章、处理日期/时间和国际化方面
      • 在视图脚本中使用 emoji
      • 转换复杂字符
      • 从浏览器数据获取语言环境
      • 按地区设置数字格式
      • 按地区处理货币
      • 按地区设置日期/时间格式
      • 创建一个HTML国际日历生成器
      • 构建一个周期性事件生成器
      • 不使用gettext处理翻译
    • 第九章、开发中间件
      • 使用中间件进行认证
      • 使用中间件实现访问控制
      • 使用高速缓存提高性能
      • 实施路由选择
      • 进行框架间的系统调用
      • 使用中间件来跨语言
    • 第十章、高级算法
      • 使用 getter 和 setter
      • 实现一个链表
      • 建立冒泡排序
      • 实现一个堆栈
      • 构建一个二分法查找类
      • 实现一个搜索引擎
      • 显示多维数组并累计总数
    • 第十一章、软件设计模式的实现
      • 创建数组到对象的转化器
      • 构建对象到数组到转化器
      • 实施策略模式
      • 定义一个映射器
      • 实现对象关系映射
      • 实施发布/订阅设计模式
    • 第十二章、提高网站安全
      • 过滤$_POST数据
      • 验证$_POST数据
      • 保护PHP session
      • 用令牌保护表格的安全
      • 建立一个安全的密码生成器
      • 带有验证码的安全保护表格
      • 不使用mcrypt进行加密/解密
    • 第十三章、最佳实践、测试和调试
      • 使用特征和接口
      • 通用异常处理程序
      • 通用错误处理程序
      • 编写一个简单的测试
      • 编写测试套件
      • 生成虚假的测试数据
      • 使用session_start参数自定义会话
    • PSR-7
  • 模块二
  • 模块三
    • GoF 设计模式
      • 结构型
      • 行为型
      • 小结
    • SOLID 设计原则
      • 开闭原则
      • 里氏替换原则
      • 接口隔离原则
      • 依赖反转原则
      • 小结
    • 模块化网店应用的需求规范
      • 线框设计
      • 定义技术栈
      • 小结
    • Symfony 概述
      • 创建一个空白项目
      • 使用 Symfony 控制台
      • 控制器
      • 路由
      • 模板
      • 表单
      • 配置 Symfony
      • bundle 系统
      • 数据库和 Doctrine
      • 测试
      • 验证
      • 小结
    • 构建核心模块
    • 构建目录模块
    • 构建客户模块
    • 构建支付模块
    • 构建发货模块
    • 构建销售模块
    • 总结
由 GitBook 提供支持
在本页
  • 如何做...
  • 如何运行...
  1. 模块一
  2. 第十章、高级算法

使用 getter 和 setter

乍一看,定义具有公共属性的类似乎是有意义的,这些属性可以直接被读取或写入。然而,我们认为最好的做法是让属性受保护,然后为每个属性定义一个getter和setter。顾名思义,getter用于检索属性的值。setter用于设置值。

最佳实践

将属性定义为受保护的,以防止意外的外部访问。使用公共的 get* 和 set* 方法来提供对这些属性的访问。通过这种方式,不仅可以更精确地控制访问,还可以在获取和设置属性的同时对其进行格式和数据类型的更改。

如何做...

1.当获取或设置值时,Getters和setters提供了额外的灵活性。如果需要的话,你可以添加一个额外的逻辑层,如果你直接读取或写入一个公共属性,这是不可能的。你需要做的就是创建一个前缀为 get 或 set 的公共方法。属性的名称就成了后缀。惯例是让变量的第一个字母大写。因此,如果属性是 $testValue ,那么getter将是 getTestValue() 。

2. 在这个例子中,我们定义了一个带有保护属性 $date 的类。请注意,get 和 set 方法允许作为 DateTime 对象或字符串来处理。在任何情况下,该值实际上都是作为一个 DateTime 实例存储的。

$a = new class() {
  protected $date;
  public function setDate($date)
  {
    if (is_string($date)) {
        $this->date = new DateTime($date);
    } else {
        $this->date = $date;
    }
  }
  public function getDate($asString = FALSE)
  {
    if ($asString) {
        return $this->date->format('Y-m-d H:i:s');
    } else {
        return $this->date;
    }
  }
};

3. getters和setters允许你过滤进入或流出的数据。在下面的例子中,有两个属性,$intVal和$arrVal,它们被设置为默认的初始值 NULL。注意,不仅获取者的返回值是数据类型的,而且还提供了默认值。设置器还强制执行传入的数据类型,或者将传入的值类型化为某种数据类型。

<?php
class GetSet
{
  protected $intVal = NULL;
  protected $arrVal = NULL;
  // note the use of the null coalesce operator to return a default value
  public function getIntVal() : int
  {
    return $this->intVal ?? 0;
  }
  public function getArrVal() : array
  {
    return $this->arrVal ?? array();
  }
  public function setIntVal($val)
  {
    $this->intVal = (int) $val ?? 0;
  }
  public function setArrVal(array $val)
  {
    $this->arrVal = $val ?? array();
  }
}

4. 如果有一个有很多很多属性的类,为每个属性定义一个不同的getter和setter可能会变得很乏味。在这种情况下,你可以使用神奇的 __call() 方法定义一种回调。下面这个类定义了九个不同的属性。我们不必定义九个getter和九个setter,而是定义了一个方法 __call(),它可以确定使用方式是 get 还是 set。如果是 get,它从内部数组中检索键。如果是set,它将值存储在内部数组中。

__call() 方法是一个神奇的方法,如果应用程序对一个不存在的方法进行调用,它就会被执行。

<?php
class LotsProps
{
  protected $firstName  = NULL;
  protected $lastName   = NULL;
  protected $addr1      = NULL;
  protected $addr2      = NULL;
  protected $city       = NULL;
  protected $state      = NULL;
  protected $province   = NULL;
  protected $postalCode = NULL;
  protected $country    = NULL;
  protected $values     = array();
    
  public function __call($method, $params)
  {
    preg_match('/^(get|set)(.*?)$/i', $method, $matches);
    $prefix = $matches[1] ?? '';
    $key    = $matches[2] ?? '';
    $key    = strtolower($key);
    if ($prefix == 'get') {
        return $this->values[$key] ?? '---';
    } else {
        $this->values[$key] = $params[0];
    }
  }
}

如何运行...

将步骤1中提到的代码复制到一个新的文件中,chap_10_oop_using_getters_and_setters.php。为了测试该类,添加以下内容。

// set date using a string
$a->setDate('2015-01-01');
var_dump($a->getDate());

// retrieves the DateTime instance
var_dump($a->getDate(TRUE));

// set date using a DateTime instance
$a->setDate(new DateTime('now'));
var_dump($a->getDate());

// retrieves the DateTime instance
var_dump($a->getDate(TRUE));

在输出中(如下图所示),可以看到 $date 属性可以使用字符串或实际的 DateTime 实例来设置。当执行 getDate() 时,你可以根据 $asString 标志的值返回一个字符串或一个 DateTime 实例。

接下来,看一下第2步中定义的代码。将这段代码复制到文件chap_10_oop_using_getters_and_setters_defaults.php 中,并添加以下内容。

// create the instance
$a = new GetSet();

// set a "proper" value
$a->setIntVal(1234);
echo $a->getIntVal();
echo PHP_EOL;

// set a bogus value
$a->setIntVal('some bogus value');
echo $a->getIntVal();
echo PHP_EOL;

// NOTE: boolean TRUE == 1
$a->setIntVal(TRUE);
echo $a->getIntVal();
echo PHP_EOL;

// returns array() even though no value was set
var_dump($a->getArrVal());
echo PHP_EOL;

// sets a "proper" value
$a->setArrVal(['A','B','C']);
var_dump($a->getArrVal());
echo PHP_EOL;

try {
    $a->setArrVal('this is not an array');
    var_dump($a->getArrVal());
    echo PHP_EOL;
} catch (TypeError $e) {
    echo $e->getMessage();
}

echo PHP_EOL;

正如你可以从下面的输出中看到的,设置一个合适的整数值和预期的一样,非数字值默认为0。有趣的是,如果你提供一个布尔值 true 作为参数给settingVal(),它就会被插值为1。

如果你调用getArrVal()而不设置一个值,默认是一个空数组。设置一个数组值的工作原理和预期一样。然而,如果你提供一个非数组值作为参数,数组的类型提示会导致抛出TypeError,这个错误可以如这里所示。

最后,把步骤3中定义的LotsProps类放到一个单独的文件中,chap_10_oop_using_getters_and_setters_magic_call.php。现在添加代码来设置值。当然,会发生的是调用了神奇的方法__call()。在运行preg_match()后,不存在的属性的剩余部分,在字母设置后,将成为内部数组$values中的一个键。

$a = new LotsProps();
$a->setFirstName('Li\'l Abner');
$a->setLastName('Yokum');
$a->setAddr1('1 Dirt Street');
$a->setCity('Dogpatch');
$a->setState('Kentucky');
$a->setPostalCode('12345');
$a->setCountry('USA');
?>

然后,你可以定义HTML,使用相应的 get 方法显示这些值。这些方法又会从内部数组中返回键。

<div class="container">
<div class="left blue1">Name</div>
<div class="right yellow1">
<?= $a->getFirstName() . ' ' . $a->getLastName() ?></div>   
</div>
<div class="left blue2">Address</div>
<div class="right yellow2">
    <?= $a->getAddr1() ?>
    <br><?= $a->getAddr2() ?>
    <br><?= $a->getCity() ?>
    <br><?= $a->getState() ?>
    <br><?= $a->getProvince() ?>
    <br><?= $a->getPostalCode() ?>
    <br><?= $a->getCountry() ?>
</div>   
</div>

这是最后的输出。

上一页第十章、高级算法下一页实现一个链表

最后更新于4年前