首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
183 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
31
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
786
篇与
的结果
2023-08-04
基于PHP的cURL快速入门
基于PHP的cURL快速入门cURL 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP、FTP、TELNET等。最爽的是,PHP也支持 cURL 库。本文将介绍 cURL 的一些高级特性,以及在PHP中如何运用它。为什么要用 cURL?是的,我们可以通过其他办法获取网页内容。大多数时候,我因为想偷懒,都直接用简单的PHP函数:以下为引用的内容:$content = file_get_contents("http://www.nettuts.com");// or$lines = file("http://www.nettuts.com");// orreadfile(http://www.nettuts.com);不过,这种做法缺乏灵活性和有效的错误处理。而且,你也不能用它完成一些高难度任务——比如处理coockies、验证、表单提交、文件上传等等。引用:cURL 是一种功能强大的库,支持很多不同的协议、选项,能提供 URL 请求相关的各种细节信息。基本结构在学习更为复杂的功能之前,先来看一下在PHP中建立cURL请求的基本步骤:初始化设置变量执行并获取结果释放cURL句柄以下为引用的内容:// 1. 初始化$ch = curl_init();// 2. 设置选项,包括URLcurl_setopt($ch, CURLOPT_URL, "http://www.nettuts.com");curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_HEADER, 0);// 3. 执行并获取HTML文档内容$output = curl_exec($ch);// 4. 释放curl句柄curl_close($ch);第二步(也就是 curl_setopt() )最为重要,一切玄妙均在此。有一长串cURL参数可供设置,它们能指定URL请求的各个细节。要一次性全部看完并理解可能比较困难,所以今天我们只试一下那些更常用也更有用的选项。检查错误你可以加一段检查错误的语句(虽然这并不是必需的):以下为引用的内容:// ...$output = curl_exec($ch);if ($output === FALSE) {echo "cURL Error: " . curl_error($ch);}// ...请注意,比较的时候我们用的是“=== FALSE”,而非“== FALSE”。因为我们得区分 空输出 和 布尔值FALSE,后者才是真正的错误.获取信息这是另一个可选的设置项,能够在cURL执行后获取这一请求的有关信息:以下为引用的内容:// ...curl_exec($ch);$info = curl_getinfo($ch);echo '获取'. $info['url'] . '耗时'. $info['total_time'] . '秒';// ...返回的数组中包括了以下信息:“url” //资源网络地址“content_type” //内容编码“http_code” //HTTP状态码“header_size” //header的大小“request_size” //请求的大小“filetime” //文件创建时间“ssl_verify_result” //SSL验证结果“redirect_count” //跳转技术“total_time” //总耗时“namelookup_time” //DNS查询耗时“connect_time” //等待连接耗时“pretransfer_time” //传输前准备耗时“size_upload” //上传数据的大小“size_download” //下载数据的大小“speed_download” //下载速度“speed_upload” //上传速度“download_content_length”//下载内容的长度“upload_content_length” //上传内容的长度“starttransfer_time” //开始传输的时间“redirect_time”//重定向耗时基于浏览器的重定向在第一个例子中,我们将提供一段用于侦测服务器是否有基于浏览器的重定向的代码。例如,有些网站会根据是否是手机浏览器甚至用户来自哪个国家来重定向网页。我们利用 CURLOPT_HTTPHEADER 选项来设定我们发送出的HTTP请求头信息(http headers),包括user agent信息和默认语言。然后我们来看看这些特定网站是否会把我们重定向到不同的URL。以下为引用的内容:// 测试用的URL$urls = array("http://www.cnn.com","http://www.mozilla.com","http://www.facebook.com");// 测试用的浏览器信息$browsers = array("standard" => array ("user_agent" => "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)","language" => "en-us,en;q=0.5"),"iphone" => array ("user_agent" => "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3","language" => "en"),"french" => array ("user_agent" => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)","language" => "fr,fr-FR;q=0.5"));foreach ($urls as $url) {echo "URL: $url\n";foreach ($browsers as $test_name => $browser) {$ch = curl_init();// 设置 urlcurl_setopt($ch, CURLOPT_URL, $url);// 设置浏览器的特定headercurl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: {$browser['user_agent']}","Accept-Language: {$browser['language']}"));// 页面内容我们并不需要curl_setopt($ch, CURLOPT_NOBODY, 1);// 只需返回HTTP headercurl_setopt($ch, CURLOPT_HEADER, 1);// 返回结果,而不是输出它curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$output = curl_exec($ch);curl_close($ch);// 有重定向的HTTP头信息吗?if (preg_match("!Location: (.*)!", $output, $matches)) {echo "$test_name: redirects to $matches[1]\n";} else {echo "$test_name: no redirection\n";}}echo "\n\n";}首先,我们建立一组需要测试的URL,接着指定一组需要测试的浏览器信息。最后通过循环测试各种URL和浏览器匹配可能产生的情况。因为我们指定了cURL选项,所以返回的输出内容则只包括HTTP头信息(被存放于 $output 中)。利用一个简单的正则,我们检查这个头信息中是否包含了“Location:”字样。运行这段代码应该会返回如下结果:URL: http://www.cnn.comstandard: no redirectioniphone: redirects to http://m.cnn.comfrench: no redirectionURL: http://www.mozilla.comstandard: redirects to http://www.mozilla.com/en-US/iphone: redirects to http://www.mozilla.com/en-US/french: redirects to http://www.mozilla.com/fr/URL: http://www.facebook.comstandard: no redirectioniphone: redirects to http://touch.facebook.com/?w2mfrench: no redirection用POST方法发送数据当发起GET请求时,数据可以通过“查询字串”(query string)传递给一个URL。例如,在google中搜索时,搜索关键即为URL的查询字串的一部分:http://www.google.com/search?q=nettuts这种情况下你可能并不需要cURL来模拟。把这个URL丢给“file_get_contents()”就能得到相同结果。不过有一些HTML表单是用POST方法提交的。这种表单提交时,数据是通过 HTTP请求体(request body) 发送,而不是查询字串。例如,当使用CodeIgniter论坛的表单,无论你输入什么关键字,总是被POST到如下页面:http://codeigniter.com/forums/do_search/你可以用PHP脚本来模拟这种URL请求。首先,新建一个可以接受并显示POST数据的文件,我们给它命名为post_output.php:print_r($_POST);接下来,写一段PHP脚本来执行cURL请求:以下为引用的内容:$url = "http://localhost/post_output.php";$post_data = array ("foo" => "bar","query" => "Nettuts","action" => "Submit");$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 我们在POST数据哦!curl_setopt($ch, CURLOPT_POST, 1);// 把post的变量加上curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo $output;执行代码后应该会得到以下结果:Array([foo]=>bar[query]=>Nettuts[action]=>Submit)这段脚本发送一个POST请求给 post_output.php ,这个页面 $_POST 变量并返回,我们利用cURL捕捉了这个输出。文件上传上传文件和前面的POST十分相似。因为所有的文件上传表单都是通过POST方法提交的。首先新建一个接收文件的页面,命名为 upload_output.php:print_r($_FILES);以下是真正执行文件上传任务的脚本:以下为引用的内容:$url = "http://localhost/upload_output.php";$post_data = array ("foo" => "bar",// 要上传的本地文件地址"upload" => "@C:/wamp/www/test.zip");$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo $output;如果你需要上传一个文件,只需要把文件路径像一个post变量一样传过去,不过记得在前面加上@符号。执行这段脚本应该会得到如下输出:Array([upload]=>Array([name]=>test.zip[type]=>application/octet-stream[tmp_name]=>C:\wamp\tmp\php4CCB.tmp[error]=>0[size]=>1183642))cURL批处理(multi cURL)cURL还有一个高级特性——批处理句柄(handle)。这一特性允许你同时或异步地打开多个URL连接。下面是来自来自php.net的示例代码:以下为引用的内容:// 创建两个cURL资源$ch1 = curl_init();$ch2 = curl_init();// 指定URL和适当的参数curl_setopt($ch1, CURLOPT_URL, "http://lxr.php.net/");curl_setopt($ch1, CURLOPT_HEADER, 0);curl_setopt($ch2, CURLOPT_URL, "http://www.php.net/");curl_setopt($ch2, CURLOPT_HEADER, 0);// 创建cURL批处理句柄$mh = curl_multi_init();// 加上前面两个资源句柄curl_multi_add_handle($mh,$ch1);curl_multi_add_handle($mh,$ch2);// 预定义一个状态变量$active = null;// 执行批处理do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);while ($active && $mrc == CURLM_OK) {if (curl_multi_select($mh) != -1) {do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);}}// 关闭各个句柄curl_multi_remove_handle($mh, $ch1);curl_multi_remove_handle($mh, $ch2);curl_multi_close($mh);这里要做的就是打开多个cURL句柄并指派给一个批处理句柄。然后你就只需在一个while循环里等它执行完毕。这个示例中有两个主要循环。第一个 do-while 循环重复调用 curl_multi_exec() 。这个函数是无隔断(non-blocking)的,但会尽可能少地执行。它返回一个状态值,只要这个值等于常量 CURLM_CALL_MULTI_PERFORM ,就代表还有一些刻不容缓的工作要做(例如,把对应URL的http头信息发送出去)。也就是说,我们需要不断调用该函数,直到返回值发生改变。而接下来的 while 循环,只在 $active 变量为 true 时继续。这一变量之前作为第二个参数传给了 curl_multi_exec() ,代表只要批处理句柄中是否还有活动连接。接着,我们调用 curl_multi_select() ,在活动连接(例如接受服务器响应)出现之前,它都是被“屏蔽”的。这个函数成功执行后,我们又会进入另一个 do-while 循环,继续下一条URL。还是来看一看怎么把这一功能用到实处吧:WordPress 连接检查器想象一下你有一个文章数目庞大的博客,这些文章中包含了大量外部网站链接。一段时间之后,因为这样那样的原因,这些链接中相当数量都失效了。要么是被和谐了,要么是整个站点都被功夫网了...我们下面建立一个脚本,分析所有这些链接,找出打不开或者404的网站/网页,并生成一个报告。请注意,以下并不是一个真正可用的WordPress插件,仅仅是一段独立功能的脚本而已,仅供演示,谢谢。好,开始吧。首先,从数据库中读取所有这些链接:以下为引用的内容:// CONFIG$db_host = 'localhost';$db_user = 'root';$db_pass = '';$db_name = 'wordpress';$excluded_domains = array('localhost', 'www.mydomain.com');$max_connections = 10;// 初始化一些变量$url_list = array();$working_urls = array();$dead_urls = array();$not_found_urls = array();$active = null;// 连到 MySQLif (!mysql_connect($db_host, $db_user, $db_pass)) {die('Could not connect: ' . mysql_error());}if (!mysql_select_db($db_name)) {die('Could not select db: ' . mysql_error());}// 找出所有含有链接的文章$q = "SELECT post_content FROM wp_postsWHERE post_content LIKE '%href=%'AND post_status = 'publish'AND post_type = 'post'";$r = mysql_query($q) or die(mysql_error());while ($d = mysql_fetch_assoc($r)) {// 用正则匹配链接if (preg_match_all("!href=\"(.*?)\"!", $d['post_content'], $matches)) {foreach ($matches[1] as $url) {// exclude some domains$tmp = parse_url($url);if (in_array($tmp['host'], $excluded_domains)) {continue;}// store the url$url_list []= $url;}}}// 移除重复链接$url_list = array_values(array_unique($url_list));if (!$url_list) {die('No URL to check');}我们首先配置好数据库,一系列要排除的域名($excluded_domains),以及最大并发连接数($max_connections)。然后,连接数据库,获取文章和包含的链接,把它们收集到一个数组中($url_list)。下面的代码有点复杂了,因此我将一小步一小步地详细解释:以下为引用的内容:// 1. 批处理器$mh = curl_multi_init();// 2. 加入需批量处理的URLfor ($i = 0; $i < $max_connections; $i++) {add_url_to_multi_handle($mh, $url_list);}// 3. 初始处理do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);// 4. 主循环while ($active && $mrc == CURLM_OK) {// 5. 有活动连接if (curl_multi_select($mh) != -1) {// 6. 干活do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);// 7. 有信息否?if ($mhinfo = curl_multi_info_read($mh)) {// 意味着该连接正常结束// 8. 从curl句柄获取信息$chinfo = curl_getinfo($mhinfo['handle']);// 9. 死链么?if (!$chinfo['http_code']) {$dead_urls []= $chinfo['url'];// 10. 404了?} else if ($chinfo['http_code'] == 404) {$not_found_urls []= $chinfo['url'];// 11. 还能用} else {$working_urls []= $chinfo['url'];}// 12. 移除句柄curl_multi_remove_handle($mh, $mhinfo['handle']);curl_close($mhinfo['handle']);// 13. 加入新URL,干活if (add_url_to_multi_handle($mh, $url_list)) {do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);}}}}// 14. 完了curl_multi_close($mh);echo "==Dead URLs==\n";echo implode("\n",$dead_urls) . "\n\n";echo "==404 URLs==\n";echo implode("\n",$not_found_urls) . "\n\n";echo "==Working URLs==\n";echo implode("\n",$working_urls);// 15. 向批处理器添加urlfunction add_url_to_multi_handle($mh, $url_list) {static $index = 0;// 如果还剩url没用if ($url_list[$index]) {// 新建curl句柄$ch = curl_init();// 配置urlcurl_setopt($ch, CURLOPT_URL, $url_list[$index]);// 不想输出返回的内容curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 重定向到哪儿我们就去哪儿curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);// 不需要内容体,能够节约带宽和时间curl_setopt($ch, CURLOPT_NOBODY, 1);// 加入到批处理器中curl_multi_add_handle($mh, $ch);// 拨一下计数器,下次调用该函数就能添加下一个url了$index++;return true;} else {// 没有新的URL需要处理了return false;}}下面解释一下以上代码。列表的序号对应着代码注释中的顺序数字。新建一个批处理器。Created a multi handle.稍后我们将创建一个把URL加入批处理器的函数 add_url_to_multi_handle() 。每当这个函数被调用,就有一个新url被加入批处理器。一开始,我们给批处理器添加了10个URL(这一数字由 $max_connections 所决定)。 运行 curl_multi_exec() 进行初始化工作是必须的,只要它返回 CURLM_CALL_MULTI_PERFORM 就还有事情要做。这么做主要是为了创建连接,它不会等待完整的URL响应。只要批处理中还有活动连接主循环就会一直持续。curl_multi_select() 会一直等待,直到某个URL查询产生活动连接。cURL的活儿又来了,主要是获取响应数据。检查各种信息。当一个URL请求完成时,会返回一个数组。在返回的数组中有一个 cURL 句柄。我们利用其获取单个cURL请求的相应信息。如果这是一个死链或者请求超时,不会返回http状态码。如果这个页面找不到了,会返回404状态码。其他情况我们都认为这个链接是可用的(当然,你也可以再检查一下500错误之类...)。从该批次移除这个cURL句柄,因为它已经没有利用价值了,关了它!很好,现在可以另外加一个URL进来了。再一次地,初始化工作又开始进行...嗯,该干的都干了。关闭批处理器,生成报告。回过头来看给批处理器添加新URL的函数。这个函数每调用一次,静态变量 $index 就递增一次,这样我们才能知道还剩多少URL没处理。我把这个脚本在我的博客上跑了一遍(测试需要,有一些错误链接是故意加上的),结果如下:以下为引用的内容:共检查约40个URL,只耗费两秒不到。当需要检查更加大量的URL时,其省心省力的效果可想而知!如果你同时打开10个连接,还能再快上10倍!另外,你还可以利用cURL批处理的无隔断特性来处理大量URL请求,而不会阻塞你的Web脚本。另一些有用的cURL 选项HTTP 认证如果某个URL请求需要基于 HTTP 的身份验证,你可以使用下面的代码:复制内容到剪贴板代码:以下为引用的内容:$url = "http://www.somesite.com/members/";$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 发送用户名和密码curl_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword");// 你可以允许其重定向curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);// 下面的选项让 cURL 在重定向后// 也能发送用户名和密码curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);$output = curl_exec($ch);curl_close($ch);FTP 上传PHP 自带有 FTP 类库, 但你也能用 cURL:以下为引用的内容:// 开一个文件指针$file = fopen("/path/to/file", "r");// url里包含了大部分所需信息$url = "ftp://username:password@mydomain.com:21/path/to/new/file";$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 上传相关的选项curl_setopt($ch, CURLOPT_UPLOAD, 1);curl_setopt($ch, CURLOPT_INFILE, $fp);curl_setopt($ch, CURLOPT_INFILESIZE, filesize("/path/to/file"));// 是否开启ASCII模式 (上传文本文件时有用)curl_setopt($ch, CURLOPT_FTPASCII, 1);$output = curl_exec($ch);curl_close($ch);翻墙术你可以用代理发起cURL请求:以下为引用的内容:$ch = curl_init();curl_setopt($ch, CURLOPT_URL,'http://www.example.com');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 指定代理地址curl_setopt($ch, CURLOPT_PROXY, '11.11.11.11:8080');// 如果需要的话,提供用户名和密码curl_setopt($ch, CURLOPT_PROXYUSERPWD,'user:pass');$output = curl_exec($ch);curl_close ($ch);回调函数可以在一个URL请求过程中,让cURL调用某指定的回调函数。例如,在内容或者响应下载的过程中立刻开始利用数据,而不用等到完全下载完。以下为引用的内容:$ch = curl_init();curl_setopt($ch, CURLOPT_URL,'http://net.tutsplus.com');curl_setopt($ch, CURLOPT_WRITEFUNCTION,"progress_function");curl_exec($ch);curl_close ($ch);function progress_function($ch,$str) {echo $str;return strlen($str);}这个回调函数必须返回字串的长度,不然此功能将无法正常使用。在URL响应接收的过程中,只要收到一个数据包,这个函数就会被调用。小结今天我们一起学习了cURL库的强大功能和灵活的扩展性。希望你喜欢。下一次要发起URL请求时,考虑下cURL吧!英文原文:http://net.tutsplus.com/tutorial%20...%20for-mastering-curl/原文作者:Burak Guzel本文链接:http://www.blueidea.com/tech/program/2010/7348.aspHow to Use cURL in PHP
2023年08月04日
11 阅读
0 评论
0 点赞
2023-08-04
PHP取二进制文件头快速判断文件类型
PHP取二进制文件头快速判断文件类型介绍:一般我们都是按照文件扩展名来判断文件类型,但是这个很不靠谱,轻易就通过修改扩展名来躲避了,一般必须要读取文件信息来识别,PHP扩展中提供了类似 exif_imagetype 这样的函数读取图片类的文件类型,但是很多时候扩展不一定安装了,有时候就需要自己来实现识别文件类型的工作。下面代码就展示了自己通过读取文件头信息来识别文件的真实类型。<?php $files = array( 'c:\1.jpg', 'c:\1.png', 'c:\1.gif', 'c:\1.rar', 'c:\1.zip', 'c:\1.exe', ); foreach ($files AS $file) { $fp = fopen($file, "rb"); $bin = fread($fp, 2); //只读2字节 fclose($fp); $str_info = @unpack("C2chars", $bin); $type_code = intval($str_info['chars1'].$str_info['chars2']); $file_type = ''; switch ($type_code) { case 7790: $file_type = 'exe'; break; case 7784: $file_type = 'midi'; break; case 8075: $file_type = 'zip'; break; case 8297: $file_type = 'rar'; break; case 255216: $file_type = 'jpg'; break; case 7173: $file_type = 'gif'; break; case 6677: $file_type = 'bmp'; break; case 13780: $file_type = 'png'; break; default: $file_type = 'unknown'; break; } echo $file , ' type: <b>', $file_type, '</b> code:<b>', $type_code, '</b><br />'; }本例输出结果c:\1.jpg type: jpg code:255216c:\1.png type: png code:13780c:\1.gif type: gif code:7173c:\1.rar type: rar code:8297c:\1.zip type: zip code:8075c:\1.exe type: exe code:7790
2023年08月04日
26 阅读
0 评论
0 点赞
2023-08-04
php与socket
php与socket一直以来很少看到有多少人使用php的socket模块来做一些事情,大概大家都把它定位在脚本语言的范畴内吧,但是其实php的socket模块可以做很多事情,包括做ftplist,http post提交,smtp提交,组包并进行特殊报文的交互(如smpp协议),whois查询。这些都是比较常见的查询。特别是php的socket扩展库可以做的事情简直不会比c差多少。预备知识:php的socket连接函数1、集成于内核的socket这个系列的函数仅仅只能做主动连接无法实现端口监听相关的功能。而且在4.3.0之前所有socket连接只能工作在阻塞模式下。此系列函数包括fsockopen,pfsockopen这两个函数的具体信息可以查询php.net的用户手册他们均会返回一个资源编号对于这个资源可以使用几乎所有对文件操作的函数对其进行操作如fgets(),fwrite(), fclose()等单注意的是所有函数遵循这些函数面对网络信息流时的规律,例如:fread() 从文件指针 handle 读取最多 length 个字节。 该函数在读取完 length 个字节数,或到达 EOF 的时候,或(对于网络流)当一个包可用时就会停止读取文件,视乎先碰到哪种情况。可以看出对于网络流就必须注意取到的是一个完整的包就停止。2、php扩展模块带有的socket功能。php4.x 有这么一个模块extension=php_sockets.dll,RHT9上安装后也有一个extension=php_sockets.so的(这个依稀记得是有的需要确认一下,好久没有玩linux了)当打开这个此模块以后就意味着php拥有了强大的socket功能,包括listen端口,阻塞及非阻塞模式的切换,multi-client 交互式处理等这个系列的函数列表参看http://www.php.net/manual/en/ref.sockets.php看过这个列表觉得是不是非常丰富呢?不过非常遗憾这个模块还非常年轻还有很多地方不成熟,相关的参考文档也非常少:(我也正在研究中,因此暂时不具体讨论它,仅给大家一个参考文章http://www.zend.com/pecl/tutorials/sockets.php下面举例说明:例子1简单应用——whois查询看一段代码CODE:<?php$server="whois.verisign-grs.com";//TLD .com whois server$data = "";$domain = "abc.com";//serch domain$fp = fsockopen($server,43);if ($fp) {fputs($fp,$domain."\r\n");while (!feof($fp)) {$data .= fgets($fp,1000);}}fclose($fp);echo ln2br($data);}[Copy to clipboard]这个应用因该非常常见了:),不用多废话了。下面看看对于ftplist的应用CODE:<?php$server="192.168.1.3";//服务器ip端口用默认的21举例而已所以不要那么复杂$data = "";$fp = fsockopen($server,21);//打开服务器$data .= fread($fp,1024);//读取状态注意用的fread那么是一个可用报文结束为一段读取。fwrite($fp,"USER hack\r\n");//登陆信息$data .= fgets($fp,1024);//读取是否有此用户,是否等待密码fwrite($fp,"PASS 123456\r\n");//密码$data .= fgets($fp,1024);//是否验证成功fwrite($fp,"REST 100\r\n");//重置数据流$data .= fgets($fp,1024);fwrite($fp,"PWD\r\n");//当前目录$data .= fgets($fp,1024);fwrite($fp,"TYPE A\r\n");//切换传输模式为A——ASCII模式$data .= fgets($fp,1024);fwrite($fp,"CWD ./123456\r\n");//更换目录为123456$data .= fgets($fp,1024);fwrite($fp,"PASV\r\n");//切换为被动模式$tstring = fgets($fp,1024);$data .=$tstring;$ports=ftp_pasvs($tstring);//获取服务器分配的端口及ipfwrite($fp,"LIST -al\r\n");//列表$sock_data=ftp_data_connection($ports);//连接被动模式下的端口while (!feof($sock_data)) {//循环获取数据$data .= fgets($sock_data, 1024);}echo nl2br($data);function ftp_pasvs($string)//用于获取被动模式下的相关连接信息{$ip_port = preg_replace("/^(.+()([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)()?.*\r\n)$/i","\\2",$string);return $ip_port;}function ftp_data_connection($ip_port)//连接服务器数据端口{$ip_port=trim($ip_port);if (!preg_match("/^[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+$/i", $ip_port)) {return "false";}$DATA = explode(",", $ip_port);$ipaddr = $DATA[0].".".$DATA[1].".".$DATA[2].".".$DATA[3];$port = $DATA[4]*256 + $DATA[5];//echo $port."|".$ipaddr;//exit;$data_connection = @fsockopen($ipaddr, $port);if (!$data_connection) {return "false";}return $data_connection;}?>[Copy to clipboard]以上这段代码作了一下简单的注释应该比较清晰,现说明几点问题。1、为什么采用fread:因为此函数是以网络包为截断的,使用这样的截断来获得信息可以很好的保持兼容性,因为对于ftpclient来讲服务器是一个未知的环境(wu-ftp,ser-u,g6ftp...),在这样的环境下有利于读取ftp的特征字符串以便以后使用。2、为什么不用主动模式连接服务器传输数据:主动模式必须客户端指定端口然后进行传输,而php自带的函数并不具备此特性。除非使用扩展库,这样程序兼容性就会。。同时由服务器指定端口避免了客户端寻找合适端口造成的额外负担,尽管这样的负担微乎其微。与这个代码类似的应用还有estmp发送邮件,管与此应用我不多说,有兴趣的朋友可以到我的blog上看看有相关代码(www.freeplug.org)冰血老弟不介意我做点小广告吧哈哈。POST,GET提交这个话题很老了:)我想不需要我在这里提起了文章很多。在看下面一个例子前先提及一组函数pack,unpack。任何一款拥有socket操作能力的语言都有一个专门用于组包的函数,php也不例外当然这组函数的用途不仅仅是组包。下面简单的介绍一下:应用一:输入16进制或者2进制流。CODE:$src="3B06";$binvar = pack('H*',$src);echo $binvar;[Copy to clipboard]看看这个程序,相当于下面的程序CODE:echo chr(0x3B).chr(0x06);[Copy to clipboard]在数据量很小的时候后面的做法,更为简便。但是大量数据的时候,前一种做法则更为实际工整些,代码量也很少。应用二:网络数据流拆包CODE:$elength = str_len($bin);extract(unpack("NLEN/Hcontent", $bin));[Copy to clipboard]将$bin拆成一维数组并解开到变量$LEN和$content内。下面看例子了这个是利用php实现中国移动cmpp登陆消息的一个缩写例子CODE:<?phpdefine("LOGINID",0x00000001);//登陆消息class SMS{var $host;var $mtport;var $moport;var $connectout;var $sms_sock;var $loginuser;var $loginpass;var $error_stop=0;function SMS($host,$user,$pass,$mtport,$moport,$timeout=30,$if_conn=0){$this->host=$host;$this->mtport=$mtport;$this->moport=$moport;$this->loginuser=$user;$this->loginpass=$pass;$if_conn && $this->login();}function login(){$fp=@fsockopen($this->host,$this->mtport,$errno,$errstr,$this->connectout);if(!$fp){$this->sms_sock='';$this->halt("error in login num=$errno, msg=$errstr");return false;}else{//$this->sms_sock=$fp;$data=pack("Na10a32",LOGINID,$this->loginuser,md5($this->loginpass));//这个地方就是组包了$data=pack("N",strlen($data)+4).$data;//$data是实际内容前面这个表示整个报文长度if(fputs($fp,$data) !== false){//print_r(unpack("N",fread($fp,4)));//此处用于调试时检测用//print_r(unpack("N",fread($fp,4)));//print_r(unpack("c",fread($fp,4)));//print_r(unpack("N",fread($fp,4)));@fread($fp,12);$results=@fread($fp,4);if($results){$rs=@unpack("Ncounts",$results);//返回socket结果。$this->sms_sock=$fp;}else{$this->sms_sock='';$this->halt("error in login: loginid=$this->loginuser loginpass=$this->loginpass");return false;}}else{$this->sms_sock='';$this->halt("error in submit logininfo: loginid=$this->loginuserloginpass=$this->loginpass");return false;}}return true;}function halt($msg){echo $msg;flush();$this->error_stop && exit;}}[Copy to clipboard]此例应用:主要用于底层的不依赖于http一类协议的通讯使用。在phpclass这个站点上有个smpp模块更为详细的演示了此类应用有兴趣的朋友可以看看。末了,不多说。由于时间仓促,有些得不对的地方,望各位用pm通知我,我会及时更正,有什么疑问也可以pm我。文章: PHP And Socket 书名: 《PHP Game Programming》作者: Matt Rutledget翻译: heiyeluren <heiyeluren_gmail_com>◇ Socket基础◇ 产生一个服务器◇ 产生一个客户端在这一章里你将了解到迷人而又让人容易糊涂的套接字(Sockets)。Sockets在PHP中是没有充分利用的功能。今天你将看到产生一个能使用客户端连接的服务器,并在客户端使用socket进行连接,服务器端将详细的处理信息发送给客户端。当你看到完整的socket过程,那么你将会在以后的程序开发中使用它。这个服务器是一个能让你连接的HTTP服务器,客户端是一个Web浏览器,这是一个单一的 客户端/服务器 的关系。◆ Socket 基础PHP使用Berkley的socket库来创建它的连接。你可以知道socket只不过是一个数据结构。你使用这个socket数据结构去开始一个客户端和服务器之间的会话。这个服务器是一直在监听准备产生一个新的会话。当一个客户端连接服务器,它就打开服务器正在进行监听的一个端口进行会话。这时,服务器端接受客户端的连接请求,那么就进行一次循环。现在这个客户端就能够发送信息到服务器,服务器也能发送信息给客户端。产生一个Socket,你需要三个变量:一个协议、一个socket类型和一个公共协议类型。产生一个socket有三种协议供选择,继续看下面的内容来获取详细的协议内容。定义一个公共的协议类型是进行连接一个必不可少的元素。下面的表我们看看有那些公共的协议类型。表一:协议名字/常量 描述AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用在IPv4的地址AF_INET6 与上面类似,不过是来用在IPv6的地址AF_UNIX 本地协议,使用在Unix和Linux系统上,它很少使用,一般都是当客户端和服务器在同一台及其上的时候使用表二:Socket类型名字/常量 描述SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。SOCK_SEQPACKET 这个协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序表三:公共协议名字/常量 描述ICMP 互联网控制消息协议,主要使用在网关和主机上,用来检查网络状况和报告错误信息UDP 用户数据报文协议,它是一个无连接,不可靠的传输协议TCP 传输控制协议,这是一个使用最多的可靠的公共协议,它能保证数据包能够到达接受者那儿,如果在传输过程中发生错误,那么它将重新发送出错数据包。现在你知道了产生一个socket的三个元素,那么我们就在php中使用socket_create()函数来产生一个socket。这个socket_create()函数需要三个参数:一个协议、一个socket类型、一个公共协议。socket_create()函数运行成功返回一个包含socket的资源类型,如果没有成功则返回false。Resourece socket_create(int protocol, int socketType, int commonProtocol);现在你产生一个socket,然后呢?php提供了几个操纵socket的函数。你能够绑定socket到一个IP,监听一个socket的通信,接受一个socket;现在我们来看一个例子,了解函数是如何产生、接受和监听一个socket。<?php$commonProtocol = getprotobyname(“tcp”);$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);socket_bind($socket, ‘localhost’, 1337);socket_listen($socket);// More socket functionality to come?>上面这个例子产生一个你自己的服务器端。例子第一行,$commonProtocol = getprotobyname(“tcp”);使用公共协议名字来获取一个协议类型。在这里使用的是TCP公共协议,如果你想使用UDP或者ICMP协议,那么你应该把getprotobyname()函数的参数改为“udp”或“icmp”。还有一个可选的办法是不使用getprotobyname()函数而是指定SOL_TCP或SOL_UDP在socket_create()函数中。$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);例子的第二行是产生一个socket并且返回一个socket资源的实例。在你有了一个socket资源的实例以后,你就必须把socket绑定到一个IP地址和某一个端口上。socket_bind($socket, ‘localhost’, 1337);在这里你绑定socket到本地计算机(127.0.0.1)和绑定socket到你的1337端口。然后你就需要监听所有进来的socket连接。socket_listen($socket);在第四行以后,你就需要了解所有的socket函数和他们的使用。表四:Socket函数函数名 描述socket_accept() 接受一个Socket连接socket_bind() 把socket绑定在一个IP地址和端口上socket_clear_error() 清除socket的错误或者最后的错误代码socket_close() 关闭一个socket资源socket_connect() 开始一个socket连接socket_create_listen() 在指定端口打开一个socket监听socket_create_pair() 产生一对没有区别的socket到一个数组里socket_create() 产生一个socket,相当于产生一个socket的数据结构socket_get_option() 获取socket选项socket_getpeername() 获取远程类似主机的ip地址socket_getsockname() 获取本地socket的ip地址socket_iovec_add() 添加一个新的向量到一个分散/聚合的数组socket_iovec_alloc() 这个函数创建一个能够发送接收读写的iovec数据结构socket_iovec_delete() 删除一个已经分配的iovecsocket_iovec_fetch() 返回指定的iovec资源的数据socket_iovec_free() 释放一个iovec资源socket_iovec_set() 设置iovec的数据新值socket_last_error() 获取当前socket的最后错误代码socket_listen() 监听由指定socket的所有连接socket_read() 读取指定长度的数据socket_readv() 读取从分散/聚合数组过来的数据socket_recv() 从socket里结束数据到缓存socket_recvfrom() 接受数据从指定的socket,如果没有指定则默认当前socketsocket_recvmsg() 从iovec里接受消息socket_select() 多路选择socket_send() 这个函数发送数据到已连接的socketsocket_sendmsg() 发送消息到socketsocket_sendto() 发送消息到指定地址的socketsocket_set_block() 在socket里设置为块模式socket_set_nonblock() socket里设置为非块模式socket_set_option() 设置socket选项socket_shutdown() 这个函数允许你关闭读、写、或者指定的socketsocket_strerror() 返回指定错误号的详细错误socket_write() 写数据到socket缓存socket_writev() 写数据到分散/聚合数组(注: 函数介绍删减了部分原文内容,函数详细使用建议参考英文原文,或者参考PHP手册)以上所有的函数都是PHP中关于socket的,使用这些函数,你必须把你的socket打开,如果你没有打开,请编辑你的php.ini文件,去掉下面这行前面的注释:extension=php_sockets.dll如果你无法去掉注释,那么请使用下面的代码来加载扩展库:<?phpif(!extension_loaded(‘sockets’)){if(strtoupper(substr(PHP_OS, 3)) == “WIN”){dl(‘php_sockets.dll’);}else{dl(‘sockets.so’);}}?>如果你不知道你的socket是否打开,那么你可以使用phpinfo()函数来确定socket是否打开。你通过查看phpinfo信息了解socket是否打开。如下图:查看phpinfo()关于socket的信息◆ 产生一个服务器现在我们把第一个例子进行完善。你需要监听一个指定的socket并且处理用户的连接。<?php$commonProtocol = getprotobyname("tcp");$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);socket_bind($socket, 'localhost', 1337);socket_listen($socket);// Accept any incoming connections to the server$connection = socket_accept($socket);if($connection){socket_write($connection, "You have connected to the socket...\n\r");}?>你应该使用你的命令提示符来运行这个例子。理由是因为这里将产生一个服务器,而不是一个Web页面。如果你尝试使用Web浏览器来运行这个脚本,那么很有可能它会超过30秒的限时。你可以使用下面的代码来设置一个无限的运行时间,但是还是建议使用命令提示符来运行。set_time_limit(0);在你的命令提示符中对这个脚本进行简单测试:Php.exe example01_server.php如果你没有在系统的环境变量中设置php解释器的路径,那么你将需要给php.exe指定详细的路径。当你运行这个服务器端的时候,你能够通过远程登陆(telnet)的方式连接到端口1337来测试这个服务器。如下图:上面的服务器端有三个问题:1. 它不能接受多个连接。2. 它只完成唯一的一个命令。3. 你不能通过Web浏览器连接这个服务器。这个第一个问题比较容易解决,你可以使用一个应用程序去每次都连接到服务器。但是后面的问题是你需要使用一个Web页面去连接这个服务器,这个比较困难。你可以让你的服务器接受连接,然后些数据到客户端(如果它一定要写的话),关闭连接并且等待下一个连接。在上一个代码的基础上再改进,产生下面的代码来做你的新服务器端:<?php// Set up our socket$commonProtocol = getprotobyname("tcp");$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);socket_bind($socket, 'localhost', 1337);socket_listen($socket);// Initialize the buffer$buffer = "NO DATA";while(true){// Accept any connections coming in on this socket$connection = socket_accept($socket);printf("Socket connected\r\n");// Check to see if there is anything in the bufferif($buffer != ""){printf("Something is in the buffer...sending data...\r\n");socket_write($connection, $buffer . "\r\n");printf("Wrote to socket\r\n");}else{printf("No Data in the buffer\r\n");}// Get the inputwhile($data = socket_read($connection, 1024, PHP_NORMAL_READ)){$buffer = $data;socket_write($connection, "Information Received\r\n");printf("Buffer: " . $buffer . "\r\n");}socket_close($connection);printf("Closed the socket\r\n\r\n");}?>这个服务器端要做什么呢?它初始化一个socket并且打开一个缓存收发数据。它等待连接,一旦产生一个连接,它将打印“Socket connected”在服务器端的屏幕上。这个服务器检查缓冲区,如果缓冲区里有数据,它将把数据发送到连接过来的计算机。然后它发送这个数据的接受信息,一旦它接受了信息,就把信息保存到数据里,并且让连接的计算机知道这些信息,最后关闭连接。当连接关闭后,服务器又开始处理下一次连接。(翻译的烂,附上原文)This is what the server does. It initializes the socket and the buffer that you use to receiveand send data. Then it waits for a connection. Once a connection is created it prints“Socket connected” to the screen the server is running on. The server then checks to see ifthere is anything in the buffer; if there is, it sends the data to the connected computer.After it sends the data it waits to receive information. Once it receives information it storesit in the data, lets the connected computer know that it has received the information, andthen closes the connection. After the connection is closed, the server starts the wholeprocess again.◆ 产生一个客户端处理第二个问题是很容易的。你需要产生一个php页连接一个socket,发送一些数据进它的缓存并处理它。然后你又个处理后的数据在还顿,你能够发送你的数据到服务器。在另外一台客户端连接,它将处理那些数据。To solve the second problem is very easy. You need to create a PHP page that connects toa socket, receive any data that is in the buffer, and process it. After you have processed thedata in the buffer you can send your data to the server. When another client connects, itwill process the data you sent and the client will send more data back to the server.下面的例子示范了使用socket:<?php// Create the socket and connect$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);$connection = socket_connect($socket,’localhost’, 1337);while($buffer = socket_read($socket, 1024, PHP_NORMAL_READ)){if($buffer == “NO DATA”){echo(“NO DATA”);break;}else{// Do something with the data in the bufferecho(“Buffer Data: “ . $buffer . “”);}}echo(“Writing to Socket”);// Write some test data to our socketif(!socket_write($socket, “SOME DATA\r\n”)){echo(“Write failed”);}// Read any response from the socketwhile($buffer = socket_read($socket, 1024, PHP_NORMAL_READ)){echo(“Data sent was: SOME DATA Response was:” . $buffer . “”);}echo(“Done Reading from Socket”);?>这个例子的代码演示了客户端连接到服务器。客户端读取数据。如果这是第一时间到达这个循环的首次连接,这个服务器将发送“NO DATA”返回给客户端。如果情况发生了,这个客户端在连接之上。客户端发送它的数据到服务器,数据发送给服务器,客户端等待响应。一旦接受到响应,那么它将把响应写到屏幕上。结合Socket的坦克大战(因为是描述游戏和socket结合,跟本文联系不大,所以不翻译,建议参考英文原文)[ 题外话 ]翻译文章的初衷是因为我个人对socket非常感兴趣,而且目前国内见php的文章比较少,除了php手册里面的部分内容,所以在我看了《PHP Game Programming》这本书里有关于socket的内容后毅然决定要翻译,我知道翻译出来的质量不行,还请见谅。另外,我在《Core PHP Programming》Third Edition中也发现里面的Socket内容讲的不错,如果有空,我想也许我会把它也给翻译一下。这是我第一次翻译文章,花了我近五个小时,文章可以说是错误百出,如果翻译的不合理请见谅,如果有兴趣提高这个内容可以给我发邮件。这个凌晨时分,竟然无法入眠,不知道是不是在其他角落,也有人同我一样。希望本文能够给向学习PHP Socket编程的朋友一点帮助,感谢你阅读这个错误百出的文章。翻译:heiyeluren <heiyeluren_at_gmail.com>时间:2005-8-14 04:46
2023年08月04日
18 阅读
0 评论
0 点赞
2023-08-04
PHP中操作MySQL的需注意的问题
PHP中操作MySQL的需注意的问题1.每一行命令都是用分号 (;) 作为结束对于 MySQL ,第一件你必须牢记的是它的每一行命令都是用分号 (;) 作为结束的,但……没有完全绝对的事,在这儿也是一样,当一行 MySQL 被插入在 PHP 代码中时,最好把后面的分号省略掉,例如:mysql_query ("INSERT INTO tablename (first_name, last_name) VALUES ('$first_name', $last_name')");这是因为 PHP 也是以分号作为一行的结束的,额外的分号有时会让 PHP 的语法分析器搞不明白,所以还是省略掉的好。在这种情况下,虽然省略了分号,但是 PHP 在执行 MySQL 命令时会自动的帮你加上的。另外还有一个不要加分号的情况。当你想把要字段的竖者排列显示下来,而不是像通常的那样横着排列时,你可以用 G 来结束一行 SQL 语句,这时就用不上分号了,例如:SELECT * FROM PENPALS WHERE USER_ID = 1G2. TEXT、DATE、和 SET 数据类型MySQL 数据表的字段必须有定义一个数据类型。这有大约 25 种选择,大部分都是直接明了的,但有几个有必要提一下。DATE 数据类型的格式是 YYYY-MM-DD ,比如: 1999-12-08 。你可以很容易的用 date 函数来得到这种格式的当前系统时间: date("Y-m-d")并且,在 DATA 数据类型之间可以作减法,得到相差的时间天数:$age = ($current_date - $birthdate);集合 SET 是一个有用的数据类型,它和枚举 ENUM 有点相似,只不过是 SET 能够保存多个值而 ENUM 只能保存一个值而已。而且, SET 类型最多只能够有 64 个预定的值,而 ENUM 类型却能够处理最多 65,535 个预定义的值。而如果需要有大于 64 个值的集合,该怎么办呢?这时就需要定义多个集合来一起解决这个问题了。3. 通配符SQL 的通配符有两种:“ * ”和“ % ”。分别用在不同的情况下。例如:如果你想看到数据库的所有内容,可以像这样来查询:SELECT * FROM dbname WHERE USER_ID LIKE '%';这儿,两个通配符都被用上了。他们表示相同的意思 ?? 都是用来匹配任何的字符串,但是他们用在不同的上下文中。“ * ”用来匹配字段名,而“ % ”用来匹配字段值。另外一个不容易引起注意的地方是“ % ”通配符需要和 LIKE 关键字一起使用。还有一个通配符,就是下划线“ _ ”,它代表的意思和上面不同,是用来匹配任何单个的字符的。4. NOT NULL 和空记录如果用户在没有填任何东西的情况下按了 submit 按钮,会怎样呢?如果你确实需要一个值,那么可以用客户端脚本或者服务器端脚本来进行数据验证,这一点在前面已经说过了。但是,在数据库中却是允许一些字段被空出来什么也不填。对此类纪录, MySQL 将要为之执行一些事情:插入值 NULL ,这是缺省的操作。如果你在字段定义中为之声明了 NOT NULL (在建立或者修改这个字段的时候), MySQL 将把这个字段空出来什么东西也不填。对于一个 ENUM 枚举类型的字段,如果你为之声明了 NOT NULL , MySQL 将把枚举集的第一个值插入到字段中。也就是说, MySQL 把枚举集的第一个值作为这个枚举类型的缺省值。一个值为 NULL 的纪录和一个空纪录是有一些区别的。 % 通配符可以匹配空纪录,但是却不能匹配 NULL 纪录。在某些时候,这种区别会造成一些意想不到的后果。就我的经验而言,任何字段都应该声明为 NOT NULL 。这样下面的 SELECT 查询语句就能够正常运转了:if (!$CITY) {$CITY = "%";}$selectresult = mysql_query ("SELECT * FROM dbnameWHERE FIRST_NAME = ' 兄 'AND LAST_NAME = ' 第连 'AND CITY LIKE '$CITY'");在第一行中,如果用户没有指定一个 CITY 值,那么就会用通配符 % 来代入 CITY 变量,这样搜索时就会把任何的 CITY 值都考虑进去,甚至包括那些 CITY 字段为空的纪录。但是如果有一些纪录,它的 CITY 字段值是 NULL ,这时问题就出现了。上面的查询是不能够找到这些字段的。问题的一个解决办法可以是这样:if (!$CITY) { $CITY = "%"; }$selectresult = mysql_query ("SELECT * FROM dbnameWHERE FIRST_NAME = ' 兄'AND LAST_NAME = ' 第连'AND (CITY LIKE '$CITY' OR CITY IS NULL)");注意在搜索 NULL 时,必须用“ IS ”关键字,而 LIKE 时不会正常工作的。在最后要提到的是,如果你在加入或者修改一个新的字段之前,数据库中已经有了一些记录了,这时新加入的字段在原来的纪录中的值,可能是 NULL ,也可能为空。这也算是 MySQL 的一个 Bug 吧,所以在这种情况下,使用 SELECT 查询要特别的小心。
2023年08月04日
11 阅读
0 评论
0 点赞
2023-08-04
如何防止 PHP 中的 SQL 注入?
https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php
2023年08月04日
10 阅读
0 评论
0 点赞
1
...
141
142
143
...
158