Wappalyzer API进行Web应用指纹识别实例

 更新时间:2016年11月25日 16:18  点击:1849
Wappalyzer是一个Firefox扩展,在网络上跟踪软件分发,并告诉您的维基,留言板等在页面上您目前使用。 本文我们来讨论如何使用Wappalyzer API进行Web应用指纹识别。

Web应用指纹识别,是web渗透信息收集最关键的一步,这方面开源的工具也非常多,像BlindElephant,whatweb 以及在非安全圈都很火的wappalyzer。本文主要描述如何使用wappalyzer的perl与php接口进行指纹识别。

Wappalyzer的功能是识别单个uri的指纹,其原理就是给指定URI发送HTTP请求,获取响应头与响应体并按指纹规则进行匹配。这也是 web应用指纹识别最基础的部分,除此之外,还有指纹置信度计算(如何去处伪造指纹,多种指纹特征如何综合判断,隐藏指纹信息如何提取),整个站点的指纹识别还涉及到有效爬虫抓取,分布式计算等问题,这些都不在本文内容中。

FreeBuf小科普:了解wappalyzer

Wappalyzer API进行Web应用指纹识别实例

Wappalyzer是一款浏览器插件,通过Wappalyzer可以识别出网站采用了那种web技术。它能够检测出CMS和电子商务系统、留言板、javascript框架,主机面板,分析统计工具和其它的一些web系统。The company behind Wappalyzer还能够收集web程序的一些信息用于统计分析,揭示出各种web系统的使用率即增长情况。实际Wappalyzer就是一个指纹识别工具。更多信息请点击:http://www.freebuf.com/tools/7391.html

官方地址:https://wappalyzer.com/

一、perl版本

原理:

给指定uri发送HTTP请求,通过分析HTTP相应的以下部分来判断指纹

(1) headers特征

响应头key:value对,多个key:value用逗号隔开,例如

"headers": { "X-AMP-Version": "([d.]+);version:", "Set-Cookie": "^AMP=" },

(2) html特征

响应体内容,多个规则用逗号隔开,例如

"html": [ "

特别注意:html全文匹配的规则一定要谨慎编写

(3) url特征

url内容,例如

"url": "/cgi-bin/UCEditor?(?:.*&)?merchantid=."

(4) meta特征

响应html 页面中诸如中的名字name:内容content对,多个规则用逗号隔开,例如

"meta": { "generator": "webEdition", "DC.title": "webEdition" }

(5) script特征

响应html页面中诸如 src中中url内容,多个规则用逗号隔开,例如

"script": [ "angular(?:-|.)([d.]*d)[^/]*.js;version:", "/([d.]+(-?rc[.d]*)*)/angular(.min)?.js;version:", "angular.*.js" ]

局限性:

不支持规则文件中APP版本号与置信度的获取

对非utf8的中文编码可能会存在问题

优点:

较之PHP版本,使用qr正则预编译处理,可以提前发现正则的问题,这是我选择该语言版本的主要原因。

(接下来的php版本就能让你知道正则不预编译处理有多坑了!)

脚本的功能:

(1)指纹识别结果按JSON格式返回,以便后续指纹信息入库等处理

(2)支持批量uri查询

(3)支持指定自定义JSON格式的指纹规则文件

(默认的指纹文件放置在/usr/lib/perl5/WWW/apps.json ,具体的路径会因cpan模块的安装路径有区别

可以使用perl -V 察看@INC变量来确定路径,或者更暴力的find吧)

安装:

cpan -i WWW::Wappalyzer
clone https://github.com/tanjiti/FingerPrint.git

运行:

(1)获取单个uri的指纹

perl FingerPrint.pl www.xxx.com tanjiti.json[指纹规则文件,可选]

返回结果

{
       "www.xxx.com": {
               "blogs": [
                       "WordPress"
               ],
               "web-servers": [
                       "Nginx"
               ],
               "cdn": [
                       "CloudFlare"
               ],
               "cms": [
                       "WordPress"
               ],
               "font-scripts": [
                       "Google Font API"
               ],
               "javascript-frameworks": [
                       "jQuery"
               ],
               "javascript-graphics": [
                       "Javascript Infovis Toolkit"
               ]
       }
}

(2) 从文件读取url列表进行批量指纹识别,并将结果输出到文件中

perl FingerPrint.pl url.txt tanjiti.json[指纹规则文件,可选]

结果输出到 url.txt__fingerprint 文件里

指纹规则文件编写示例:

more tanjiti.json


"apps": {

       "Discuz!":{
      "website": "www.discuz.net/forum.php",
      "cats": [ 1 ],
        "meta": { "generator": "Discuz"},
        "headers": {"Set-Cookie": "_lastact.*_sid|_sid.*_lastact|_sid.*smile|smile.*_sid"},
        "url": "/uc_server[/$]|uc_client[/$]",
        "html": "Powered by (?:Discuz!|<a href="http://www.discuz.net/"|UCenter)",
        "implies": "php"
       },
"PHP": {
       "website": "php.net",
       "cats": [ 27 ],
       "headers": { "Server": "php/?([d.]+)?;confidence:40;version:", "X-Powered-By": "php/?([d.]+)?;confidence:40;version:", "Set-Cookie": "PHPSESSID" },
       "url": ".php(?:$|?)"
       }

}

二、php版本

原理

同perl版本,区别有两点:

1. HTTP请求部分:较之perl使用LWP发送HTTP请求,php使用curl发送HTTP请求

2. 规则匹配部分:指纹规则的匹配部分使用javascript语法,然后通过php的v8js模块来解析。(为什么要这样做呢?当返回响应体内容很多,指纹正则写的很烂的时候,会卡死在规则匹配这一过程中,现在的规则下sina,163等大站基本卡死!)

运行

PHP版的接口已经能直接使用了,只是需要替换规则文件,??/p>

第一步:安装php、curl及v8js

apt-get install php5-dev php-pear build-essential libv8-dev php5-curl
pecl install channel://pecl.php.net/v8js-0.1.3
echo extension=v8js.so >> /etc/php5/cli/php.ini

验证是否安装成功

php -m | grep v8js
v8js -----------OK

第二步:下载Wappalyzer脚本

git clone https://github.com/ElbertF/Wappalyzer.git
cp -R Wappalyzer/drivers/php/* .
cp Wappalyzer/share/js/wappalyzer.js js/
cp Wappalyzer/share/apps.json . (指纹规则文件) 该规则文件的正则编写的有问题,所以我使用的以前版本的规则文件cp /usr/lib/perl5/WWW/apps.json .

php版指纹识别程序结构如下:

index.php 主程序
Wappalyzer.php
WappalyzerException.php
js/driver.js js/wappalyzer.js

apps.json 指纹规则文件

第三步:验证是否成功

php index.php www.tanjiti.com

输出格式为

应用名,版本号,置信度,app类型 (比perl版本多了对版本号,置信度的获取)

CloudFlare, , 100%, cdn
Javascript Infovis Toolkit, , 100%, javascript-graphics
jQuery, , 100%, javascript-frameworks
Nginx, , 100%, web-servers
PHP, 5.5.9, 100%, programming-languages
Ubuntu, , 100%, operating-systems
WordPress, 4.0, 100%, cms, blogs

接下来的优化输出为json格式与批量处理uri同perl版本。

最后寄言,一定一定要写好正则,NFA引擎正则优化TIPS、Perl正则技巧及正则性能评测方法

有时为了协同工作,或者因为项目需求,要用到php调用 C# 的 dll 类库,那么问题来了,要如何调用呢?以下是本人整理的方法。

有的时候,我们需要在php中利用到其他语言编写的dll类库,如C#编写的dll,方法就是利用PHP new COM方法来调用,在调用之前先要把dll库注册并把程序集放入到全局缓存中。

1. 创建一个 C# Class Library ,命名为:HelloWorld

2.  打开项目的属性,在点选左边的 "Application"(就是第一个tab) , 然后点击 Assembly Information 按钮 , 在弹出的Dialog中, 必须在底部勾上: Make assembly COM-visible !否则 , 这个dll将不能以COM方式访问 . ( 也可以在代码中的类声明中写上[ComVisible(true)] , 效果一样,需要增加using System.Runtime.InteropServices;引用)

PHP调用C#dll类库方法

3. 创建强命名签名文件并使用

使用vs.net的“Vsitual Studio .Net工具”-->Vistual Studio .Net命令提示符,输入 sn -k d:HelloWorld.snk 回车即创建了强命名签名文件

打开项目的属性,点选左边Signing 勾上Sign the assembly 在 Choose a strong name key file:处选择 选择刚才创建的HelloWorld.snk文件

PHP调用C#dll类库方法

4. 创建类库并编译成dll

namespace HelloWorld 

    //[ComVisible(true)] //or check "Assembly COM-Visible" at Application-Assembly_Information dialog ; 
    public class Hello 
    { 
        public string Write() 
        { 
            return "Hello World"; 
        } 
    } 
}

5. 找到dll文件夹路径 ,然后使用vs.net的“Vsitual Studio .Net工具”-->Vistual Studio .Net命令提示符

进入该dll文件夹下输入:

regasm HelloWorld.dll<回车>

这时候,这个.dll的.net程序集就变成一个标准的Com组件了,但是还不能用,必须让它变成全局Com组件.

将程序集添加到全局程序集缓存中

进入提示符窗口,输入:

gacutil /I HelloWorld.dll<回车>

这时,你的这个dll就被复制到全局程序集缓存中了.也就是说无论在这个电脑的哪个硬盘上都可以使用此dll组件了.

如果不进行强命名签名,这一步会提示加载失败

PHP调用C#dll类库方法

PHP测试

<?php 
$r=new Com("HelloWorld.Hello"); 
$s=$r->Write(); 
echo $s; 
?> 

命令符下:

CD [/D] [drive:][path] #进入指定路径
CD [..] #返回父目录

网站采集功能现在多半会使用火车头这些软件来实现了,但是对于一些定时或小的采集我们可以使用程序来实现,在php中curl是当选的一个函数了,下面一起来看看curl网站采集的实现程序吧.

选择curl的理由


关于curl与file_get_contents,摘抄一段通俗易懂的对比:
file_get_contents其实是一堆内置的文件操作函数的合并版本,比如file_exists,fopen,fread,fclose,专门提供给懒人用的,而且它主要是用来对付本地文件的,但又是因为懒人的原因,同时加入了对网络文件的支持;
curl是专门用来进行网络交互的库,提供了一堆自定义选项,用来应对不同的环境,稳定性自然要大于file_get_contents。

使用方法

1、开启curl支持

由于php环境安装后默认是没有打开curl支持的,需修改php.ini文件,找到;extension=php_curl.dll,把前面的冒号去掉,重启服务即可;

2、使用curl进行数据抓取

// 初始化一个 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);

3、通过正则匹配找到关键数据

//$data是curl_exec返回的的值,即采集的目标内容
preg_match_all("/<li class=\"item\">(.*?)<\/li>/",$data, $out, PREG_SET_ORDER);
foreach($out as $key => $value){
    //此处$value是数组,同时记录找到带匹配字符的整句和单独匹配的字符
    echo '匹配到的整句:'.$value[0].'
';
    echo '单独匹配到的:'.$value[1].'
';
}

技巧

1、超时的相关设置

通过curl_setopt($ch, opt) 可以设置一些超时的设置,主要包括:
CURLOPT_TIMEOUT 设置cURL允许执行的最长秒数。
CURLOPT_TIMEOUT_MS 设置cURL允许执行的最长毫秒数。 (在cURL 7.16.2中被加入。从PHP 5.2.3起可使用。 )
CURLOPT_CONNECTTIMEOUT 在发起连接前等待的时间,如果设置为0,则无限等待。
CURLOPT_CONNECTTIMEOUT_MS 尝试连接等待的时间,以毫秒为单位。如果设置为0,则无限等待。 在cURL 7.16.2中被加入。从PHP 5.2.3开始可用。

CURLOPT_DNS_CACHE_TIMEOUT 设置在内存中保存DNS信息的时间,默认为120秒。

curl_setopt($ch, CURLOPT_TIMEOUT, 60);   //只需要设置一个秒的数量就可以

curl_setopt($ch, CURLOPT_NOSIGNAL, 1);    //注意,毫秒超时一定要设置这个
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);  //超时毫秒,cURL 7.16.2中被加入。从PHP 5.2.3起可使用

2、通过post提交数据,保留cookie

//以下摘抄一个例子过来,用于学习借鉴:
//Curl 模拟登录 discuz 程序,适合DZ7.0
 
!extension_loaded('curl') && die('The curl extension is not loaded.');  
  
$discuz_url = 'http://www.111cn.net';//论坛地址  
$login_url = $discuz_url .'/logging.php?action=login';//登录页地址  
$get_url = $discuz_url .'/my.php?item=threads'; //我的帖子  
  
$post_fields = array();  
//以下两项不需要修改  
$post_fields['loginfield'] = 'username';  
$post_fields['loginsubmit'] = 'true';  
//用户名和密码,必须填写  
$post_fields['username'] = 'lxvoip';  
$post_fields['password'] = '88888888';  
//安全提问  
$post_fields['questionid'] = 0;  
$post_fields['answer'] = '';  
//@todo验证码  
$post_fields['seccodeverify'] = '';  
  
//获取表单FORMHASH  
$ch = curl_init($login_url);  
curl_setopt($ch, CURLOPT_HEADER, 0);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  
$contents = curl_exec($ch);  
curl_close($ch);  
preg_match('/<input\s*type="hidden"\s*name="formhash"\s*value="(.*?)"\s*\/>/i', $contents, $matches);  
if(!empty($matches)) {  
    $formhash = $matches[1];  
} else {  
    die('Not found the forumhash.');  
}  
  
//POST数据,获取COOKIE  
$cookie_file = dirname(__FILE__) . '/cookie.txt';  
//$cookie_file = tempnam('/tmp');  
$ch = curl_init($login_url);  
curl_setopt($ch, CURLOPT_HEADER, 0);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  
curl_setopt($ch, CURLOPT_POST, 1);  
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);  
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);  
curl_exec($ch);  
curl_close($ch);  
  
//带着上面得到的COOKIE获取需要登录后才能查看的页面内容  
$ch = curl_init($get_url);  
curl_setopt($ch, CURLOPT_HEADER, 0);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);  
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);  
$contents = curl_exec($ch);  
curl_close($ch);  
  
var_dump($contents);

在使用curl 中有一些心得给各位分享一下


编码转换

首先通过查看源代码找到采集的网站使用的编码,通过mb_convert_encoding函数进行转码;
具体使用方法:

//源字符是$str
 
//以下已知原编码为GBK,转换为utf-8
mb_convert_encoding($str, "UTF-8", "GBK");
 
//以下未知原编码,通过auto自动检测后,转换编码为utf-8
mb_convert_encoding($str, "UTF-8", "auto");

3、为更好地避开换行符和空格等不定因素的阻碍,有必要先清除采集到的源码中的换行符、空格符和制表符

//方法一,使用str_replace进行替换
$contents = str_replace("\r\n", '', $contents); //清除换行符
$contents = str_replace("\n", '', $contents); //清除换行符
$contents = str_replace("\t", '', $contents); //清除制表符
$contents = str_replace(" ", '', $contents); //清除空格符
 
//方法二,使用正则表达式进行替换

$contents = preg_replace("/([\r\n|\n|\t| ]+)/",'',$contents);

4、通过正则表达式匹配找出需要获得的代码段,使用preg_match_all实现该匹配

函数解释:

int preg_match_all ( string pattern, string subject, array matches [, int flags] )
pattern即正规表达式
subject即要进行查找的原文
matches是用于储存输出结果的数组
flags是储存的模式,包括:
    PREG_PATTERN_ORDER;  //整个数组是二维数组,$arr1[0]是包括边界所构成匹配字符串的数组,$arr1[1]除去边界所构成的匹配字符串的数组
    PREG_SET_ORDER;  //整个数组是二维数组,$arr2[0][0]是第一个包括边界所构成的匹配的字符串,$arr2[0][1]是第一个除去边界所构成的匹配的字符串,之后的数组以此类推
    PREG_OFFSET_CAPTURE;  //整个数组是三维数组,$arr3[0][0][0]是第一个包括边界所构成的匹配的字符串,$arr3[0][0][1]是到达第一个匹配字符串的边界的偏移量(边界不算在内),之后以此类推,$arr2[1][0][0]是第一个包括边界所构成的匹配的字符串,$arr3[1][0][1]是到达第一个匹配字符串的边界的偏移量(边界算在内);
 
//实际应用
preg_match_all('/<pclass=\"content\">(.*?)<\/p>/',$contents, $out, PREG_SET_ORDER);
$out将获取到所有匹配的元素
$out[0][0]将是包括<pclass=\"content\"></p>在内的全段字符
$out[0][1]将是仅包括(.*?)括号内所匹配到的字符段
 
//如此类推,第n个匹配到的字段可以用以下方法取得
$out[n-1][1]
 
//若正则表达式中存大多个括号,则取得句中第m个匹配点的方法是
$out[n-1][m]

5、取得要找到字符后,若要去掉html标签,使用PHP自带的函数strip_tags即可方便地实现
 
//例
$result=strip_tags($out[0][1]);

上面只是把数据采集下载了,当然最好我们需要把$contents内容进入库处理了,这里就是简单的php数据查询保存的功能了,非常简单.

下面来看一个我们在生活中常碰到的一个php实现微信红包的程序算法,希望这个程序对各位做微信开的朋友会带来帮助.

最近一直在微信群里体验红包功能,红包类型有


•普通红包

•拼手气红包两种


普通红包就不用多解析了,大锅饭原理,平分。
拼手气红包讲的是手气(运气),有人可以抢到很多,有人抢的少得可怜,当然也不是先抢就一定多,说到底了就是随机。

 

2848416585426645400

 

想了想,自己写写看,能不能实现类似的功能(不敢说是算法)。
// $bonus_total 红包总金额
// $bonus_count 红包个数
// $bonus_type 红包类型 1=拼手气红包 0=普通红包
function randBonus($bonus_total=0, $bonus_count=3, $bonus_type=1){
    $bonus_items    = array(); // 将要瓜分的结果
    $bonus_balance  = $bonus_total; // 每次分完之后的余额
    $bonus_avg      = number_format($bonus_total/$bonus_count, 2); // 平均每个红包多少钱

    $i              = 0;
    while($i<$bonus_count){
        if($i<$bonus_count-1){
            $rand           = $bonus_type?(rand(1, $bonus_balance*100-1)/100):$bonus_avg; // 根据红包类型计算当前红包的金额
            $bonus_items[]  = $rand;
            $bonus_balance  -= $rand;
        }else{
            $bonus_items[]  = $bonus_balance; // 最后一个红包直接承包最后所有的金额,保证发出的总金额正确
        }
        $i++;
    }
    return $bonus_items;
}


 好吧,我们现在来体验一下
// 发3个拼手气红包,总金额是100元
$bonus_items    = randBonus(100, 3, 1);
// 查看生成的红包
var_dump($bonus_items);
// 校验总金额是不是正确,看看微信有没有坑我们的钱
var_dump(array_sum($bonus_items));


 另一个使用数组实现的版本,原理差不多:
function sendRandBonus($total=0, $count=3, $type=1){
    if($type==1){
        $input          = range(0.01, $total, 0.01);
        if($count>1){
            $rand_keys  = (array) array_rand($input,  $count-1);
            $last       = 0;
            foreach($rand_keys as $i=>$key){
                $current    = $input[$key]-$last;
                $items[]    = $current;
                $last       = $input[$key];
            }
        }
        $items[]        = $total-array_sum($items);
    }else{
        $avg            = number_format($total/$count, 2);
        $i              = 0;
        while($i<$count){
            $items[]    = $i<$count-1?$avg:($total-array_sum($items));
            $i++;
        }
    }
    return $items;
}

下面的代码是用此前一个名为MyPDO的类改写的,引入了单例模式来保证在全局调用中不会重复实例化这个类,降低系统资源的浪费。

用php大部分操作都是和各种数据库打交道,包括mysql,redis,memcache等各种关系型和非关系型数据库,所以一个应用中会 存在大量连接数据库的操作,如果不用单例模式,那每次都要new操作,但是每次new都会消耗大量的内存资源和系统资源,而且每次打开和关闭数据库连接都 是对数据库的一种极大考验和浪费


代码如下:


<?php

class MyPDO
{
    protected static $_instance = null;
    protected $dbName = '';
    protected $dsn;
    protected $dbh;
   
    /**
     * 构造
     *
     * @return MyPDO
     */
    private function __construct($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset)
    {
        try {
            $this->dsn = 'mysql:host='.$dbHost.';dbname='.$dbName;
            $this->dbh = new PDO($this->dsn, $dbUser, $dbPasswd);
            $this->dbh->exec('SET character_set_connection='.$dbCharset.', character_set_results='.$dbCharset.', character_set_client=binary');
        } catch (PDOException $e) {
            $this->outputError($e->getMessage());
        }
    }
   
    /**
     * 防止克隆
     *
     */
    private function __clone() {}
   
    /**
     * Singleton instance
     *
     * @return Object
     */
    public static function getInstance($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset)
    {
        if (self::$_instance === null) {
            self::$_instance = new self($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset);
        }
        return self::$_instance;
    }
   
    /**
     * Query 查询
     *
     * @param String $strSql SQL语句
     * @param String $queryMode 查询方式(All or Row)
     * @param Boolean $debug
     * @return Array
     */
    public function query($strSql, $queryMode = 'All', $debug = false)
    {
        if ($debug === true) $this->debug($strSql);
        $recordset = $this->dbh->query($strSql);
        $this->getPDOError();
        if ($recordset) {
            $recordset->setFetchMode(PDO::FETCH_ASSOC);
            if ($queryMode == 'All') {
                $result = $recordset->fetchAll();
            } elseif ($queryMode == 'Row') {
                $result = $recordset->fetch();
            }
        } else {
            $result = null;
        }
        return $result;
    }
   
    /**
     * Update 更新
     *
     * @param String $table 表名
     * @param Array $arrayDataValue 字段与值
     * @param String $where 条件
     * @param Boolean $debug
     * @return Int
     */
    public function update($table, $arrayDataValue, $where = '', $debug = false)
    {
        $this->checkFields($table, $arrayDataValue);
        if ($where) {
            $strSql = '';
            foreach ($arrayDataValue as $key => $value) {
                $strSql .= ", `$key`='$value'";
            }
            $strSql = substr($strSql, 1);
            $strSql = "UPDATE `$table` SET $strSql WHERE $where";
        } else {
            $strSql = "REPLACE INTO `$table` (`".implode('`,`', array_keys($arrayDataValue))."`) VALUES ('".implode("','", $arrayDataValue)."')";
        }
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }
   
    /**
     * Insert 插入
     *
     * @param String $table 表名
     * @param Array $arrayDataValue 字段与值
     * @param Boolean $debug
     * @return Int
     */
    public function insert($table, $arrayDataValue, $debug = false)
    {
        $this->checkFields($table, $arrayDataValue);
        $strSql = "INSERT INTO `$table` (`".implode('`,`', array_keys($arrayDataValue))."`) VALUES ('".implode("','", $arrayDataValue)."')";
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }
   
    /**
     * Replace 覆盖方式插入
     *
     * @param String $table 表名
     * @param Array $arrayDataValue 字段与值
     * @param Boolean $debug
     * @return Int
     */
    public function replace($table, $arrayDataValue, $debug = false)
    {
        $this->checkFields($table, $arrayDataValue);
        $strSql = "REPLACE INTO `$table`(`".implode('`,`', array_keys($arrayDataValue))."`) VALUES ('".implode("','", $arrayDataValue)."')";
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }
   
    /**
     * Delete 删除
     *
     * @param String $table 表名
     * @param String $where 条件
     * @param Boolean $debug
     * @return Int
     */
    public function delete($table, $where = '', $debug = false)
    {
        if ($where == '') {
            $this->outputError("'WHERE' is Null");
        } else {
            $strSql = "DELETE FROM `$table` WHERE $where";
            if ($debug === true) $this->debug($strSql);
            $result = $this->dbh->exec($strSql);
            $this->getPDOError();
            return $result;
        }
    }
   
    /**
     * execSql 执行SQL语句
     *
     * @param String $strSql
     * @param Boolean $debug
     * @return Int
     */
    public function execSql($strSql, $debug = false)
    {
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }
   
    /**
     * 获取字段最大值
     *
     * @param string $table 表名
     * @param string $field_name 字段名
     * @param string $where 条件
     */
    public function getMaxValue($table, $field_name, $where = '', $debug = false)
    {
        $strSql = "SELECT MAX(".$field_name.") AS MAX_VALUE FROM $table";
        if ($where != '') $strSql .= " WHERE $where";
        if ($debug === true) $this->debug($strSql);
        $arrTemp = $this->query($strSql, 'Row');
        $maxValue = $arrTemp["MAX_VALUE"];
        if ($maxValue == "" || $maxValue == null) {
            $maxValue = 0;
        }
        return $maxValue;
    }
   
    /**
     * 获取指定列的数量
     *
     * @param string $table
     * @param string $field_name
     * @param string $where
     * @param bool $debug
     * @return int
     */
    public function getCount($table, $field_name, $where = '', $debug = false)
    {
        $strSql = "SELECT COUNT($field_name) AS NUM FROM $table";
        if ($where != '') $strSql .= " WHERE $where";
        if ($debug === true) $this->debug($strSql);
        $arrTemp = $this->query($strSql, 'Row');
        return $arrTemp['NUM'];
    }
   
    /**
     * 获取表引擎
     *
     * @param String $dbName 库名
     * @param String $tableName 表名
     * @param Boolean $debug
     * @return String
     */
    public function getTableEngine($dbName, $tableName)
    {
        $strSql = "SHOW TABLE STATUS FROM $dbName WHERE Name='".$tableName."'";
        $arrayTableInfo = $this->query($strSql);
        $this->getPDOError();
        return $arrayTableInfo[0]['Engine'];
    }
   
    /**
     * beginTransaction 事务开始
     */
    private function beginTransaction()
    {
        $this->dbh->beginTransaction();
    }
   
    /**
     * commit 事务提交
     */
    private function commit()
    {
        $this->dbh->commit();
    }
   
    /**
     * rollback 事务回滚
     */
    private function rollback()
    {
        $this->dbh->rollback();
    }
   
    /**
     * transaction 通过事务处理多条SQL语句
     * 调用前需通过getTableEngine判断表引擎是否支持事务
     *
     * @param array $arraySql
     * @return Boolean
     */
    public function execTransaction($arraySql)
    {
        $retval = 1;
        $this->beginTransaction();
        foreach ($arraySql as $strSql) {
            if ($this->execSql($strSql) == 0) $retval = 0;
        }
        if ($retval == 0) {
            $this->rollback();
            return false;
        } else {
            $this->commit();
            return true;
        }
    }
 
    /**
     * checkFields 检查指定字段是否在指定数据表中存在
     *
     * @param String $table
     * @param array $arrayField
     */
    private function checkFields($table, $arrayFields)
    {
        $fields = $this->getFields($table);
        foreach ($arrayFields as $key => $value) {
            if (!in_array($key, $fields)) {
                $this->outputError("Unknown column `$key` in field list.");
            }
        }
    }
   
    /**
     * getFields 获取指定数据表中的全部字段名
     *
     * @param String $table 表名
     * @return array
     */
    private function getFields($table)
    {
        $fields = array();
        $recordset = $this->dbh->query("SHOW COLUMNS FROM $table");
        $this->getPDOError();
        $recordset->setFetchMode(PDO::FETCH_ASSOC);
        $result = $recordset->fetchAll();
        foreach ($result as $rows) {
            $fields[] = $rows['Field'];
        }
        return $fields;
    }
   
    /**
     * getPDOError 捕获PDO错误信息
     */
    private function getPDOError()
    {
        if ($this->dbh->errorCode() != '00000') {
            $arrayError = $this->dbh->errorInfo();
            $this->outputError($arrayError[2]);
        }
    }
   
    /**
     * debug
     *
     * @param mixed $debuginfo
     */
    private function debug($debuginfo)
    {
        var_dump($debuginfo);
        exit();
    }
   
    /**
     * 输出错误信息
     *
     * @param String $strErrMsg
     */
    private function outputError($strErrMsg)
    {
        throw new Exception('MySQL Error: '.$strErrMsg);
    }
   
    /**
     * destruct 关闭数据库连接
     */
    public function destruct()
    {
        $this->dbh = null;
    }
}
?>
 

调用方法:

PHP

<?php
require 'MyPDO.class.php';
$db = MyPDO::getInstance('localhost', 'root', '123456', 'test', 'utf8');
 
//do something...
 
$db->destruct();
?>

[!--infotagslink--]

相关文章

  • JS跨浏览器解析XML应用过程详解

    这篇文章主要介绍了JS跨浏览器解析XML应用过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-10-16
  • C++ opencv实现车道线识别

    这篇文章主要为大家详细介绍了C++ opencv实现车道线识别,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-20
  • Opencv LBPH人脸识别算法详解

    这篇文章主要为大家详细介绍了Opencv LBPH人脸识别算法的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • vivo X9如何查出后台偷跑流量应用?vivo X9查出后台偷跑流量应用的方法

    vivo X9如何查看后台流量偷跑的情况?小编教你轻松查到!还不了解的小伙伴快来看看吧! 1)打开手机自带的【i管家】应用,打开后点击【流量监控】选项。(如下图) 2)接着选...2016-12-31
  • PHP云存储Redis的应用场景与Redis实现排行榜功能

    Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。本文我们来讲解Redis的应用场景实例。 C...2016-11-25
  • python 实现性别识别

    这篇文章主要介绍了python 实现性别识别的示例,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下...2020-11-22
  • PHP explode()函数的几个应用和implode()函数有什么区别

    explode()函数介绍explode() 函数可以把字符串分割为数组。语法:explode(separator,string,limit)。 参数 描述 separator 必需。规定在哪里分割字符串。 string...2015-11-08
  • PHP Libevent扩展安装配置及简单应用

    Libevent 是一个用C语言编写的、轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,下文我们就一起来看PHP Libevent扩展安装...2016-11-25
  • php中echo <<< 的应用

    <? $a="变量的值将被带入"; echo <<< help <pre> php中echo <<< 的应用 虽然echo "...";可以断行,但若其中如出现",则仍需做转义 处理。需写做: echo " ...2016-11-25
  • PHP-GTK 介绍及其应用

    1. PHP-GTK介绍 1.1 PHP-GTK PHP-GTK是PHP的延伸模组,它可以让程式设计师写出在客户端执行的、且独立的GUI的程式。这个模组不允许在浏览器上显视GTK+的程式,它一开始就...2016-11-25
  • python 实现表情识别

    这篇文章主要介绍了python 实现表情识别的示例代码,帮助大家更好的理解和学习python,感兴趣的朋友可以了解下...2020-11-22
  • C#实现的简单验证码识别实例

    这篇文章主要介绍了C#实现的简单验证码识别实例,只适应一些简单的验证码,需要的朋友可以参考下...2020-06-25
  • C# windows语音识别与朗读实例

    这篇文章主要为大家详细介绍了C# windows语音识别与朗读实例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-07-26
  • 基于python3+OpenCV实现人脸和眼睛识别

    这篇文章主要为大家详细介绍了基于python3+OpenCV实现人脸和眼睛识别,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-06
  • C# 10分钟完成百度人脸识别(入门篇)

    这篇文章主要介绍了C# 10分钟完成百度人脸识别(入门篇),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • Vue中keep-alive的两种应用方式

    这篇文章主要介绍了Vue中keep-alive的两种应用方式,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-15
  • C# 使用AspriseOCR.dll实现验证码识别

    这篇文章主要介绍了C# 使用AspriseOCR.dll实现验证码识别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • css中Float属性深入剖析与应用

    这篇文章介绍了css中的float属性的应用和理解,让设置的标签产生浮动效果。希望能帮助到有需要的同学。 一、Float的特性 1. 应用于文字围绕图片 2. 创建一个块级框...2017-01-22
  • ajax简单与ajax+php实例应用(1/4)

    [ajax介绍]   ajax是使用客户端脚本与web服务器交换数据的web应用开发方法。web页面不用打断交互流程进行重新加裁,就可以动态地更新。使用ajax,用户可以创建接近本地...2016-11-25
  • php ob_start() ob_end_flush()缓存技术简单应用

    本文章介绍了一个简单的关于php入门篇-缓存技术简单应用,有需要的朋友可以看看哦,这里是利用了ob_start(); ob_end_flush(); 来实例的。 代码如下 复制...2016-11-25