我的应用程序是构建PDF文档.它使用脚本来生成每个页面的HTML.
PDF-Generating类是“Production”,页面类是“Page”.
class Production
{
private $_pages; // an array of "Page" objects that the document is composed of
public getPages()
{
return $this->_pages;
}
public render()
{
foreach($this->_pages as $page) {
$pageHtml = $page->getHtml($this); // Page takes a pointer to production to access some of its data.
}
}
}
这是Page类摘要:
class Page
{
private $scriptPath; // Path to Script File (PHP)
public function getHtml(Production &$production)
{
$view = new Zend_View();
$view->production = $production;
return $view->render($this->scriptPath);
}
}
我在编写目录时遇到了问题.它访问Production,获取所有页面,查询它们,并根据页面标题构建TOC:
// TableOfContents.PHP
// "$this" refers to Zend_View from Pages->getHtml();
$pages = $this->production->getPages();
foreach($pages as $page) {
// Populate TOC
// ...
// ...
}
会发生什么是TableOfContents.PHP中的foreach干扰了生产中的foreach.生产foreach循环终止于索引页面(实际上是封面页之后的文档中的第二页).
文档布局是这样的:
1)封面
2)目录
3)页面A.
4)页面B.
5)页面C.
TableOfContents.PHP,在其foreach循环中,根据需要遍历页面并构建整个文档的索引,但生产中的循环终止于目录,不会继续呈现页面A,B和C.
如果我从TableOfContents.PHP中删除foreach,则会正确呈现所有连续页面.
我觉得这是指针和变量范围的问题,所以我该怎么做才能修复它?
解决方法:
诊断
我怀疑问题是$_pages不是普通的PHP数组,而是碰巧实现Iterator
interface的对象.因此,foreach循环的“状态”存储在对象本身上,这意味着两个循环是冲突的.
如果$_pages是一个普通数组,那么就没有问题,因为行$pages = $this-> production-> getPages();会复制,因为PHP数组在赋值时复制(与对象不同),也因为普通数组上的嵌套foreach循环没有这个问题. (可能来自一些内部数组复制/辅助逻辑.)
解
“快速和肮脏”的修复是为了避免foreach循环,但我认为这既烦人又是未来错误的原因,因为很容易忘记$_pages需要超级特殊处理.
对于一个真正的修复,我建议查看$_pages中对象后面的任何类,看看是否可以更改该类.而不是将$_pages作为Iterator,更改$_pages,以便通过IteratorAggregate
接口提供迭代器.
这样每个foreach循环都会请求一个单独的迭代器对象并维护单独的状态.
<?PHP
class MyIterator implements Iterator
{
private $var = array();
public function __construct($array)
{
if (is_array($array)) {
$this->var = $array;
}
}
public function rewind()
{
reset($this->var);
}
public function current()
{
$var = current($this->var);
return $var;
}
public function key()
{
$var = key($this->var);
return $var;
}
public function next()
{
$var = next($this->var);
return $var;
}
public function valid()
{
$key = key($this->var);
$var = ($key !== NULL && $key !== FALSE);
return $var;
}
}
// END BOILERPLATE DEFinitioN OF IteraTOR, START OF INTERESTING PART
function getMyArrayThingy(){
/*
* hey, let's conveniently give them an object that
* behaves like an array. It'll be convenient!
* nothing Could possibly go wrong, right?
*/
return new MyIterator(array("a","b","c"));
}
// $arr = array("a,b,c"); // This is old code. It worked fine. Now we'll use the new convenient thing!
$arr = getMyArrayThingy();
// We expect this code to output nine lines, showing all combinations of a,b,c
foreach($arr as $item){
foreach($arr as $item2){
echo("$item, $item2\n");
}
}
/*
* Oh no! It printed only a,a and a,b and a,c!
* The outer loop exited too early because the counter
* was set to C from the inner loop.
*/
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。