Boost.PHP で SPL のイテレータを実装する

残念ながら所用で参加できなかった モダンPHP勉強会。SPL の発表を Ust 経由で聞いて楽しくなってきたので、Boost.PHP でインターフェイスを実装できるようにしてみた。

追記: Boost.PHP の導入については、id:Kiske さんが素敵なチュートリアルを書いているのでそちらをご覧あれ

m009.cpp:

#include "boost/php/module.hpp"
#include "boost/php/function.hpp"
#include "boost/php/klass.hpp"
#include <boost/mpl/vector.hpp>
#include <boost/shared_ptr.hpp>
#include <Zend/zend_interfaces.h>

using namespace boost;

class RangeIterator {
public:
    RangeIterator(int start, int end): cnt_(start), till_(end) {}

    int current() const {
        return cnt_;
    }

    int key() const {
        return cnt_;
    }

    void rewind() {
        cnt_ = 0;
    }

    bool valid() const {
        return cnt_ < till_;
    }

    void next() {
        ++cnt_;
    }

private:
    int cnt_;
    const int till_;
};

class m009_module
    : public php::module,
      public php::function_container<m009_module> {
public:
    class handler
        : public php::module::handler {
    public:
        handler(m009_module* mod)
            : php::module::handler(mod) {}

        void __initialize(TSRMLS_D) {
            php::def_class<RangeIterator>("RangeIterator", boost::mpl::vector2<int, int>() TSRMLS_CC)
                .implements(::zend_ce_iterator)
                .defun("rewind", &RangeIterator::rewind)
                .defun("key", &RangeIterator::key)
                .defun("current", &RangeIterator::current)
                .defun("next", &RangeIterator::next)
                .defun("valid", &RangeIterator::valid)
                .fixup();
        }
    };
public:
    m009_module(zend_module_entry* entry)
            : php::module(entry) {
    }
};

#define BOOST_PHP_MODULE_NAME m009
#define BOOST_PHP_MODULE_CAPITALIZED_NAME M008
#define BOOST_PHP_MODULE_VERSION "0.1"
#define BOOST_PHP_MODULE_CLASS_NAME m009_module

#include "boost/php/module_def.hpp"

こんな感じで implements(...) の引数に zend_class_entry へのポインタを指定しておくと、そのインターフェイスを実装したクラスを作れるという仕様。

もちろん上の拡張モジュールをコンパイルすると、

<?php
foreach(new RangeIterator(0, 5) as $i) {
    var_dump($i);
}
?>

int(0)
int(1)
int(2)
int(3)
int(4)

という結果を返します!

Enjoy!