PHP浮点数

<?php
    $f = 0.58;
    var_dump(intval($f * 100)); //为啥输出57

为啥输出是57啊? PHP的bug么?

我相信有很多的同学有过这样的疑问, 因为光问我类似问题的人就很多, 更不用说bugs.php.net上经常有人问…

要搞明白这个原因, 首先我们要知道浮点数的表示:

浮点数, 以64位的长度为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

这里的关键点就在于, 小数在二进制的表示, 关于小数如何用二进制表示, 大家可以百度一下, 我这里就不再赘述, 我们关键的要了解, 0.58 对于二进制表示来说, 是无限长的值(下面的数字省掉了隐含的1)..

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995

0.58 * 100 = 57.999999999

那你intval一下, 自然就是57了….

可见, 这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的”

so, 不要再以为这是PHP的bug了, 这就是这样的…..

PHP中使用strlen和mb_strlen, iconv_strlen的区别

在PHP中,我们常常需要处理字符串长度。比如说,有些情况下需要把过长的字符串截短一点,然后存入数据库或者文件。

今天的程序中,出现了一个bug,很简单的问题,postgresql说无法存储字符串,说是无效的UTF-8字符。很好奇,数据库到程序包括页面都是UTF-8,为什么会出现这个问题。仔细读了程序,原来有一个地方是使用的strlen获取字符串长度,而字符串是中文。根据这个strlen获取了长度后,又继续通过substr进行了字符串截取。这样肯定会有问题,因为里面的字符都是中文的,至少应该处理一下.

那么,可以这么讲,在php的中文编程中,基本上可以先不使用strlen这个方法了,直接使用mb_strlen不可以了。因为mb_strlen能够处理多语言字符,当然你要安装php-mbstring.

在使用mb_strlen的时候,如果不确定运行时是否为utf-8,可以用 mb_internal_encoding(“UTF-8″);来处理。不过用mb_strlen第二个参加添加utf-8也可以。

对于iconv_strlen,我用得不多,不过我觉得这个方法和mb_strlen是一样的,只要设置的字符集没问题。

反正,别用strlen了,除非是判断纯拉丁字符。

解决php内存泄露问题

网站每天大概有60万ip/300万pv的访问,网站产品很复杂,代码结构差,开发工程师来来去去,代码只能只读了。突然有一天开始频繁出现php-fpm进程耗光内存和cpu占有率飙升,前端频繁出现504错误

php-fpm进程耗光内存 这个就是传说中的内存泄露,所谓内存泄露,是指进程在运行过程中,内存占用率逐步上升而不释放,导致系统可用内存越来越少的情况

严格上说,这个也不算致命错误,“内存泄露”只对长期运行的程序有威胁,对单一任务的执行脚本不需要担心

最简单的处理方式,是定时重启进程。php-fpm的配置信息里面有个max_request,就定义一个fastcgi进程处理完多少个请求之后退出这样系统可用释放掉内存,但是如果内存占用率增长速度非常快,频繁重启进程,就会影响服务的稳定性,所以这个问题必须正面解决

内存泄露排查非常困难

  • 因为代码规模非常大,想靠做code review的方式来查基本上不可能
  • php并非运行在虚拟机上,没有什么官方的monitor(类似java hprof,JVM Monitor等)
  • 在互联网上搜索,找不到任何答案

探索解决问题

  1. 使用 valgrind 调试php-cgi进程

    Valgrind 是一个linux常用的程序的内存调试和代码剖析,对调试C/C++程序的内存泄露很有帮助,它的机制是在系统alloc/free等函数调用上加计数。用 valgrind 调试php-cgi,要求php-cgi 是debug版本,实践证明行不通:

    1. php-cgi debug 版本放在线上根本跑不起来,性能太差
    2. php程序的内存泄露,是由于一些循环引用,或者gc的逻辑错误,valgrind无法探测,它适合去检查php解释器是否有内存泄露问题
  2. php解释器(Zend core)自带有检查内存泄露的机制

    php解释器的核心代码叫做(Zend Core) 在用valgrind 调试php-cgi进程,我查看了php-cgi的代码,发现zend core 实现了内存泄露的自我检查 但是 同上原因,php-cgi debug 跑不起来,也无法得到调试信息

  3. FreeBSD 的 DTrace

    DTrace是freebsd 系统支持的核心调试器,可以在各个系统函数调用上加计数点,twitter曾经用过。这个方法最后没有使用 有如下原因:

    1. 需要找一台服务器安装freebsd,并部署到线上、或者模拟负载,非常繁琐

    2. 我仔细研究了DTrace的文档,发现这个可以认为是增强的 valgrind,也不能解决我们的问题

这3种方法都不行,陷入困境.但是换个角度思考:虽然解决php程序内存泄露没有方便的工具,但是 web 程序是按请求切分的,一个http请求,对应的php进程执行一个php文件

一个自然的想法是,记录 每次 http请求处理前后php进程的内存占用率之差,然后对结果排序,就能找出,让进程内存增加可能性最大的 文件 ,这个文件导致内存泄露的可能性最大

计算进程内存占用率有两种方式

php内置函数 memory_get_usage

  1. 这个函数是 Zend Core里面一个计数器,是zend core认为的内存使用量,但是php内存泄露有可能是zend core逻辑错误导致的,所以memory_get_usage不一定可靠

  2. linux 系统文件 /proc/{$pid}/status 会记录某个进程的运行状态,里面的 VmRSS 字段记录了该进程使用的常驻物理内存(Residence),这个就是该进程实际占用的物理内存了,用这个数据比较靠谱,在程序里面提取这个值也很容易

找到思路,就可以开始动手写程序

直接修改了php-cgi的源代码,在main.c里面处理每个fastcgi请求前后分别加计数代码,输出日志到log文件,重新编译上线

运行30分钟之后,执行

cat short.log| awk '{print $3 "\t" $7 "\t" $6 "\t" $4$5}' |sort -r -n |head -n 100

很容易找到最可能出现内存泄露的代码文件,然后进一步排查,重构代码,这就很简单了:能不加载的文件就不加载,大数组用完之后赶紧unset ….

更好的办法

后来,我才发现其实不需要去修改php的源代码,php.ini配置文件里面有两个配置项: auto_append_file,auto_prepend_file,可以在请求前后注入代码 ….

web程序做性能优化也是这个思路,但是要简单很多,无需写代码,在nginx log里面加上$request_time ,用awk/sort 处理一下就可以找出瓶颈。

PHP编码规范:日志接口

本文档用来描述日志类库的通用接口。

主要目标是让类库获得一个Psr\Log\LoggerInterface对象并且使用一个简单通用的方式来写日志。有自定义需求的框架和CMS可以根据情况扩展这个接口,但应当保持和该文档的兼容性,这将确保使用第三方库和应用能统一的写应用日志。

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

关键词实现者在这个文档被解释为:在日志相关的库和框架实现LoggerInterface接口的人。用这些实现的人都被称作用户

1. 规范

1.1 基础

  • LoggerInterface暴露八个接口用来记录八个等级(debug, info, notice, warning, error, critical, alert, emergency)的日志。

  • 第九个方法是log,接受日志等级作为第一个参数。用一个日志等级常量来调用这个方法的结果必须和调用具体等级方法的一致。如果具体的实现不知道传入的不按规范的等级来调用这个方法必须抛出一个Psr\Log\InvalidArgumentException。用户不应自定义一个当前不支持的未知等级。

1.2 消息

  • 每个方法都接受字符串,或者有__toString方法的对象作为消息。实现者可以对传入的对象有特殊的处理。如果不是,实现者必须将它转换成字符串。

  • 消息可以包含可以被上下文数组的数值替换的占位符。

    占位符名字必须和上下文数组键名对应。

    占位符名字必须使用使用一对花括号为分隔。在占位符和分隔符之间不能有任何空格。

    占位符名字应该A-Za-z0-9,下划线_和句号.。其它的字符作为以后占位符规范的保留。

    实现者可以使用占位符来实现不同的转义和翻译日志成文。用户在不知道上下文数据是什么的时候不应提前转义占位符。

下面提供一个占位符替换的例子,仅作为参考:

function interpolate($message, array $context = array())
{
    // build a replacement array with braces around the context keys
    $replace = array();
    foreach ($context as $key => $val) {
        $replace['{' . $key . '}'] = $val;
    }
    // interpolate replacement values into the message and return
    return strtr($message, $replace);
}
// a message with brace-delimited placeholder names
$message = "User {username} created";

// a context array of placeholder names => replacement values
$context = array('username' => 'bolivar');

// echoes "Username bolivar created"
echo interpolate($message, $context);

1.3 上下文

  • 每个方法接受一个数组作为上下文数据,用来存储不适合在字符串中填充的信息。数组可以包括任何东西。实现者必须确保他们对上下文数据足够的掌控。在上下文中一个给定值不可抛出一个异常,也不可产生任何PHP错误,警告或者提醒。

  • 如果在上下文中传入了一个异常对象,它必须以exception作为键名。记录异常轨迹是通用的模式,如果日志底层支持这样也是可以被允许的。实现者在使用它之前必须验证exception的键值是不是一个异常对象,因为它可以允许是任何东西。

1.4 助手类和接口

  • Psr\Log\AbstractLogger类让你非常简单的实现和扩展LoggerInterface接口以实现通用的log方法。其他八个方法将会把消息和上下文转发给它。

  • 类似的,使用Psr\Log\LoggerTrait只需要你实现通用的log方法。记住traits不能实现接口前,你依然需要implement LoggerInterface

  • Psr\Log\NullLogger是和接口一个提供的。它可以为使用接口的用户提供一个后备的“黑洞”。如果上下文数据非常重要,这不失为一个记录日志更好的办法。

  • Psr\Log\LoggerAwareInterface只有一个setLogger(LoggerInterface $logger)方法可以用来随意设置一个日志记录器。

  • Psr\Log\LoggerAwareTraittrait可以更简单的实现等价于接口。通过它可以访问到$this->logger

  • Psr\Log\LogLevel类拥有八个等级的常量。

2. 包

作为psr/log 的一部分,提供接口和相关异常类的一些描述以及一些测试单元用来验证你的实现。

3. Psr\Log\LoggerInterface

<?php

namespace Psr\Log;

/**
* Describes a logger instance
*
* The message MUST be a string or object implementing __toString().
*
* The message MAY contain placeholders in the form: {foo} where foo
* will be replaced by the context data in key "foo".
*
* The context array can contain arbitrary data, the only assumption that
* can be made by implementors is that if an Exception instance is given
* to produce a stack trace, it MUST be in a key named "exception".
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
* for the full interface specification.
*/
interface LoggerInterface
{
    /**
    * System is unusable.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function emergency($message, array $context = array());

    /**
    * Action must be taken immediately.
    *
    * Example: Entire website down, database unavailable, etc. This should
    * trigger the SMS alerts and wake you up.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function alert($message, array $context = array());

    /**
    * Critical conditions.
    *
    * Example: Application component unavailable, unexpected exception.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function critical($message, array $context = array());

    /**
    * Runtime errors that do not require immediate action but should typically
    * be logged and monitored.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function error($message, array $context = array());

    /**
    * Exceptional occurrences that are not errors.
    *
    * Example: Use of deprecated APIs, poor use of an API, undesirable things
    * that are not necessarily wrong.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function warning($message, array $context = array());

    /**
    * Normal but significant events.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function notice($message, array $context = array());

    /**
    * Interesting events.
    *
    * Example: User logs in, SQL logs.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function info($message, array $context = array());

    /**
    * Detailed debug information.
    *
    * @param string $message
    * @param array $context
    * @return null
    */
    public function debug($message, array $context = array());

    /**
    * Logs with an arbitrary level.
    *
    * @param mixed $level
    * @param string $message
    * @param array $context
    * @return null
    */
    public function log($level, $message, array $context = array());
}

4. Psr\Log\LoggerAwareInterface

<?php

namespace Psr\Log;

/**
* Describes a logger-aware instance
*/
interface LoggerAwareInterface
{
    /**
    * Sets a logger instance on the object
    *
    * @param LoggerInterface $logger
    * @return null
    */
    public function setLogger(LoggerInterface $logger);
}

5. Psr\Log\LogLevel

<?php

namespace Psr\Log;

/**
* Describes log levels
*/
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}

PHP编码规范:基本代码规范

本节标准包含了成为标准代码所需要的基本元素,以确保高级技术特性可以在PHP代码中共享。

RFC 2119中的特性关键词”必须”(MUST),“不可”(MUST NOT),“必要”(REQUIRED),“将会”(SHALL),“不会”(SHALL NOT),“应当”(SHOULD),“不应”(SHOULD NOT),“推荐”(RECOMMENDED),“可以”(MAY)和“可选”(OPTIONAL)在这文档中将被用来描述。

1. 大纲

  • 文件必须使用 <?php<?= 标签。

  • 文件必须使用不带BOM的UTF-8代码文件。

  • 文件应当声明符号(类,函数,常量等…)或者引起副作用(例如:生成输出,修改.ini配置等),但不能同时存在。

  • 命名空间和类名必须遵守 PSR-0

  • 类名必须使用骆驼式StudlyCaps写法 (译者注:驼峰式的一种变种,后文将直接用StudlyCaps表示)。

  • 类名常量必须使用全大写和下划线分隔符。

  • 方法名必须使用驼峰式cameCase写法(译者注:后文将直接用camelCase表示)。

2. 文件

2.1. PHP标签

PHP代码必须使用长标签<?php ?>或者短输出式<?= ?>标签;它不可使用其他的标签变种。

2.2. 字符编码

PHP代码必须只使用不带BOM的UTF-8。

2.3. 副作用

一个文件应当声明新符号 (类名,函数名,常量等)并且不产生副作用,或者应当执行有边缘影响的逻辑,但不能同时使用。

短语”副作用”意思是不直接执行逻辑的类,函数,常量等 仅包括文件

“副作用”包含但不局限于:生成输出,明确使用requireinclude,连接外部服务,修改ini配置,触发错误和异常,修改全局或者静态变量,读取或修改文件等等

下面是一个例子文件同时包含声明和副作用 即避免的例子:

<?php   
// side effect: change ini settings
ini_set('error_reporting', E_ALL);

// side effect: loads a file
include "file.php";

// side effect: generates output
echo "<html>\n";

// declaration
function foo()
{
    // function body
}

下面这个例子仅仅包含声明并且没有副作用; 即需要提倡的例子:

<?php
// declaration
function foo()
{
    // function body
}

// conditional declaration is *not* a side effect
if (! function_exists('bar')) {
    function bar()
    {
        // function body
    }
}

3. 命名空间和类名

命名空间和类名必须遵守 PSR-0: http://www.zzphp.net/?p=278.

这意味着每个类只能是一个文件本身,并且至少有一个层级的命名空间:顶级的组织名称。

类名必须使用骆驼式StudlyCaps写法

代码必须使用PHP5.3及以后编写正式的命名空间 例子:

<?php
// PHP 5.3 and later:
namespace Vendor\Model;

class Foo
{
}

代码使用5.2.x及之前编写应当使用Vendor_作为前缀的伪命名空间作为类

<?php
// PHP 5.2.x and earlier:
class Vendor_Model_Foo
{
}

4. 类常量,属性和方法

术语“类”指所有的类,接口和特性(traits)

4.1. 常量

类常量必须使用全大写,分隔符使用下划线作为声明。 例子:

<?php
namespace Vendor\Model;

class Foo
{
    const VERSION = '1.0';
    const DATE_APPROVED = '2012-06-01';
}

4.2. 属性

本手册有意避免推荐使用$StulyCaps$cameCase或者unser_score作为属性名字

不管名称约定是不是在一个应当接受的合理范围。这个范围可能是组织,包,类,方法。

4.3. 方法

方法名必须用cameCase()写法

PHP编码规范:自动加载器特性强制性要求

下面描述了关于自动加载器特性强制性要求:

强制性

  • 一个完全标准的命名空间必须要有一下的格式结构\<Vendor Name>\(<Namespace>\)*<Class Name>
  • 命名空间必须有一个顶级的组织名称 (“Vendor Name”).
  • 命名空间中可以根据情况决定使用多少个子空间
  • 命名空间中的分隔符当从文件系统加载的时候将被映射为 DIRECTORY_SEPARATOR
  • 命名空间中的类名中的_没有特殊含义,也将被作为DIRECTORY_SEPARATOR对待.
  • 命名空间中的类名在从文件系统加载时文件名都需要以.php结尾
  • 组织名,空间名,类名都可以随意选择使用大小写英文字符

示例

  • \Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
  • \Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
  • \Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
  • \Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php

命名空间和类名中的下划线

  • \namespace\package\Class_Name => /path/to/project/lib/vendor/namespace/package/Class/Name.php
  • \namespace\package_name\Class_Name => /path/to/project/lib/vendor/namespace/package_name/Class/Name.php

以上是我们为实现无痛的自动加载特性设定的最低标准。你可以按照此标准实现一个SplClassLoader在PHP 5.3中去加载类。

实例

下面是一个函数实例简单展示如何使用上面建议的标准进行自动加载

<?php

function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strrpos($className, '\\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

    require $fileName;
}

SplClassLoader实现

下面的gist是一个SplClassLoader实例可以按照上面建议的自动加载特性来加载类。这也是我们当前推荐在PHP5.3中按照上述标准加载类的方式

不带Doctrine的Symfony2是什么样?

这是一个奇怪话题,因为Doctrine是Symfony2的几个核心之一(另外几个是DI, Twig, 集成的单元测试).

怎么会突然想到不带Doctrine的Symfony2呢?因为最近用Slim用得很爽,直接是Twig+Slim+PDO,虽然有点阳春白雪,但是用起来果断很爽,比原生写PHP要方便很多,比Symfony2要轻便很多.

仔细对比了一会,最大的区别应该还是Form的处理,换句话说,核心依然是Doctrine,在为没有Doctrine就没有方便的操作Form的接品,这种事情让我突然有一种奇怪的感觉:也许Symfony2走到最后,不只是需要一些代码生成器,更需要一个IDE.

如果使用Symfony2但却不使用Doctrine,会怎么样呢?

相对来说,当再做Symfony2项目时,假如没有Doctrine,那么应该更适合从页面入手,先做一个页面出来,然后考虑需要展示的数据有哪些,再然后呢?做一个简单的数据表,然后在Service中处理一下db操作,写一个单元测试.

这个数据链接要么是写一个pdo的单例,要么是直接从entity manager中获取,用完应该记得关闭.

似乎这样用Symfony2比直接用Doctrine要复杂?

那么是不是说明DI应该配合ORM来用,而不应该直接用?

这个问题在Java时代就开始思考,这么做也许可以,也许不可以,不过Symfony2很适合多人协作,Slim更适合一个人开发.Slim要想多人开发自然也可以,只是分隔性不如Symfony2.

我思考问题太简单了,哪能只考虑这些,可能是因为今天写了一天代码,头有点晕吧.不管了,睡觉,明天再继续.

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

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/

PHP文件头部空白影响CSS布局

在编写PHP文件过程中,发现在浏览器预览PHP文件时,顶部会出现一行空白,影响了页面的布局。

关于BOM header的解释如下:

通常情况下,使用Windows系统自带的记事本程序编写网页程序,但在编写或修改php博客系统代码后,进行调试时总是会出现如同以下几点问题:

–不能登入或者不能登出; –页顶出现一条空白; –页顶出现错误警告; –其它不正常的情况。

分析原因: 由于使用UTF-8编码,在编写或修改代码后都保存为utf-8编码格式。虽然现在几乎所有的文本编辑软件都可以显示并编辑UTF-8编码的文件,但是很遗憾的是其中很多软件的表现并不理想。

类 似WINDOWS自带的记事本等软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(_0xEF _0xBB _0xBF,即BOM——Byte Order Mark)。它是一串隐藏的字符,用于让记事本等编辑器识别这个文件是否以UTF-8编码。对于一般的文件,这样并不会产生什么麻烦。但对于 PHP来说,PHP在设计时就没有考虑BOM的问题,不会忽略UTF-8编码的文件开头BOM的那三个字符,会把BOM作为该文件开头正文的一部分。由于必须在<?或者<?php后面的代码才会作为PHP代码执行,所以将会造成在页面上输出这三个字符,显示效果就要看浏览器了,一般是一个空行或是一个乱码。由于在html一开头有这3个字符的存在,即使页面的 top padding 设置为0,也无法让整个网页紧贴浏览器顶部。由于受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在 COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIE、SESSION实现的功能全部无效。

解决办法:

在编辑、更改任何文本文件时,请务必使用不会乱加BOM的编辑器。Linux下的编辑器应该都没有这个问题。WINDOWS下,请勿使用记事本等编辑器。推荐的编辑器是: Editplus 2.12版本以上; EmEditor; UltraEdit(需要取消‘添加BOM’的相关选项); Dreamweaver(需要取消‘添加BOM’的相关选项); Notepad(需要进行“转换为不带BOM的UTF-8”)等。

对于已经添加了BOM的文件,要取消的话,可以用以上编辑器另存一次。(Editplus需要先另存为gb,再另存为UTF-8。Dreamweaver在”页面属性“的 “包含Unicode 签名(BOM)”取消即可)

转载自:http://www.cnblogs.com/newsouls/archive/2013/01/11/2856518.html

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