Zephir、PHP-CPP で UTF-8 対応の文字列関数を開発する

Zephir と PHP-CPP の学習および比較のために、UTF-8 の文字列関数の開発に取り組みました。今後、複数の関数や C 言語の例を追加してゆく予定です。

インストール
----------

### Zephir

ビルドツールをインストールした上で、git のリポジトリからビルドします。

```bash
git clone https://github.com/phalcon/zephir
cd zephir
./install -c
```

### PHP-CPP

```
git clone https://github.com/CopernicaMarketingSoftware/PHP-CPP.git
cd PHP-CPP
sudo make install
```


Mac OS X の場合、Makefile を次のように修正しました。


```
INSTALL_PREFIX                  =       /usr/local

LINKER_FLAGS                    =       -shared -undefined dynamic_lookup


${PHP_SHARED_LIBRARY}: ...
...     ${LINKER} ${PHP_LINKER_FLAGS} -Wl -o libphpcpp.so.$(SONAME)
```

プロジェクトのセットアップとビルド
-----------------------------

### Zephir

```
zephir init myproject
```

ビルドには `zephir compile` を実行します。`zephir build` はインストールも実行されます。


### PHP-CPP

[Your first extension](http://www.php-cpp.com/documentation/your-first-extension) のチュートリアルページから EmptyExtension.zip および EmptyExtension.tar.gz をダウンロードします。ビルドとインストールのために make および make install を実行します。

Mac OS X の場合、Makefile を次のように修正しました。

```
INI_DIR                         =       /usr/local/etc/php/5.6/conf.d

COMPILER_FLAGS          =       -I /usr/local/include/ -I /usr/local/include/phpcpp -Wall -c -O2 -std=c++11 -fpic -o
LINKER_FLAGS            =       -shared -L /usr/local/lib 
LINKER_DEPENDENCIES     =       -lphpcpp
```

### C 言語

プロジェクトのセットアップには `ext_skel` コマンドを使います。

```
cd /path/to/ext
./ext_skel --extname=helloworld
```

config.m4 の次の3行のコメントを解除します。

```
PHP_ARG_WITH(helloworld, for helloworld support,
Make sure that the comment is aligned:
[  --with-helloworld             Include helloworld support])
```

Makefile の生成には phpize を実行します。make および make install でビルドおよびインストールを実行します。

```
phpize
./configure
make
make install
```

### PHP 7.0 対応

zend_string API と smart_string が導入されました [Upgrading PHP extensions from PHP5 to NG](https://wiki.php.net/phpng-upgrading)。また引数の解析のパフォーマンス改善のために [Fast Parameter Parsing API](https://wiki.php.net/rfc/fast_zpp) が導入されました。

UTF-8 の仕様
-----------

Unicode 8.0 の[3章](http://www.unicode.org/versions/Unicode8.0.0/ch03.pdf) で定義されています (Table 3-7. Well-Formed UTF-8 Byte Sequences)。

```
      コードポイント  1番目のバイト 2番目のバイト 3番目のバイト 4番目のバイト
  U+0000 -   U+007F   00 - 7F
  U+0080 -   U+07FF   C2 - DF    80 - BF
  U+0800 -   U+0FFF   E0         A0 - BF     80 - BF
  U+1000 -   U+CFFF   E1 - EC    80 - BF     80 - BF
  U+D000 -   U+D7FF   ED         80 - 9F     80 - BF
  U+E000 -   U+FFFF   EE - EF    80 - BF     80 - BF
 U+10000 -  U+3FFFF   F0         90 - BF     80 - BF    80 - BF
 U+40000 -  U+FFFFF   F1 - F3    80 - BF     80 - BF    80 - BF
U+100000 - U+10FFFF   F4         80 - 8F     80 - BF    80 - BF
```

拡張書記素クラスターの境界の仕様
---------------------------

旧字体や色違いの絵文字、梵字など、1つの文字が基底文字と結合記号の組み合わせで表現されることがあります。このような複数のコードポイントで構成される基本単位を拡張書記素クラスターと呼びます。拡張書記素クラスターの境界は UNICODE TEXT SEGMENTATION の仕様の [Grapheme Cluster Boundary Rules](http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules) で定義されます。

PCRE は拡張書記素クラスターをあらわすメタ文字の `\X` を定義しており、`(?>\PM\pM*)` と同等とのことです。

C 言語で書記素を1つずつ取り出して表示するコードの例は [gist](https://gist.github.com/masakielastic/5e37a7cb3e127b30cb53) で公開しています。

文字とコードポイントの相互変換
-------------------------

### C 言語
コードの例は[こちら](http://blog.sarabande.jp/post/57502757210)の記事をご参照ください。

文字数を求める
------------

記事の公開時点では不正なバイト列は考慮しない状態です。

### Zephir

```
namespace StrUtils;

function length(string str)
{
    int bytesize;
    int len, current, size;
    uchar lead;

    let bytesize = str->length();
    let len = 0;
    let current = 0;
    let size = 0;

    while bytesize > current {
        let lead = str[current];

        if lead < 0x80 {
            let size = 1;
        } elseif lead < 0xE0 {
            let size = 2;
        } elseif lead < 0xF0 {
            let size = 3;
        } elseif lead < 0xF5 {
            let size = 4;
        } else {
            let size = 1;
        }

        let current += size;
        let len++;
    }

    return len;
}
```


### PHP-CPP

```cpp
#include <phpcpp.h>
#include <string>

Php::Value length(Php::Parameters &amp;params)
{
    std::string str = params[0];

    int pos;
    unsigned char lead;
    int size;

    Php::Value len = 0;

    for (pos = 0; pos < str.size(); pos += size) {

        lead = str.at(pos);

        if (lead < 0x80) {
            size = 1;
        } else if (lead < 0xE0) {
            size = 2;
        } else if (lead < 0xF0) {
            size = 3;
        } else {
            size = 4;
        }

        len += 1;
    }

    return len;
}


extern "C" {
    
    PHPCPP_EXPORT void *get_module() 
    {
        static Php::Extension extension("strutils", "1.0");

        extension.add("StrUtils\\length", length);

        return extension;
    }

}
```

### C 言語

`standard/html.c` で定義される `php_next_utf8_char` を使うことができます。この関数は `htmlspecialchars` の実装に使われています。コードの例は[こちら](http://blog.sarabande.jp/post/57158304150)の記事をご参照ください。