PHP extension 개발을 손쉽게 - PHP-CPP

php sass extension을 만든 경험을 정리했습니다.

개요

아주 오래전에 C 라이브러리를 PHP 에서 사용하기 위해 PHP extension 으로 만들어 본 적이 있습니다. 당시의 기억을 떠올려보면 extension 을 만들 때 가장 어려웠던 점은 문서가 없는 점이었습니다.

PHP extension 만들때 필요한 Zend 엔진의 내부 API 에 대한 문서가 전무했기 때문에 기존 extension 의 소스를 참고해서 작업을 마쳤던 기억이 납니다.


Zend 엔진의 API 에 대한 문서화 상황은 현재도 그리 나아진 것 같지 않으며 이때문에 여전히 PHP extension을 만드는 것은 쉽지 않은 작업입니다.

PHP-CPP 는 이런 문제를 해결하기 위해 extension 을 쉽게 만들수 있도록 도와주는 헬퍼 라이브러리로 최신 C++ 로 작성되어 사용이 쉽고 잘 정리된 매뉴얼이 제공됩니다.

특히 Zend 엔진의 내부를 감싸주므로 PHP 7 같이 큰 변화가 있어도 기존 extension을 크게 수정하지 않고도 새로운 PHP 엔진에서 사용할 수 있는 장점이 있습니다.


제작사는 Copernica 라는 회사로 PHP-CPP 와 여러 개의 오픈소스를 지원하고 있으며 오픈소스에 대해서 기술 지원을 비즈니스 모델로 가져가고 있는 것 같습니다.

PHP-CPP 를 지속적으로 유지하려면 수익 모델이 당연히 필요하고 상용 기술 지원은 많은 회사가 채택하는 모델이지만 PHP extension 을 만들 필요가 있는 회사는 크게 많지 않을듯 하여 프로젝트가 지속적으로 유지될수 있을까 하는 우려가 있기는 합니다.


빌드

github 에서 소스를 내려 받아서 빌드하여 사용하면 됩니다. 빌드를 하기 위해서는 C++ 11을 지원하는 4.7 이상 버전의 g++ 컴파일러가 필요합니다. (CentOS 6 은 안 됩니다.)

  1. git clone

    git clone https://github.com/CopernicaMarketingSoftware/PHP-CPP
  2. 소스 폴더로 이동합니다.

    cd PHP-CPP
  3. 현재 master 브랜치에 있는 소스는 PHP 7.2 을 지원하며 tag 는 2.1.0 입니다. 만약 PHP 5.x 를 사용한다면 v1.5.3 을 체크아웃하고 PHP 7 을 사용한다면 최종 태그를 사용하면 됩니다.

    PHP 5.x 사용시
    git checkout -b v1.5.3
    git checkout -b v2.1.0
  4. 빌드해서 라이브러리를 생성합니다. master 브랜치일 경우 libphpcpp.so.2.0.0 가 생성되며 v1.5.3 브랜치일 경우 libphpcpp.so.1.5.3 이 생성됩니다.

    make
  5. root 로 설치합니다.

    make install


사용

모듈을 만드려는 프로젝트는 PHP-CPP 의 include 폴더에 있는 헤더를 포함해서 빌드하고 링크 단계에서 phpcpp 를 링크해 주면 됩니다.

즉 Makefile 에 다음과 같은 정보를 추가하면 됩니다.

CPP             = g++
RM              = rm -f
CPP_FLAGS       = -Wall -c -I. -O2 -std=c++11 -Ilibsass/include -IPHP-CPP
LD              = g++
LD_FLAGS        = -Wall -shared -O2

Mac OS X 사용자는 https://github.com/CopernicaMarketingSoftware/PHP-CPP/issues/39 를 참고하여 Makefile 에 다음 내용 추가하면 정상적으로 빌드할 수 있습니다.

-undefined dynamic_lookup


현재 PHP-CPP 에서는 PHP 의 모듈 정보를 출력(phpinfo() 호출시)하는 PHP_MINFO_FUNCTION 메서드는 지원 안 하지만 variables, arrays, functions, objects, classes, interfaces, exception, namespace 등 대부분의 PHP 의 특성을 지원합니다.

 Click here to expand...
PHP_MINFO_FUNCTION 사용 예제
static PHP_MINFO_FUNCTION(bz2);
 
 
static PHP_MINFO_FUNCTION(bz2)
{
    php_info_print_table_start();
    php_info_print_table_row(2, "BZip2 Support", "Enabled");
    php_info_print_table_row(2, "Stream Wrapper support", "compress.bzip2://");
    php_info_print_table_row(2, "Stream Filter support", "bzip2.decompress, bzip2.compress");
    php_info_print_table_row(2, "BZip2 Version", (char *) BZ2_bzlibVersion());
    php_info_print_table_end();
}
모듈 정보는 다음 구조체
zend_module_entry bz2_module_entry = {
    STANDARD_MODULE_HEADER,
    "bz2",
    bz2_functions,
    PHP_MINIT(bz2),
    PHP_MSHUTDOWN(bz2),
    NULL,
    NULL,
    PHP_MINFO(bz2),
    NO_VERSION_YET,
    STANDARD_MODULE_PROPERTIES
};


PHP-CPP 클래스의 생성자는 모듈 정보 함수를 입력받지 않으므로 사용 불가

./zend/extensionimpl.cpp
ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion) :
    ExtensionBase(data)
{
 
	// ...
	_entry.info_func = NULL;                                       // information for retrieving info
}


예제 프로젝트

php-sass 는 제가 libsass 를 PHP-CPP 를 사용하여 extension 으로 만들어 본 프로젝트로 PHP 5.x 와 PHP 7 을 지원합니다.

그리고 중국에서 만든 hprose-php 라는 PHP-CPP 로 만든 extension 도 있습니다.

같이 보기