首页 > 编程技术 > php

PHP编译安装后PHP-FPM使用笔记

发布时间:2016-11-25 16:18

PHP-FPM我们相信各位用高版本的php经常使用到了,下面整理了一些关于PHP-FPM的笔记,有兴趣的可进来看看。


今天赶上了123System OPenVZ VPS全场半价的机会,购入了一台512MB内存、双核3.49Ghz Xeon E3-1270 V3 CPU的套餐,这是第一次使用123system的产品,整体印象非常不错,就目前而言速度也是杠杠哒,便手动配置起了最新版的Tengine和PHP 5.6.8。在编译完成之后我决定以PHP -FPM的方式来运行PHP,下面是从各处收集到并且重新整理的一些关于FPM使用的技巧,记录一下方便自己与一些平时接触到这方面工作的朋友吧!

1、安装完之后,对php-conf的修改:
修改用户组:

user = www-data
group = www-data
如果www-data用户不存在,那么执行linux命令先添加www-data用户

groupadd www-data
useradd -g www-data www-data
部分参数解析:

pid = run/php-fpm.pid
#pid设置,默认在安装目录中的var/run/php-fpm.pid,建议开启

error_log = log/php-fpm.log
#错误日志,默认在安装目录中的var/log/php-fpm.log

log_level = notice
#错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice.

emergency_restart_threshold = 60
emergency_restart_interval = 60s
#表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。

process_control_timeout = 0
#设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0.

daemonize = yes
#后台执行fpm,默认值为yes,如果为了调试可以改为no。在FPM中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。

listen = 127.0.0.1:9000
#fpm监听端口,即nginx中php处理的地址,一般默认值即可。可用格式为: 'ip:port', 'port', '/path/to/unix/socket'. 每个进程池都需要设置.

listen.backlog = -1
#backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41

listen.allowed_clients = 127.0.0.1
#允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. 如果没有设置或者为空,则允许任何服务器请求连接
2、对Nginx.conf的fastcgi部分修改:

location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
3、PHP-FPM启动相关参数

#测试php-fpm配置
/usr/local/php/sbin/php-fpm -t
/usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf -t

#启动php-fpm
/usr/local/php/sbin/php-fpm
/usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf

#关闭php-fpm
kill -INT `cat /usr/local/php/var/run/php-fpm.pid`

#重启php-fpm
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
4、Nginx的开启与关闭

结束进程:fuser -k 80/tcp
启动Nginx:sudo /usr/local/nginx/nginx

如果我们做大点的网站对于缓存我们用到是非常的多了,因为缓存可以让网站运行更多加的快了,下面一起来了解一下关于php 页面输出缓存控制


在php所谓的输出缓冲,就是代码中的echo 或者其他输出命令在执行的时候是先写入到 php buffer,在脚本执行完或者强制执行输出缓存的命令后,才会把数据输出到浏览器(其中php buffer 就是php.ini中设置的output_buffering ,默认是on,表明无限制大小,可以换成数字来限制大小)。

例子:


echo 'www.111cn.net';
echo '技术';
echo '分享';

这两个echo 是按顺序插入到缓冲区的,只有脚本执行完成或者强制执行缓存输出才会把数据输出到浏览器。
如果我想要实时地输出echo的数据,见下面的代码:

ob_end_flush(); //关闭php缓存,或者在flush()前先执行ob_flush(),下面有解释
echo str_pad(" ", 256); 
for ($i=5; $i>0; $i--) { 
   echo $i. '<br>'; 
   flush(); 
   sleep(1);  
}

注意:

1:flush和ob_flush 区别:

乍看之下两者很像,而且很多手册的解释也不清楚,模凌两可,其实两者是有很大区别的。

当php.ini没有开启php buffer缓存时,php脚本输出的内容都会在服务端处于等待输出状态 ,不会保存到输出缓存,因为缓存都没开,此时利用flush可以将那些等待输出的内容立即输出来发到客户端(浏览器或者其他输出端)。

当php.ini开启了php buffer缓存后,php脚本输出内容的第一步是存储在输出缓存中 ,此时等到输出的内容是没有数据的,用flush的话是无效果,取不到数据的。因此要先利用ob_flush把输出缓存中的内容取出来变成等待输出的状态,接下来在利用flush把内容发到客户端。执行的顺序是先ob_flush 再 flush。

因此要实现实时地输出,要么利用ob_end_flush 先关掉php 输出缓存后直接flush,要么先 ob_flush再flush。

2:浏览器无法输出实时数据

把代码改成下面的代码,在chrome firefox ie等浏览器都是一次性输出的,很奇葩的现象:

ob_end_flush(); //关闭php缓存,或者在flush前ob_flush();
echo str_pad(" ", 256); 
for ($i=5; $i>0; $i--) { 
   echo $i; 
   flush(); 
   sleep(1);  
}

找了半天的bug,终于发现了个现象,只要顺便加个html标签,即可实时输出。
原因是:只有在遇到html标签的时候才会即时输出,真是神奇,还好一般输出的内容都会带着html标签,很少纯文本。
解决办法:加个回车或者其他的html标签即可解决问题。

一:下面详细介绍out control 的相关函数

1、flush //将等待输出的内容发送带浏览器,不会对缓存区有影响。

2、ob_flush // 将缓存区的内容变成等待输出状态,数据还没有输出到客户端。

3、ob_start(callback) // 打开输出缓冲区,可以加入回调的callback函数,实现在输出之前执行想要的功能。

例子(1)


ob_start('callbackFun');
echo '1111111';

function callbackFun($string){
  return md5($string);//进行md5加密
}
结果:

1
e10adc3949ba59abbe56e057f20f883e
浏览器返回的结果是加密过的字符串,而不是 1111111,说明在数据存入输出缓存之前执行了回调函数对数据进行了md5加密后才存入输出缓存的。

例子(2)

结合ob_gzhandler实现网页内容的gzip压缩,减少传输的大小,提高页面的加载速度。


ob_start('ob_gzhandler');
echo str_repeat('hlmblog', 1024);

使用了ob_gzhandler 后页面的大小:
QQ截图20140530175025

 

没使用ob_gzhandler 的页面大小

 

QQ截图20140530175056

 

明显压缩后页面的大小变小了很多。

例子(3)

可以嵌套ob_start,但是记得要有对应的闭合,两个一一对应,不然会报错,或者取不到数据。

ob_start();
var_dump(1);
ob_start();
var_dump(2);
ob_end_flush();
ob_end_flush();

结果:

int 1
int 2
4、ob_get_contents() 获取输出缓冲区的内容或者页面输出的内容

echo str_pad('', 1024);//使缓冲区溢出
ob_start();//打开缓冲区
phpinfo();
$content = ob_get_contents();//获取缓冲区内容
$f = fopen('./phpinfo.txt', 'wb');//打开文件
fwrite($f, $content);//内容写入txt文件
fclose($f);//关闭文件
ob_end_clean();//关闭缓冲区,输出数据并清空浏览器
//ob_end_flush();//发送缓冲区内容到客户端,并关闭缓冲区,不清空浏览器
注意:

这时候浏览器将什么都不输出,因为使用了ob_end_clean() 清空掉了,但是会在当前目录产生一个phpinfo.txt 的文件,里面是获取到的phpinfo信息。

如果使用ob_end_flush,不仅会生成phpinfo.txt 文件,而且还在浏览器输出信息。

5、ob_get_length() 返回输出缓冲区的内容长度

echo str_pad('', 1024);//缓冲区溢出
ob_start();//打开缓冲区
phpinfo();
$string = ob_get_contents();//获取缓冲区内容
$length = ob_get_length();//获取缓冲区内容长度
$re = fopen('./phpinfo.txt', 'wb');
fwrite($re, $string);//将内容写入文件
fclose($re);
var_dump($length); //输出长度
ob_end_flush();//输出并关闭缓冲区

6、ob_get_level() //获取输出缓冲区嵌套级别,就是所处的级别位置


ob_start();
var_dump(ob_get_level());
ob_start();
var_dump(ob_get_level());
ob_end_flush();
ob_end_flush();

浏览器会输出对应的级别:


int 2
int 3
7、ob_get_status() //获取当前缓冲区的状态,返回的是一个数组信息


ob_start('ob_gzhandler');
var_dump(ob_get_status());
ob_start();
var_dump(ob_get_status());
ob_end_flush();
ob_end_flush();

array
  'level' => int 2
  'type' => int 1
  'status' => int 0
  'name' => string 'ob_gzhandler' (length=12)
  'del' => boolean true
array
  'level' => int 3
  'type' => int 1
  'status' => int 0
  'name' => string 'default output handler' (length=22)
  'del' => boolean true

返回数组参数详解:
level:嵌套级别,和ob_get_level()获取到的值是一样的。
type :处理缓冲类型,0是系统内部自动处理,1是用户手动处理。
status :缓冲处理状态, 0是开始, 1是进行中, 2是结束。
name: :定义的输出处理函数名称,就是ob_start() 函数中第一个参数的回调函数。
del :是否运行了删除缓冲区操作。

8、ob_list_handlers() //获得处理程序的函数名数组,也就是ob_start函数传入的第一个参数函数名


print_r(ob_list_handlers());
ob_end_flush();
  
ob_start("ob_gzhandler");
print_r(ob_list_handlers());
ob_end_flush();
浏览器输出对应的处理函数信息数组:


Array ( [0] => default output handler ) Array ( [0] => ob_gzhandler )

9、ob_implicit_flush() // 打开或关闭绝对刷送模式,就是每一次输出后自动执行 flush(),不用对应地写多次的flush,节省代码,提高效率


echo str_pad('', 1024);//缓冲区溢出
ob_end_flush();
ob_implicit_flush(true);//打开绝对刷送
echo 'hlmblog</br>';
//flush(); 
sleep(1);
echo '分享</br>';
//flush();
sleep(1);
echo '技术</br>';
浏览器实时的输出下面:

hlmblog
分享
技术
10、ob_end_flush() //发送内部缓冲区的内容到浏览器,并且关闭输出缓冲区

11、ob_end_clean() //删除内部缓冲区的内容,并且关闭内部缓冲区

二:控制缓存输出可以用来做什么,具体的几个示例

1:生成静态页面

静态页面的加载速度就是快,这句话是家户喻晓的道理,不用请求数据库,这是多么爽的事情啊。
下面是生成静态页面的例子:

echo str_pad('', 1024);//使缓冲区溢出
ob_start();//打开缓冲区
$content = ob_get_contents();//获取页面输出的内容
$f = fopen('./index.html', 'w');
fwrite($f, $content);//内容写入txt文件
fclose($f);
ob_end_clean();//清空并关闭缓冲区
传说中的静态页面就这样简单的生成。

2:捕获输出


function test($param) {
  if($param) {
    ob_start();
    eval($param);
    $contents = ob_get_contents();
    ob_end_clean();
  }else {
    echo '遗憾的没有输出';
    exit();
  }
  return $contents;
}

PHP的命名空间(namespace)是php5.3之后才有的之前学习php所以没有这个东西了,最近用到了php命名空间了,下面我们一起来看看命名空间namespace用法


现在说这个,感觉有点过时了,但是感觉用namespace的人还是不多,估计还是因为不习惯吧。

class把一个一个function组织起来,namespace可以理解成把一个一个class,function等有序的组织起来。个人觉得,namespace的主要优势有

第一,可以更好的管理代码
第二,文件一多,可以避免class,function的重名
第三,代码可读性增强了

1,定义namespace
 

namespace userCenter;  

//php代码  

  

namespace userCenter\register;  

//php代码  

  

namespace userCenter\login {  

 //php代码  

}


命名空间不能嵌套或在同一代码处声明多次(只有最后一次会被识别)。但是,你能在同一个文件中定义多个命名空间化的代码,比较合适的做法是每个文件定义一个命名空间(可以是相同命名空间)。
2,调用namespace
 
\userCenter\register; //绝对调用 
userCenter\login; //相对调用 
use userCenter\register; //引用空间 
use userCenter\register as reg; //引用空间并加别名 
3,实例说明
login.class.php
 

<?php  

  

namespace userCenter;  

  

function check_username(){  

 echo "login OK<br>";  

}  

  

class login{  

 public function save(){  

 echo "login had saved<br>";  

 }  

}  

?>  

regist.class.php

 

<?php  

namespace userCenter\regist  

{  

 function check_username() {  

 echo "regist OK<br>";  

 }   

  

 class regist{  

 public function save(){  

 echo "regist had saved<br>";  

 }  

 }  

}  

  

?>


test.php
 

<?php  

  

require "login.class.php";  

require "regist.class.php";  

  

use userCenter\regist;    //使用use调用空间  

use userCenter\regist as reg;  //as定义别名  

  

echo \userCenter\check_username();  //绝对调用  

  

$login = new \userCenter\login();  

echo $login->save();  

  

echo regist\check_username();    //相对调用  

echo reg\check_username();       //别名调用   

  

$regist = new reg\regist();  

echo $regist->save();


使用use,比绝对调用要好一点,好比给class,function等加了一个前缀,这样看起来就比较清楚了

Doctrine是基于数据库抽像层上的ORM,它可以通过PHP对象轻松访问所有的数据库,例如MYSQL,它支持的PHP最低版本为5.2.3,下面我们一起来看看Doctrine文件上传处理例子,希望文章对各位有帮助。

基本设置


创建一个简单的Doctrine实体类:


// src/Acme/DemoBundle/Entity/Document.php
namespace Acme\DemoBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
 
/**
 * @ORM\Entity
 */
class Document
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    public $id;
 
    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank
     */
    public $name;
 
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    public $path;
 
    public function getAbsolutePath()
    {
        return null === $this->path
            ? null
            : $this->getUploadRootDir().'/'.$this->path;
    }
 
    public function getWebPath()
    {
        return null === $this->path
            ? null
            : $this->getUploadDir().'/'.$this->path;
    }
 
    protected function getUploadRootDir()
    {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir();
    }
 
    protected function getUploadDir()
    {
        // get rid of the __DIR__ so it doesn't screw up
        // when displaying uploaded doc/image in the view.
        return 'uploads/documents';
    }
}

该document实体有一个名称与文件相关联。这个path属性存储一个文件的相对路径并且在数据库中存储。这个getAbsolutePath()会返回一个绝对路径,getWebPath()会返回一个web路径,用于模板加入上传文件链接。


如果你还没有这样做的话,你应该阅读http://symfony.com/doc/current/reference/forms/types/file.html首先了解基本的上传过程。

如果您使用注释来验证规则(如本例所示),请确保你启用了注释验证(见http://symfony.com/doc/current/book/validation.html#book-validation-configuration)。

 

在处理一个实际的文件上传时,使用一个“虚拟”的file字段。例如,如果你在controller中直接构建一个form,他可能是这样的:


public function uploadAction()
{
    // ...
 
    $form = $this->createFormBuilder($document)
        ->add('name')
        ->add('file')
        ->getForm();
 
    // ...
}
下一步,创建file这个属性到你的Document类中并且添加一些验证规则:
use Symfony\Component\HttpFoundation\File\UploadedFile;
 
// ...
class Document
{
    /**
     * @Assert\File(maxSize="6000000")
     */
    private $file;
 
    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
    }
 
    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }
}
annotations
Annotations
// src/Acme/DemoBundle/Entity/Document.php
namespace Acme\DemoBundle\Entity;
 
// ...
use Symfony\Component\Validator\Constraints as Assert;
 
class Document
{
    /**
     * @Assert\File(maxSize="6000000")
     */
    private $file;
 
    // ...
}

 当你使用File约束,symfony会自动猜测表单字段输入的是一个文件上传。这就是当你创建表单(->add(‘file’))时,为什么没有在表单明确设置为文件上传的原因。

下面的控制器,告诉您如何处理全部过程:


// ...
use Acme\DemoBundle\Entity\Document;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
// ...
 
/**
 * @Template()
 */
public function uploadAction(Request $request)
{
    $document = new Document();
    $form = $this->createFormBuilder($document)
        ->add('name')
        ->add('file')
        ->getForm();
 
    $form->handleRequest($request);
 
    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
 
        $em->persist($document);
        $em->flush();
 
        return $this->redirect($this->generateUrl(...));
    }
 
    return array('form' => $form->createView());
}

以前的controller当提交name自动的存储Document实体,但是他不会做任何关于文件的事情并且path属性也将是空白。


处理文件上传一个简单的方法就是在entity持久化之前设置相应的path属性。在某一时刻处理文件上传时,要调用Document实体类一个upload()方法给path赋值。


if ($form->isValid()) {
    $em = $this->getDoctrine()->getManager();
 
    $document->upload();
 
    $em->persist($document);
    $em->flush();
 
    return $this->redirect(...);
}
这个upload()方法利用UploadedFile对象,是它提交后返回file字段:
public function upload()
{
    // the file property can be empty if the field is not required
    // 该file属性为空这个属性就不需要了
    if (null === $this->getFile()) {
        return;
    }
 
    // use the original file name here but you should
    // sanitize it at least to avoid any security issues
    //  这里你应该使用原文件名但是应该至少审核它避免一些安全问题
    // move takes the target directory and then the
    // target filename to move to
    // 将目标文件移动到目标目录
    $this->getFile()->move(
        $this->getUploadRootDir(),
        $this->getFile()->getClientOriginalName()
    );
 
    // set the path property to the filename where you've saved the file
    // 设置path属性为你保存文件的文件名
    $this->path = $this->getFile()->getClientOriginalName();
 
    // clean up the file property as you won't need it anymore
    // 清理你不需要的file属性
    $this->file = null;
}
 使用生命周期回调
生命周期回调是一种有限的技术,他有一些缺点。如果你想移除Document::getUploadRootDir()方法里的写死的编码__DIR__,最好的方法是开始使用Doctrine listeners。在哪里你将能够注入内核参数,如kernel.root_dir来建立绝对路径。
这种原理工作,他有一个缺陷:也就是说当entity持久化时会有什么问题呢?答:该文件已经转移到了它的最终位置,实体类下的path属性不能够正确的实体化。
(如果entity有持久化问题或者文件不能够移动,什么事情也没有发生)为了避免这些问题,你应该改变这种实现方式以便数据库操作和自动删除文件:
/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Document
{
}


接下来,利用这些回调函数重构Document类:


use Symfony\Component\HttpFoundation\File\UploadedFile;
 
/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Document
{
    private $temp;
 
    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
        // check if we have an old image path
        // 检查如果我们有一个旧的图片路径
        if (isset($this->path)) {
            // store the old name to delete after the update
            $this->temp = $this->path;
            $this->path = null;
        } else {
            $this->path = 'initial';
        }
    }
 
    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            // do whatever you want to generate a unique name
            // 去生成一个唯一的名称
            $filename = sha1(uniqid(mt_rand(), true));
            $this->path = $filename.'.'.$this->getFile()->guessExtension();
        }
    }
 
    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->getFile()) {
            return;
        }
 
        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        //当移动文件发生错误,一个异常move()会自动抛出异常。
        //这将阻止实体持久化数据库发生错误。
        $this->getFile()->move($this->getUploadRootDir(), $this->path);
 
        // check if we have an old image
        if (isset($this->temp)) {
            // delete the old image
            unlink($this->getUploadRootDir().'/'.$this->temp);
            // clear the temp image path
            $this->temp = null;
        }
        $this->file = null;
    }
 
    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        $file = $this->getAbsolutePath();
        if ($file) {
            unlink($file);
        }
    }
}

 如果更改你的entity是由Doctrine event listener 或event subscriber处理,这个 preUpdate()回调函数必须通知Doctrine关于正在做的改变。有关preUpdate事件限制的完整参考请查看 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#preupdate

现在这个类做了你需要的一切:他会在entity持久化之前生成一个唯一的文件名,持久化之后,移动文件,删除文件。


现在移动文件是entity自动完成,这个$document->upload()就应该从controller中移除了:


if ($form->isValid()) {
    $em = $this->getDoctrine()->getManager();
 
    $em->persist($document);
    $em->flush();
 
    return $this->redirect(...);
}

 这个@ORM\PrePersist()和@ORM\PostPersist()事件回调:一个是在entity持久化到数据库之前触发,一个是在entity持久化到数据库之后触发。另一方面, @ORM\PreUpdate() 和 @ORM\PostUpdate()事件回调时当实体更新时触发。

当改变entity字段后进行持久化操作时,PreUpdate和PostUpdate回调才会被触发。这意味着,默认情况下,你只改变了$file属性,这些事件不会被触发,因为这个属性它自己不会持久化到Doctrine。有一个解决方法,就是创建一个updated字段把它持久化到Doctrine,并当文件改变时手动调整它。

使用ID作为文件名

如果要使用ID作为文件名,实现略有不同,您需要保存path属性为文件扩展名,而不是实际的文件名:


use Symfony\Component\HttpFoundation\File\UploadedFile;
 
/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Document
{
    private $temp;
 
    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
        // check if we have an old image path
        if (is_file($this->getAbsolutePath())) {
            // store the old name to delete after the update
            $this->temp = $this->getAbsolutePath();
        } else {
            $this->path = 'initial';
        }
    }
 
    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            $this->path = $this->getFile()->guessExtension();
        }
    }
 
    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->getFile()) {
            return;
        }
 
        // check if we have an old image
        if (isset($this->temp)) {
            // delete the old image
            unlink($this->temp);
            // clear the temp image path
            $this->temp = null;
        }
 
        // you must throw an exception here if the file cannot be moved
        // so that the entity is not persisted to the database
        // which the UploadedFile move() method does
        $this->getFile()->move(
            $this->getUploadRootDir(),
            $this->id.'.'.$this->getFile()->guessExtension()
        );
 
        $this->setFile(null);
    }
 
    /**
     * @ORM\PreRemove()
     */
    public function storeFilenameForRemove()
    {
        $this->temp = $this->getAbsolutePath();
    }
 
    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        if (isset($this->temp)) {
            unlink($this->temp);
        }
    }
 
    public function getAbsolutePath()
    {
        return null === $this->path
            ? null
            : $this->getUploadRootDir().'/'.$this->id.'.'.$this->path;
    }
}

你会注意到,在这种情况下,你需要做一点工作,以删除该文件。在数据删除之前,你必须保存文件路径(因为它依赖于ID)。然后,一旦对象已经完全从数据库中删除,你就可以安全的删除文件(在数据删除之后)。


下面我们来看一篇关于php性能分析之php-fpm的慢执行日志slow log文章,希望文章对各位有帮助


众所周知,mysql有slow query log,根据慢查询日志,我们可以知道那些sql语句有性能问题。作为mysql的好搭档,php也有这样的功能。如果你使用php-fpm来管理php的话,你可以通过如下选项开启。

PHP 5.3.3 之前设置如下:

<value name=”request_slowlog_timeout”>5s</value>
<value name=”slowlog”>logs/php-fpm-slowlog.log</value>
PHP 5.3.3 之后设置以下如下:
request_slowlog_timeout = 5s
slowlog = /usr/local/php/log/php-fpm-slowlog.log

说明:

request_slowlog_timeout是脚本超过多长时间 就可以记录到日志文件
slowlog 是日志文件的路径
开启后,如果有脚本执行超过指定的时间,就会在指定的日志文件中写入类似如下的信息:

[19-Dec-2013 16:54:49] [pool www] pid 18575
script_filename = /home/admin/web/htdocs/sandbox_canglong/test/tt.php
[0x0000000003a00dc8] curl_exec() /home/admin/web/htdocs/sandbox_canglong/test/tt.php:2
[0x0000000003a00cd0] exfilter_curl_get() /home/admin/web/htdocs/sandbox_canglong/test/tt.php:6

日志说明:

script_filename 是入口文件

curl_exec() : 说明是执行这个方法的时候超过执行时间的。
exfilter_curl_get() :说明调用curl_exec()的方法是exfilter_curl_get() 。
每行冒号后面的数字是行号。

开启后,在错误日志文件中也有相关记录。如下:

[19-Dec-2013 15:55:37] WARNING: [pool www] child 18575, script ‘/home/admin/web/htdocs/sandbox_canglong/test/tt.php’ (request: “GET /test/tt.php”) executing too slow (1.006222 sec), logging
[19-Dec-2013 15:55:37] NOTICE: child 18575 stopped for tracing
[19-Dec-2013 15:55:37] NOTICE: about to trace 18575
[19-Dec-2013 15:55:37] NOTICE: finished trace of 18575

标签:[!--infotagslink--]

您可能感兴趣的文章: