ORM到底是用还是不用?

ORM即Object/Relation Mapping的简写,一般称作“对象关系映射”,在Web开发中最常出没于和关系型数据库交互的地方。接口、中间件、库、包,你都可以这么称呼它。

我们可以结合PHP和MySQL,从ORM的四个核心理念来认识它:

  • 简单:ORM以最基本的形式建模数据。比如ORM会将MySQL的一张表映射成一个PHP类(模型),表的字段就是这个类的成员变量
  • 精确:ORM使所有的MySQL数据表都按照统一的标准精确地映射成PHP类,使系统在代码层面保持准确统一
  • 易懂:ORM使数据库结构文档化。比如MySQL数据库就被ORM转换为了PHP程序员可以读懂的PHP类,PHP程序员可以只把注意力放在他擅长的PHP层面(当然能够熟练掌握MySQL更好)
  • 易用:ORM的避免了不规范、冗余、风格不统一的SQL语句,可以避免很多人为Bug,方便编码风格的统一和后期维护

接下来再通过一个很基本的例子来说明一下ORM的使用,还以PHP和MySQL为例。

user这个数据模型是再普遍不过的了。假设我们有一张user数据表,结构如图:

在OOP中通常我们需要写一个对应的class User来作为user数据表的数据模型:

// 声明class User 
class User{ 
    $id; 
    $name; 
    function create(){/*…*/} 
    function load($id){/*…*/} 
} 
// 使用class User 
$user = new User(); 
$user->name = ‘fancy’; 
$user->create();

但是通过ORM,我们可以不用去声明class User,可以直接继承ORM提供的工厂类,比如:

// 直接使用!对于熟悉MVC的亲知道这个意义之所在! 
$user = new ORM(‘user’); 
// ORM都有自己的规则,这里直接使用了MySQL的表名 
$user->name = ‘fancy’; 
// MySQL的表的字段就是$user对象的成员变量 
$user->save(); 
// 掉用ORM提供的接口函数

ORM一般都针对数据模型提供了一下常见的接口函数,比如:create(), update(), save(), load(), find(), find_all(), where()等,也就是讲sql查询全部封装成了编程语言中的函数,通过函数的链式组合生成最终的SQL语句。

所以由这些来看,ORM对于敏捷开发和团队合作开发来说,好处是非常非常大的。这里就罗列一下我想到的ORM显著的优点:

  • 大大缩短了程序员的编码时间,减少甚至免除了对Model的编码
  • 良好的数据库操作接口,使编码难度降低,使团队成员的代码变得简洁易读、风格统一
  • 动态的数据表映射,在数据表结构甚至数据库发生改变时,减少了相应的代码修改
  • 减少了程序员对数据库的学习成本
  • 可以很方便地引入数据缓存之类的附加功能

但是ORM并不是一个完美的东西,它同时也有其自身不可避免的缺点:

  • 自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还好啦,一般来说都可以忽略之,特别是有cacha存在的时候
  • 在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂且猥琐
  • 越是功能强大的ORM越是消耗内存,因为一个ORM Object会带有很多成员变量和成员函数。有一次修复bug时就遇见,使用ORM查询的时候会占用12MB的内存,而使用SQL的查询时只占用了1.7MB……

ORM就是这么一个让人又爱又恨的东西。回到我们开始的问题:“ORM到底是用还是不用?”。

Fancy个人的观点是:ORM要用!但关键部位不能用!

因为对于一般的Web应用开发来说,使用ORM确实能带来上述的诸多好处,而且在大部分情况下涉及不到ORM的不好的地方。但是在系统里面有大数据量、大运算量、复杂查询的地方,就不要用ORM。ORM的性能问题将给你带来灾难。在这些地方就可以使用纯SQL或者其他简单轻量的DB Helper库了。在详细了解ORM之后,你就可以扬长避短让ORM发挥其最大效用了。

原文链接: http://www.fancycedar.info/2013/01/orm/

Symfony2之不能承受之重

本来想写成PHP之不能承受之轻,但PHP本身问题并不大,易学易用,大多数需要的东西都有,友好的C扩展接品,虽然有一些不好的语法等问题存在,但不能说PHP之不能承受之轻.

那还是说说Symfony2这个自带DI的Web开发框架吧,最起码说一说Symfony2之不能承受之重.

首先,组件.Symfony2在PHP的世界里带来了组件的概念,这个概念非常好,特别是以完全面向对象的风格,引入各种bundle作为组件.虽然21世纪的技术早就普及的组件的概念,但是Symfony2实现得非常好,比如安全组件,再比如权限等等.但没有完美的东西,Form组件与验证组件用起来就太重了,不方便单元测试,使用复杂,到今天我还是记不住所有语法,问过其他人,也大多记不下语法.

然后, 自然要提到I18N组件,简直就是能让人疯狂的组件,没有文档说明如何配置是正确的,有人用yml,有人用xml还有人用更奇怪的办法.所以,每当碰上这种I18N的情况,我宁可多加一个部分,在Web层配置两套twig.

再然后,单元测试.不少人说Symfony2的单元测试挺好啊,又用不着自己处理垃圾数据,直接放在内存里面跑,但又有几个人真正了解如何以bootstrap的模式启动Symfony2的内核?真正的单元测试又怎么能够不以这种方式做单元测试和数据层?别提Mock,我知道什么是Mock,但你只测试QueryBuilder生成的query语句还不够,一定要跑起来再看实体定义,Symfony2应该增加这部分的默认配置.

再再然后,就是Bundle的继承关系,没有一个真正优秀的设计,这部分继承关系,如同鸡肋,食之无味,弃之可惜(比如用FOS的组件就需要用到这个特性)

最后,再折腾一句:cache设计得不合理,很多人都需要花很久才能真正用好,应该再自动化一点,智能一点,多封装几种算法.

又写废话了,Symfony2还是最好的PHP框架之一,只是有一些地方,总是有着开发人员无法承受之重.

转载自:http://saharabear.com/weblog/?p=1799

为什么很多人讨厌PHP

幾年前我有回覆類似的問題,核心理念是,當我們只從一個或少數角度來觀看一件事時,往往會忘了另一片風景。

重點是,我們打算用程式語言(PHP)做什麼?這會決定我們看的角度,甚至影響了我們的心態。我曾在公開演講中表示,程式語言對我來說,是個「選擇(Option)」,是用來「解決某個問題」。如同我們不會拿榔頭敲掉螺絲,而是使用螺絲起子。一旦我們受限於只比較「工具」的局部特性時,我們很容易只看到一個工具的缺點,而忘了它的優點。

今天我在網路上看到 Phil Sturgeon 在 Quora 針對此問題的回答,同時也同步在他的網誌,分享給大家。

Phil 認為很多人有各種不同的理由「討厭」PHP,或至少看衰它。其中有一些是有根據的,有些則否。

1. Inconsistent haystack / needle

Phil 舉的第一個例子是「不一致的 haystack / needle」。

舉個 PHP 中的兩個內建函式:

  • in_array($needle, $haystack)
  • strpos($haystack, $needle)

很多人批評函式參數的 haystack 及 needle 在不同的情況下的順序不一致,這導致開發者很可能要「硬背」這些 API。

Phil 指出,其實 PHP 的函式設計是有規則的。如果 haystack 處理的是 array,則順序是 needle, haystack;反之若是 string,則順序是 haystack, needle。

2. PHP is a HTML file, with logic

很多人認為 PHP 與 HTML 的混用規則是很奇怪的設計。

Phil 指出,PHP 在起初設計上就是預設檔案是個 HTML,只是中間可以嵌入一些 PHP 的邏輯處理。我們也不能否認的是,在當時的那個時代,這個設計使得 PHP 使用率得以快速成長。

但也不能否認的是,這個現象在現代看來有些不合味口,所以在未來的 PHP 6 將會引入新特性來讓開發者得以解決這個問題。

3. No standards

在 PHP 的世界中有太多的 Web framework,每個都有著自己的標準,例如很多人會爭論使用 snake_case 還是 camelCase。最近這個現象也開始舒解了,因為有 PHP-FIG 組織制定了相關的遵循標準。

4. Lack of Quality Packages

另外很多人批評 PHP 沒有夠好的套件管理工具。例如我們知道,

  • Node.js 有 NPM
  • Ruby 有 Bundler/Gems
  • Python 有 PIP
  • Perl 有 CPAN

而過去的 PHP 只有難用的 PEAR。相對好用的 PHP 套件管理工具也不是沒有,只是過於分散不集中。例如,

  • CodeIgniter 的 Sparks
  • FuelPHP 的 Cells
  • Laravel 的 Bundles
  • CakePHP 的 Bakery
  • ZF2 的 Modules

Phil 認為這個現象將會也會得到舒解。因為除了 PHP-FIG 組織制定的 PSR-0 標準外,也有 Composer 團隊推出標準的套件管理計畫。

5. Misconception

Phil 指出(編者註: Phil 同時也是 Ruby 使用者),很多 Ruby 開發者對 PHP 的批評認知還停留在 PHP 4 的時代。其實 PHP 5 已改進非常多,甚至 PHP 5.3 是一個大躍進。他認為很多人的評論還停留在 7 年前的 PHP。

Phil 同時也說,有些人說 PHP 沒有內建 Web server 是個很糟糕的事,但好消息是從 PHP 5.4 開始就內建 built-in web server 功能了。

6. You were doing it wrong

Phil 指出,很多人會說「You can mix MySQL, HTML and PHP in the same files. PHP is disgusting!」(編者譯: 「你可以把 MySQL, HTML 及 PHP 同時放在一個檔案裡面,可見 PHP 多麼噁心」)。

但 Phil 認為這個缺點可以由 PHP Web framework 獲得解決,同時他也認為如果 Ruby 開發者在不使用 Sinatra 或 Rails 時是怎麼解決這個問題的?或者 Node.js 開發者不使用 Express 或 CanJS 時又是怎麼解決的?(編者譯:Phil 指的應該是「不要拿程式語言(PHP)來和 Web framework (Rails 或 Express) 來比較」)

7. Elitism

Phil 感慨的指出,現在很多人認為「不使用 PHP 的人才酷」(編者註:Phil 言下之意不知是否也指「不要以為使用 Rails 或 Node.JS 就覺得很潮」?)

除了批評之外,我們該想想,「為什麼現在還有那麼多人使用 PHP ?」。Phil 認為「沒意外的話,就是氣勢(Momentum)」,他說根據統計,證明就是很多人使用 PHP,也不否認他仍然使用 PHP 的原因就是,相較於 Ruby、Python 及 Node.JS 而言,PHP 仍然擁有最大的市場。

最後 Phil 認為,很多開發者都在追求「完美的語言」,但答案是「世界上沒有完美的工具」,因此沒有任何人應該「討厭」任何程式語言。

转载自:http://blog.gcos.me/2012-12-04_phil-sturgeon-anwser-why-some-people-hate-php.html

PHP语言的精华

我不擅长布道,因为我总是认为人家书上写得东西比我写得好多了.可是我却想把一些简单的道理说一下,因为总有人问我,那我就说几句.

PHP有很多不好的地方,比如说使用$符作为变量的一部分,大量使用->和=>作为分隔符或者引用之类的,并且它的方法并不面向对象,而对象却不能少了过程式方法,它又不像Python那样,方法本身也是对象,同时方法的参考顺序也没有统一的设计,我天天写PHP都要没事就查文档去看substr, strpos, strrpos这些东西.但任何语言都有问题,而PHP却有臫优秀的一面,提提它的精华吧.

第一条精华:简单.

PHP是极其简单的语言,虽然说你可以把PHP复杂化,但是PHP的简单是不需要解释的,也不需要争论的.只要想着PHP在正常情况下会一行一行向下执行,任何人都能够简单地入手.

第二条精华:深入.

不要认为PHP不能做复杂的东西,看看Symfony2框架就明白了,它是一个让PHP完全面向对象的框架,用它能做很多大家伙.

第三条精华:Web+CLI

现在都是PHP5.4的年代了,PHP的GC已经很不错了,你可以在CLI下面写脚本,就像Python和perl做的那样.

第四条精华:扩展

有啥不够用的东西,用C顶上,没有解决不了的问题.

第五条精华:资源

在Web上有大量超乎想像的PHP资源,反正我认为在Web领域要远超其他语言(JavaScript这货不算)

第六条精华:成熟产品

Drupal, WordPress, Joomla, media wiki, tiki, mantis等等,还有crm相关的,国内也有phpwind, discuz, 方维等各家都在使用PHP做产品,大部分都易于修改扩展.

就这六条,应该满足了.别黑我大PHP了.

转载自:http://saharabear.com/weblog/?p=1718

乱谈PHP

新年第二天,从今天以后的一年里都不再数新年第几天。

整体来讲,我不算一个PHP程序员,因为我的入门语言是C和Java,虽然当时一直不明白为什么要用C,因为做网站用Java比用C写CGI要好得多。

再后来学习的是Python,因为这东西在04到05年不大火,却有很多老外希望用这种语言写东西,会的人少,大赚一笔,为此还去读了一部分Python的代码。

再后来就是写商业的Java项目,从Struts+Spring+Hibernate到Struts2再到Wicket+Guice+IBatis还有Wicket+EJB3最后用上Grails和该死的Groovy,我终于烦了。

其间,一直在写PHP。

为什么我很少参与PHP的活动呢?比如我参与的PHP交流不多,看过的PHP代码也不多,到今天还是没学会怎么真正使用Drupal,只能简单做一做模板。其实就是因为PHP这门语言太好了,根本不用多花时间去研究它的实现,只要有一本手册,无论是Web还是Cli都能够很好地运行,不管是直接用过程式,还是大量利用现在的5.4的新特性,都很容易写好代码,虽然有一些时候,需要工具来监控一下执行过程的内存问题,但比起其他语言来讲,PHP的确是极其容易学习的。

就因为这个,我提到PHP的次数不多,因为不太必要提,因为它易学易用。

但是,最近总有人:又黑我大PHP。

所以,我决定宣布我就是PHP程序员,我不打算再当杂牌程序员,不再说自己是Python程序员或者Java程序员了,因为那样,我再次对别人说:又黑我大PHP的时候,他们就不能再说:谁让你一写Python的来掺和,或者说:你一Java端的,你哪头的。

所以,我是PHP程序员。

Over。

转载自:http://saharabear.com/weblog/?p=1703

安装PHP_Beautifier出现错误

Failed to download pear/PHP_Beautifier, version "0.0.15", latest release is version 0.1.15, stability "beta", use "channel://pear.php.net/PHP_Beautifier-0.1.15" to install
install failed

今天在opensuse上面安装PHP_beautifier出现上面的问题。

如果你也同样出现上面的问题,请使用下面命令进行安装:

sudo pear install PHP_Beautifier-beta

PHP 5.5会有哪些改进

PHP 5.4刚刚发布4个月,现在来谈论下一个版本的PHP,可能为时过早,但是在 PHP的内部邮件列表中,这个话题现在很火。

PHP 5.5目前还处于早期发展阶段,最终他会成什么样子,现在没人会知道,下面 只是总结人们对PHP 5.5的期望,但是PHP 5.5并不会包含下面所有的内容。

简单的来说,有4个比较重要的功能点:

  • 一个简单的密码hash函数
  • 类型约束检测 (Scalar typehinting)
  • Getters and seters (属性)
  • 生成器 (Generators)

其他的很多细节:

不支持Windows XP和2003

PHP 5.5将不再支持Windows XP和2003系统,这些系统太老了。

/e修饰符定义为过时的

/e修饰符被设置之后 preg_replace()在进行了对替换字符串的后向引用替换之后, 将替换后的字符串作为php代码评估执行(eval函数方式), 并使用执行结果作为实际 参与替换的字符串. 单引号, 双引号, 反斜线(\)和NULL字符在 后向引用替换时会 被用反斜线转义。

这将会导致安全问题,作为替代,应该使用preg_replace_callback函数。

boolval()

PHP已经实现了strval, intval, floatval函数,转换为bool类型的boolval函数将会被添加。 他和(bool)的转换是一样的,但是他可以作为回调函数使用。

array_column()

array_colume或者array_pluch函数将会表现如下:

<?php

$userNames = array_column($users, 'name');
// 等同于下面代码
$userNames = [];
foreach ($users as $user) {
    $userNames[] = $user['name'];
}

一个简单的密码hash函数

最近很多大型网站泄漏密码(注意这个的泄漏密码不是国内的各种密码门,老外才不关心国内呢,在国外也 有很多类是国内的密码泄漏事件),对于密码我们一直提倡使用bcrypt进行加密,但还是有很多人使用不 安全的sha1哈希。(国内、外的密码泄漏,虽然都是泄漏,但国外泄漏是被哈希过的密码,而国内直接是 明文)

我估计这可能是使用crypt函数太困难的(sha1, md5的确很简单),因为我们需要一个简单而且安全的密码 哈希函数:

<?php

$password = "foo";

// 创建一个密码哈希
$hash = password_hash($password, PASSWORD_BCRYPT);

// 验证一个密码
if (password_verify($password, $hash)) {
    // 密码正确!
} else {
    // 密码错误!
}

新的密码哈希有更多的更能,这里是 RFC 概述。

常量引用 (Constant dereferencing)

“Constant dereferencing” 是指可以直接用操作数组的方式来操作字符串,看下面2个例子:

<?php

function randomHexString($length) {
    $str = '';
    for ($i = 0; $i < $length; ++$i) {
        $str .= "0123456789abcdef"[mt_rand(0, 15)]; // direct dereference of string
    }
}

function randomBool() {
    return [false, true][mt_rand(0, 1)]; // direct dereference of array
}

我不认为应该使用该特性,但他让语言语法多了一点。查看 RFC

empty()函数将支持参数是一个函数调用(和其他表达式)

目前的empty()函数的参数只能是一个变量,PHP 5.5中将支持emtpy($this->getFriends())这样使用,在之前的版本 这将会抛出一个错误。查看 RFC

获取完整的类名

PHP 5.3中增加了命名空间的特性。就导致下面代码的问题:

<?php

use Some\Deeply\Nested\Namespace\FooBar;

// 这个代码不能工作,因为他会在全局中查找FooBar类,显然这是找不到的
$reflection = new ReflectionClass('FooBar');

为了解决这个问题引入一个新的语法FooBar::class返回完整的类名

use Some\Deeply\Nested\Namespace\FooBar;

// this works because FooBar::class is resolved to "Some\\Deeply\\Nested\\Namespace\\FooBar"
$reflection = new ReflectionClass(FooBar::class);

更多示例查看 RFC

函数参数可以跳过(Parameter skipping)

如果你有一个函数接受多个可选参数,目前你没有办法使用中间参数的默认值,新增的default可以做到使用某些参数的默认值, 而不是在调用函数的地方再定义一遍值。

下面是 RFC 中的一个例子:

function create_query($where, $order_by, $join_type='', $execute = false, $report_errors = true) { ... }

如果我们要设置 $report_errors = false 而不需要设置 $join_type和 $execute参数,我们就可以使用下面代码:

create_query("deleted=0", "name", default, default, false);

类型约束检测

检测类型约束原本是要包含中PHP 5.4中的,但是由于未达成共识,他没有被包含进PHP 5.4。

现在PHP 5.5又开始讨论提议他了。查看 RFC

他会检测并转换输入值的类型,但只有在转换不丢失数据的情况下,例如:123, 123.0, “123″ 都是一个int类型参数的 有效值,但”hello word.”不会。

function foo(int $i) { ... }

foo(1);      // $i = 1
foo(1.0);    // $i = 1
foo("1");    // $i = 1
foo("1abc"); // not yet clear, maybe $i = 1 with notice
foo(1.5);    // not yet clear, maybe $i = 1 with notice
foo([]);     // error
foo("abc");  // error

新的Getters和 Setters语法

如果你是 getXYZ()和 setXYZ()写法的粉丝,那么这是一个可喜的变化。PHP 5.5中提议增加一个对于直接属性读写的 语法:

<?php

class TimePeriod {
    public $seconds;

    public $hours {
        get { return $this->seconds / 3600; }
        set { $this->seconds = $value * 3600; }
    }
}

$timePeriod = new TimePeriod;
$timePeriod->hours = 10;

var_dump($timePeriod->seconds); // int(36000)
var_dump($timePeriod->hours);   // int(10)

也有一些其他的特性,例如只读的属性,更多内容可以参考 RFC

生成器

目前自定义的迭代器很少被使用,因为使用他太麻烦了,需要很多其他无关的代码(被称为模板代码),生成器提供一个方便 使用迭代器的方法来解决这个问题。

例如,这是一个自定义范围的函数,但他也可以被当迭代器使用:

<?php

function *xrange($start, $end, $step = 1) {
    for ($i = $start; $i < $end; $i += $step) {
        yield $i;
    }
}

foreach (xrange(10, 20) as $i) {
    // ...
}

上面xrange函数具有同内置的range函数相同的功能,唯一不同的是他返回的不是一个包含了所有值的数组,而是一个迭代器 生成的动态值。

更多的功能可以参考 RFC

列表内涵和生成器表达式

列表内涵提供一个简单的访问数组的方式:

$firstNames = [foreach ($users as $user) yield $user->firstName];

上面类表内涵等同于下面代码:

$firstNames = [];
foreach ($users as $user) {
    $firstNames[] = $user->firstName;
}

他也可以过滤数组:

$underageUsers = [foreach ($users as $user) if ($user->age < 18) yield $user];

生成器表达式类是,但他返回的是一个迭代器动态生成的值,而不是一个数组。

(End)

PHP中文手册(最新)

做了个PHP中文手册的镜像,地址:http://www.zzphp.net/doc/

为什么要做个这个手册呢:

  1. 现有的PHP中文手册都太旧,有些都好些年没更新过了。
  2. 官方的中文手册有些不太好用,特别是查看函数的时候,记得有这个函数,又想不起来这个函数名。

这个PHP中文手册要做的:

  1. 尽量保持最新的内容,每月更新一次应该没什么问题。
  2. 在这个PHP中文手册的基础上做一些东西,方便使用的,比如函数快速查找等等的一些实用东西。
  3. 也作为PHP官方中文手册的一个镜像。

当然,这些需要时间来做,目前就是原版的官方中文手册上线。

(End)