PHPcms v9 get标签 sql语句limit无效及num和rows属性无效问底层分析题解决全攻略
2013-06-04 16:51:23;  来源:追太阳;  作者:逐日者;  评论:0 点击:

PHPcms v9 get标签sql语句limit无效及num和rows属性无效问题至今也已经是一个老问题了,网络上也有一些表面的解决办法,但是逐日者并没有找到从底层,层模板标签解析及模板引擎代码上真正解决此问题的办法和解释。今天在使用get标签sql语句调用数据的时候当然也遇到了此问题,经过研究找到了问题的原因并且整理出本文希望对php爱好者有所帮助。
PHPcms v9 get标签sql语句limit无效及num和rows属性无效问题至今也已经是一个老问题了,网络上也有一些表面的解决办法,但是逐日者并没有找到从底层,层模板标签解析及模板引擎代码上真正解决此问题的办法和解释。 本人比较喜欢phpcms清晰的mvc模式构架,所以追太阳网站的部分也由此构建。今天在使用get标签sql语句调用数据的时候当然也遇到了此问题,经过研究找到了问题的原因并且整理出本文希望对php爱好者有所帮助。

提示:如果您并不怎么喜欢编程只是想尽快解决这个问题,您可以直接看下文给你整理提供的网络上已有的快速解决办法,如果您想从phpcms的底层代码上解决这个问题,其实也只要在对应的模板标签解析函数上加一句代码即可,而且这样就可以正常使用num属性来指定获取数据的数目了。对于php编程爱好者重要是原因分析及解决思维过程,不是吗?^.^

问题描述: 逐日者想从数据库调用8条点击量最高的书籍的信息显示在前台,通过get标签,代码如下:
	<ul>

	{pc:get sql="select * from zty_category where parentid=22 or parentid=23 or parentid=52 order by allclicks desc" num='8'}

	{loop $data $r}

	<li><a href="{$r[url]}" class="bookimg"><img src="{$r[image]}" width="130" height="170" alt="{$r[catname]}"/></a><a href="{$r[url]}" class="bookname">{$r[catname]}<span style="font-size: 10px;color:#666">({$r[allclicks]}views)</span></a></li>

	{/loop}

	{/pc}

	</ul>
但结果显示的数量超过了8条,把数据库中所有的10条数据都显示了出来,这说明rows属性无效。
(了解phpcms的细心的朋友可能会说了:控制数量应该是用num属性啊,你为什么使用了rows属性,这样当然无效了,这也是逐日者平常不爱好背这些标签这类东西,所以使用的时候直接网上一百度,搜出来的,看来网络各种copy使得很多东西准确性都不够,或者rows是用于老版本的,说这么多只是想提醒大家:虽然网上流传了很多资料中get标签sql语句调用数据数量控制使用rows属性对于phpcms v9是不正确的。
那么如果rows属性无效,我直接在sql语句中加入limit来显示8条数据算了(此时本人仍然不知使用num属性控制数量,本人对于phpcms并不是侵淫多年的老用户,惭愧惭愧--!),标签更改如下:

…… {pc:get sql="select * from z_category where parentid=22 or parentid=23 order by allclicks desc LIMIT 8" } {loop $data $r}…… 结果系统报出如下sql语法错误:

很明显,这是phpcms系统解析标签的时候如果没有获得有效的指定数据条数的参数时会默认追加“limit 20”的限制量。现在看来简单的在源sql语句上追加limit 8也是不行的。

逐日者也很快知道了指定数量用num属性,但是正如网又们知道的,num属性也无效(为什么num属性是正确的指定数量的参数在后面的底层代码讲解中您将得到回答)。返回的仍然是系统默认追加的“limit 20”的限制。
如此说明,在解析源代码地方出现了获取num属性值和判断是否有此值是否要使用默认20的错误。

在下面先给出整理的网络上的解决方法,然后再给大家讲解怎样去除源代码上的错误来正常的使用num属性指定获取数据条数。

整理的网络上的解决方案(来源phpcms官方论坛):
1.方法一,加入start属性,比如
	{pc:get  sql="SELECT title,url FROM v9_news where catid=9 and status=99 order by updatetime desc" start="0" num="4" return="v"}

  可以加入start 和 num 来控制。这样就获得0到4的数据。

  2.方法二(比较绝的方法),加入--将系统自动加上的limit 20注释掉:

	{pc:get  sql="SELECT title,url FROM v9_news where catid=9 and status=99 order by updatetime desc limit 0,4--" return="v"}

  注意4后面的两个减号,把v9自带的LIMIT 0,20语句给注释了!这样就不会和你加入的limit 0,4冲突了。

逐日者给出的层底层去掉源代码错误的办法(只要改一行代码哦!)

在/phpcms/libs/classes/template_cache.class.php模板解析缓存文件中找到pc_tag()方法,大约在115行左右,然后在

131 $str = '';
132 $num = isset($num) && intval($num) ? intval($num) : 20;

第131行和第132行之间插入一句,如下:

131 $str = '';
132 $num = eval("return ".$num.";");//插入语句
133 $num = isset($num) && intval($num) ? intval($num) : 20;
这样,大功告成!在后台更新一下缓存,然后测试一下,get标签中num属性指定数据数量就可以用了。

接下来就是对于此错误的分析及解决过程了,如果您有兴趣可以继续往下看:

bug分析及解决过程:
首先,我们把涉及get标签的解析原程序代码贴出来并用注释将关键点讲解,如下:

	public static function pc_tag($op, $data, $html) {
	    preg_match_all("/([a-z]+)\=[\"]?([^\"]+)[\"]?/i", stripslashes($data), $matches, PREG_SET_ORDER);//正则,获得标签中的属性及对应设定的值(实际上是等号后的字符串段)
	    $arr = array('action','num','cache','page', 'pagesize', 'urlrule', 'return', 'start');//设定属性数组,里面没有rows,所以说本文刚开始使用rows属性是无效的
	    $tools = array('json', 'xml', 'block', 'get');
	    $datas = array();
	    $tag_id = md5(stripslashes($html));
	    //可视化条件
	    $str_datas = 'op='.$op.'&tag_md5='.$tag_id;
	    foreach ($matches as $v) {//遍历正则匹配返回的属性与值数组(数组格式可见下面打印的matches数组格式图)
	        $str_datas .= $str_datas ? "&$v[1]=".($op == 'block' && strpos($v[2], '$') === 0 ? $v[2] : urlencode($v[2])) : "$v[1]=".(strpos($v[2], '$') === 0 ? $v[2] : urlencode($v[2]));
	        if(in_array($v[1], $arr)) {
	            $$v[1] = $v[2];//利用可变变量来将标签中所有属性压入可用变量(当我们定义了num属性后在这里就成了 $num=值 形式,下面就可以调用了)
	            continue;
	        }
	        $datas[$v[1]] = $v[2];
	    }
	    $str = '';

	    $num = isset($num) && intval($num) ? intval($num) : 20;//三元表达式,如果$num存在并且转换成整数成功就将这个整数赋给$num,否则值默认20.!!BUG源就在这里~

	    $cache = isset($cache) && intval($cache) ? intval($cache) : 0;
	    $return = isset($return) && trim($return) ? trim($return) : 'data';
	    if (!isset($urlrule)) $urlrule = '';
	    if (!empty($cache) && !isset($page)) {
	        $str .= '$tag_cache_name = md5(implode(\'&\','.self::arr_to_html($datas).').\''.$tag_id.'\');if(!$'.$return.' = tpl_cache($tag_cache_name,'.$cache.')){';
	}
	       if (in_array($op,$tools)) {
	           switch ($op) {//判断是什么标签对应处理

	           case 'json':
	           if (isset($datas['url']) && !empty($datas['url'])) {
	               $str .= '$json = @file_get_contents(\''.$datas['url'].'\');';
	               $str .= '$'.$return.' = json_decode($json, true);';
	           }
	           break;

	          case 'xml':
	          $str .= '$xml = pc_base::load_sys_class(\'xml\');';
	          $str .= '$xml_data = @file_get_contents(\''.$datas['url'].'\');';
	          $str .= '$'.$return.' = $xml->xml_unserialize($xml_data);';
	          break;

	          case 'get'://以下是处理get标签的地方
	          $str .= 'pc_base::load_sys_class("get_model", "model", 0);';
	          if ($datas['dbsource']) {
	              $dbsource = getcache('dbsource', 'commons');
	              if (isset($dbsource[$datas['dbsource']])) {
	                    $str .= '$get_db = new get_model('.var_export($dbsource,true).', \''.$datas['dbsource'].'\');';
	                    } else {
	              return false;
	              }
	          } else {
	              $str .= '$get_db = new get_model();';
	          }
	          $num = isset($num) && intval($num) > 0 ? intval($num) : 20;//又判断了一遍$num
	          if (isset($start) && intval($start)) {
	              $limit = intval($start).','.$num;
	          } else {
	              $limit = $num;//将$num值给$limit
	          }
	          if (isset($page)) {//如果有分页
	          $str .= '$pagesize = '.$num.';';
	          $str .= '$page = intval('.$page.') ? intval('.$page.') : 1;if($page<=0){$page=1;}';
	          $str .= '$offset = ($page - 1) * $pagesize;';
	          $limit = '$offset,$pagesize';
	          if ($sql = preg_replace('/select([^from].*)from/i', "SELECT COUNT(*) as count FROM ", $datas['sql'])) {
	              $str .= '$r = $get_db->sql_query("'.$sql.'");$s = $get_db->fetch_next();$pages=pages($s[\'count\'], $page, $pagesize, $urlrule);';
	          }
	          }
	          $str .= '$r = $get_db->sql_query("'.$datas['sql'].' LIMIT '.$limit.'");while(($s = $get_db->fetch_next()) != false) {$a[] = $s;}$'.$return.' = $a;unset($a);';//在这里结合以上传递过来的$num属性值给sql语句中的limit并执行此sql语句。
	          break;

	          case 'block':
	          $str .= '$block_tag = pc_base::load_app_class(\'block_tag\', \'block\');';
	          $str .= 'echo $block_tag->pc_tag('.self::arr_to_html($datas).');';
	          break;
	      }
	} else {
	。。。。。。


以上是我们通过注释文字给大家分析了以下具体get标签的解析过程。指出
$num = isset($num) && intval($num) ? intval($num) : 20;
一句是出问题的根源。那么我们设定get标签中的 num=‘8’ ,然后用var_dump()打印几个关键的变量观察一下:

。。。。。。
echo "<pre>";
print_r($matches);
echo "</pre>";
。。。。。。
$str = '';
echo "<br>====================<br>";
var_dump($num);
echo "<br>====================<br>";
echo intval($num);
$num =isset($num) && intval($num) ? $num:20;
echo "<br>====================<br>";
echo $num;
。。。。。。
输出打印结果如下:


由此可见,出问题的关键在于正则匹配后返回的num的值是为‘8’的长度为3的字符串,也就是说其中的前后单引号也是字符串的元素之一,而不是表示字符串数据类型的引号,又因为我们知道intval()函数在对字符串进行转换的时候当字符串的第一个字符不是数字的时候会返回0.因此才有了下面的打印intval($num)为0的结果,那么在$num = isset($num) && intval($num) ? intval($num) : 20;的三元判断当中返回为20也就不足为奇了。

在给出的解决方法当中,通过eval()函数,将“ ‘8’ ” 这个字符串当做程序段运行后,8前后的两个单引号就变成了表示字符串数据类型引号,这样在后面的整形转换时就会返回对应的整数,是$num在之后的使用都是正确的值。

 

本文属追太阳原创文章,转载请声明出处:http://www.zhuitaiyang.com/html/fcms/70.html逐日者_追太阳

相关热词搜索:PHPcms v9 get标签 sql limit num rows 无效 底层分析 解决办法

上一篇:dedecms栏目添加选项字段--栏目自定义字段
下一篇:DeDecms首页文章标题显示的字数太短怎么解决

收藏
回到顶部