はじめに
どうもはじめまして、異世転生者のジツム・ミケイケンと申します。
ある日交差点で車に轢かれてしまった俺はエスイーエス?というよくわからない世界に飛ばされてしまった。
右も左もわからない俺だが、ここでの生活の為にHello Worldくらいは書けるようになっておこうと思う。
本記事の成果物 (コピペ用)
短い時間で解決したい方に配慮し、本記事で作成するプログラムの成果物を先に開示しておきます。 (省エネ)
<?php
hello_world("Да не люблю я Кудзэ-куна!");
本編
ここからは、上記コードを実行できるように手順を書いていきます。
記事をスクロールしながらコピペさえすれば動作するよう書いていく為、手元で動かしたい方はやってみて下さい。
あと、最近筆者が
にハマってしまった影響でサンプルコードにも影響が出ていますがご容赦下さい。
記事を読んでいくと前述のmain.phpのロシア語で何を言っているかが判明します。
(え?どうでもいいって?)
環境
- M1 Mac
- Docker Desktop
- OS: debian 12
※これじゃないと動かないというよりも、筆者はこれで動きました的なやつです。
作成予定のファイル構成
ふんわりと、こんな感じのファイル作成するんだな〜と思いながら読み飛ばして下さい。
.
├── Dockerfile
├── Makefile
├── alya.cpp
├── alya.ini
└── main.php
まずはDockerをInstallしましょう
以下のリンクからDockerをダウンロードできます。
環境によってはDockerは不要ですが、筆者はホストマシンでうまく動かなかったので利用しています。
DockerとはMacの中で別の異世界を作るようなものであるとイメージして下さい。
実行環境の立ち上げ
PHPのinstallが面倒くさいので、もともとPHPが入っているDocker imageを利用します。
FROM php:bookworm
WORKDIR /app
RUN alias gs='git status -s' ## 仮想環境の中で打つの辛いので
RUN apt update
RUN apt install -y git
RUN apt install -y vim ## lsp使えないのが辛い...
RUN apt install -y clang-format ## それでもformatはしたい
RUN git clone https://github.com/CopernicaMarketingSoftware/PHP-CPP.git /tmp/PHP-CPP
RUN cd /tmp/PHP-CPP && \
make && \
make install
解説
PHPにはhello_worldという関数は存在しない為、関数自体を自作をしています。
## 具体的にはここから下の行です。
RUN git clone https://github.com/CopernicaMarketingSoftware/PHP-CPP.git /tmp/PHP-CPP
急に出てきたリポジトリですが、紹介します。
CopernicaMarketingSoftware/PHP-CPPとはC++でPHP extensionがコンパイルできるOSSで、本記事はこちらのライブラリがあって成立しています。
Extensionを作成するには他にもPHP FFIなどいくつかの方法があります。
- Zend Extension (ネイティブ実行)
- PHP Extension (ネイティブ実行)
- PHP FFI (ネイティブ+Zend VM実行)
- PHP Library (Zend VM実行)
Dockerfile作成後コンテナを立ち上げていく
## hogeという名前のImage作成
docker build -t hoge .
## fooという名前のコンテナを立ち上げ
docker run -itd -v $PWD:/app --name foo hoge
仮想環境の中に入る方法
docker exec -it foo bash
作成した環境の確認
あまり重要じゃないですが、環境差異でエラーになった方向けです。
## アーキテクチャ
arch
> aarch64
## OS
cat /etc/os-release
> PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
> NAME="Debian GNU/Linux"
> VERSION_ID="12"
> VERSION="12 (bookworm)"
> VERSION_CODENAME=bookworm
> ID=debian
> HOME_URL="https://www.debian.org/"
> SUPPORT_URL="https://www.debian.org/support"
> BUG_REPORT_URL="https://bugs.debian.org/"
## PHP
php -v
> PHP 8.3.9 (cli) (built: Jul 23 2024 06:02:10) (NTS)
> Copyright (c) The PHP Group
> Zend Engine v4.3.9, Copyright (c) Zend Technologies
## C++
c++ -v
> Using built-in specs.
> COLLECT_GCC=c++
> COLLECT_LTO_WRAPPER=/usr/lib/gcc/aarch64-linux-gnu/12/lto-wrapper
> Target: aarch64-linux-gnu
> Thread model: posix
> Supported LTO compression algorithms: zlib zstd
> gcc version 12.2.0 (Debian 12.2.0-14)
コンパイルに必要なファイルを揃える
Makefile
公式ドキュメントにMakefileを作ってくれとあるので作成しちゃいます。
ファイルの中で変更可能箇所にコメントしてます。
CPP = g++
RM = rm -f
CPP_FLAGS = -Wall -c -I. -O2 -std=c++11
PHP_CONFIG = $(shell which php-config)
LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir)
PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir)
LD = g++
LD_FLAGS = -Wall -shared -O2
RESULT = alya.so ## ここをお好みで変えても大丈夫
PHPINIFILE = alya.ini ## ここをお好みで変えても大丈夫
SOURCES = $(wildcard *.cpp)
OBJECTS = $(SOURCES:%.cpp=%.o)
all: ${OBJECTS} ${RESULT}
${RESULT}: ${OBJECTS}
${LD} ${LD_FLAGS} -o $@ ${OBJECTS} -lphpcpp
clean: ##
${RM} *.obj *~* ${OBJECTS} ${RESULT}
${OBJECTS}:
${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp}
## PHP Extension Dirへcp
install:
cp -f ${RESULT} ${LIBRARY_DIR}/
cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/
## PHP Extension Dirからrm
uninstall:
rm ${LIBRARY_DIR}/${RESULT}
rm ${PHP_CONFIG_DIR}/${PHPINIFILE}
alya.ini (php.ini)
PHPがロードする為のconfigファイルの作成を行います。
PHPINIFILEで定義しているので、makeで読み込まれます。
; 時々ボソッとロシア語でデレる隣のアーリャさん
extension=alya.so
C++コード
以下のC++コードで、hello_world関数を定義しつつextern "C"
でCからC++を利用可能にしています。
公式Docにもありますが拡張機能は必ずget_moduleシンボルを提供する必要があります。
PHP extensions must export a get_module symbol.
あ、あたし拡張機能の名称を「tun_dere(ツンデレ)」になんか、してないんだからね!! ///
#include <iostream>
#include <phpcpp.h>
using namespace std;
Php::Value hello_world(Php::Parameters ¶ms) {
string v = params[0].stringValue();
cout << v + " (久世君なんか好きじゃないもん)" << endl;
return 0;
}
extern "C" {
PHPCPP_EXPORT void *get_module() {
static Php::Extension extension("tun_dere", "1.0");
extension.add<hello_world>("hello_world",
{
Php::ByVal("string", Php::Type::String),
});
return extension.module();
}
}
ファイルが揃ったのでビルドしていく
Build
書いたC++(C)コードをPHPで利用可能にするため、.soファイル(PHP拡張機能)を生成します。
make
> g++ -Wall -c -I. -O2 -std=c++11 -fpic -o alya.o alya.cpp
> g++ -Wall -shared -O2 -o alya.so alya.o -lphpcpp
-o alya.so alya.o
となっているので、soファイルが作成されていそうです。
/php/extensionsや/php/conf.dに各種ファイル設置
make install
> cp -f alya.so /usr/local/lib/php/extensions/no-debug-non-zts-20230831/
> cp -f alya.ini /usr/local/etc/php/conf.d/
自作したPHP拡張機能がinstallされているか確認
php -m | grep -2 tun_dere
> standard
> tokenizer
> tun_dere ## <=== installされている!
> xml
> xmlreader
おまけ (デバッグしたい場合)
make clean && make && make install
### 以下コマンドを打ったのと同じ
## make clean
## make
## make uninstall
## make install
さあ「Hello World」をしてみよう! (ついにタイトル回収)
main.phpというファイルを以下の内容で作成します。
<?php
## ※アーリャはロシア語でデレます
hello_world("Да не люблю я Кудзэ-куна!");
作成したファイルを実行してみます。
## PHP 実行
php main.php
実行結果
本当は俺のこと好きなくせにってやつですね(^o^)♪ <= ちょっとキモい
まとめ
俺は無事に「ケイケンネンスウ・サンネンイジョウ」を手にして、異世界のんびりスローライフ生活を送れる日は来るのだろうか ...
次回予告: 受託開発国、闇の見積もり村編
※ 本記事はフィクションです。