4

私のアプリケーションは PDF ドキュメントを作成しています。スクリプトを使用して各ページの HTML を生成します。PDF生成クラスは「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 ループは、インデックス ページ (実際にはドキュメントの表紙の後の 2 ページ目) で終了します。

ドキュメント レイアウトは次のようになります。

1) 表紙

2) 目次

3) ページ A

4) ページ B

5) ページ C

foreach ループ内の TableOfContents.php は、必要に応じてページを調べ、ドキュメント全体のインデックスを作成しますが、Production 内のループは目次で終了し、ページ A、B、および C のレンダリングには進みません。

TableOfContents.php から foreach を削除すると、連続するすべてのページが適切にレンダリングされます。

ポインタと変数のスコープに問題があるような気がするのですが、どうすれば直せますか?

4

3 に答える 3

3

診断

問題は、通常のPHP配列ではなく、インターフェイスを実装するオブジェクトであると思われます。このため、foreachループの「状態」はオブジェクト自体に保存されます。これは、2つのループが競合していることを意味します。$_pagesIterator

$_pagesプレーン配列の場合、行$pages = $this->production->getPages();コピーを作成するため、PHP配列は(オブジェクトとは異なり)割り当て時にコピーさforeachれ、通常の配列のネストされたループにはその問題がないため、問題はありません。(おそらく、いくつかの内部配列のコピー/割り当てロジックからです。)

解決

$_pages「速くて汚い」修正はforeachループを回避することですが、それは非常に特別な処理が必要であることを忘れがちなので、迷惑であり、将来のバグの原因になると思います。

実際の修正については、オブジェクトの背後にあるクラスを調べて、$_pagesそのクラスを変更できるかどうかを確認することをお勧めします。である代わりに、インターフェイスを介してイテレータを提供する$_pages ようIterator変更$_pagesします。IteratorAggregate

このようにして、すべてforeachのループが個別のイテレータオブジェクトを要求し、個別の状態を維持します。

これは、問題を説明するためのサンプルスクリプトであり、PHPリファレンスページから部分的に削除されています。

<?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.
 */
于 2011-07-05T19:31:52.030 に答える
0

あなたの問題が何であるかを理解することはできませんが、PHP関数を見ることができますreset=)

于 2011-07-05T19:02:09.120 に答える
0

ここで提案されているように、解決策は foreach の使用を避け、従来のループを使用することでした: PHP の問題でのネストされた foreach

于 2011-07-05T19:16:12.630 に答える