├── rust ├── Cargo.toml ├── Cargo.lock └── src │ └── lib.rs ├── php-ext ├── php_score.h ├── score.c └── config.m4 ├── Dockerfile ├── .gitignore └── README.md /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "score" 3 | version = "0.0.1" 4 | description = "Example Rust library." 5 | license = "MIT" 6 | authors = [ "herman@hermanradtke.com" ] 7 | 8 | [lib] 9 | name = "score" 10 | crate-type = ["dylib"] 11 | 12 | [dependencies] 13 | libc = "0.1.7" 14 | -------------------------------------------------------------------------------- /rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "score" 3 | version = "0.0.1" 4 | dependencies = [ 5 | "libc 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "libc" 10 | version = "0.1.7" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | -------------------------------------------------------------------------------- /php-ext/php_score.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_SCORE_H 2 | 3 | #define PHP_SCORE_H 4 | 5 | #define PHP_SCORE_EXTNAME "score" 6 | #define PHP_SCORE_EXTVER "1.0" 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include "config.h" 10 | #endif 11 | 12 | #include "php.h" 13 | 14 | extern zend_module_entry score_module_entry; 15 | #define phpext_score_ptr &score_module_entry 16 | 17 | extern double ext_score(unsigned char *, unsigned int, unsigned char *, unsigned int); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | use libc::c_uchar; 4 | 5 | #[no_mangle] 6 | /// External interface for the scoring algorithm 7 | pub extern "C" fn ext_score(choice: *const c_uchar, choice_len: u32, query: *const c_uchar, query_len: u32) -> f64 { 8 | let c = unsafe { 9 | std::str::from_utf8(std::slice::from_raw_parts(choice, choice_len as usize)).unwrap() 10 | }; 11 | 12 | let q = unsafe { 13 | std::str::from_utf8(std::slice::from_raw_parts(query, query_len as usize)).unwrap() 14 | }; 15 | 16 | score(c, q) 17 | } 18 | 19 | pub fn score(_choice: &str, _query: &str) -> f64 { 20 | 42.0 21 | } 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:5.6-cli 2 | 3 | MAINTAINER Herman J. Radtke III 4 | 5 | RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates curl git libssl-dev 6 | RUN curl -sL https://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz | tar xvz -C /tmp && /tmp/rust-nightly-x86_64-unknown-linux-gnu/install.sh 7 | 8 | RUN apt-get autoclean && apt-get clean 9 | RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 10 | 11 | ONBUILD COPY . /source 12 | ONBUILD RUN cd rust && cargo build 13 | ONBUILD RUN cd php-ext && phpize && ./configure --with-score=../rust/target/debug && make 14 | 15 | VOLUME ["/source"] 16 | WORKDIR /source 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rust/target 2 | php-ext/.deps 3 | php-ext/.libs/ 4 | php-ext/Makefile 5 | php-ext/Makefile.fragments 6 | php-ext/Makefile.global 7 | php-ext/Makefile.objects 8 | php-ext/acinclude.m4 9 | php-ext/aclocal.m4 10 | php-ext/autom4te.cache/ 11 | php-ext/backup/ 12 | php-ext/build/ 13 | php-ext/config.guess 14 | php-ext/config.h 15 | php-ext/config.h.in 16 | php-ext/config.h.in~ 17 | php-ext/config.log 18 | php-ext/config.nice 19 | php-ext/config.status 20 | php-ext/config.sub 21 | php-ext/configure 22 | php-ext/configure.in 23 | php-ext/install-sh 24 | php-ext/libtool 25 | php-ext/ltmain.sh 26 | php-ext/missing 27 | php-ext/mkinstalldirs 28 | php-ext/modules/ 29 | php-ext/run-tests.php 30 | php-ext/score.la 31 | php-ext/score.lo 32 | -------------------------------------------------------------------------------- /php-ext/score.c: -------------------------------------------------------------------------------- 1 | #include "php_score.h" 2 | 3 | PHP_FUNCTION(score) 4 | { 5 | char *choice; 6 | int choice_len; 7 | char *query; 8 | int query_len; 9 | 10 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &choice, &choice_len, &query, &query_len) == FAILURE) { 11 | return; 12 | } 13 | 14 | double s = ext_score(choice, choice_len, query, query_len); 15 | 16 | RETURN_DOUBLE(s); 17 | } 18 | 19 | ZEND_BEGIN_ARG_INFO_EX(arginfo_score, 0, 0, 1) 20 | ZEND_ARG_INFO(0, choice) 21 | ZEND_ARG_INFO(0, query) 22 | ZEND_END_ARG_INFO() 23 | 24 | zend_function_entry php_score_functions[] = { 25 | PHP_FE(score, arginfo_score) 26 | {NULL, NULL, NULL} /* must be the last line */ 27 | }; 28 | 29 | zend_module_entry score_module_entry = { 30 | STANDARD_MODULE_HEADER, 31 | PHP_SCORE_EXTNAME, 32 | php_score_functions, 33 | NULL, /* minit */ 34 | NULL, /* mshutdown */ 35 | NULL, /* rinit */ 36 | NULL, /* rshutdown */ 37 | NULL, /* minfo */ 38 | PHP_SCORE_EXTVER, 39 | STANDARD_MODULE_PROPERTIES 40 | }; 41 | 42 | #ifdef COMPILE_DL_SCORE 43 | ZEND_GET_MODULE(score) 44 | #endif 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust and PHP Extension Example 2 | 3 | 4 | ## Docker Setup 5 | 6 | There is a docker container that is fully setup here: https://registry.hub.docker.com/u/hjr3/rust-php-ext/ 7 | 8 | To see this in action, simply run: `docker run -it -v $(pwd):/source hjr3/rust-php-ext php -d extension=/source/php-ext/modules/score.so -r "var_dump(score('vim', 'vi'));"` 9 | 10 | ## Manual Setup 11 | 12 | ### Rust 13 | 14 | To build the Rust library: 15 | 16 | ``` 17 | $ cd rust 18 | $ cargo build 19 | ``` 20 | 21 | ### PHP 22 | 23 | To build the PHP extension: 24 | 25 | ``` 26 | $ cd php-ext 27 | $ phpize 28 | $ ./configure --with-score=../rust/target/debug 29 | $ make 30 | ``` 31 | 32 | You can optionally `make install` if you want to install the PHP extension shared object. 33 | 34 | There are other ways to configure the PHP extension. My [blog post][blog post] has more details. 35 | 36 | ### Run Example 37 | 38 | Once the Rust library is built and the PHP extension is compiled, you can see it in action using: 39 | 40 | ``` 41 | $ php -d extension=php-ext/modules/score.so -r "var_dump(score('vim', 'vi'));" 42 | ``` 43 | 44 | The `-d` option tells PHP that we are passing an ini option to look for the `score.so` shared object at the specified path. The `-r` option tells PHP to run the code within quotes. 45 | 46 | [blog post]: http://hermanradtke.com/2015/08/03/creating-a-php-extension-to-rust.html 47 | -------------------------------------------------------------------------------- /php-ext/config.m4: -------------------------------------------------------------------------------- 1 | CFLAGS="$CFLAGS -Wall" 2 | 3 | PHP_ARG_WITH(score, 4 | [whether to enable the "score" extension], 5 | [ --enable-score Enable "score" extension support]) 6 | 7 | if test "$PHP_SCORE" != "no"; then 8 | dnl SCORE_LIB_DIR=/usr/local/lib 9 | 10 | SEARCH_PATH="/usr/local /usr" 11 | SEARCH_FOR="/libscore.so" 12 | if test -r $PHP_SCORE/$SEARCH_FOR; then # path given as parameter 13 | SCORE_LIB_DIR=$PHP_SCORE 14 | else # search default path list 15 | AC_MSG_CHECKING([for pecl-example files in default path]) 16 | for i in $SEARCH_PATH ; do 17 | if test -r $i/lib/$SEARCH_FOR; then 18 | SCORE_LIB_DIR=$i 19 | AC_MSG_RESULT(found in $i) 20 | fi 21 | done 22 | fi 23 | 24 | if test -z "$SCORE_LIB_DIR"; then 25 | AC_MSG_RESULT([not found]) 26 | AC_MSG_ERROR([Please reinstall the score rust library]) 27 | fi 28 | 29 | PHP_ADD_INCLUDE(./ffi.h) 30 | 31 | PHP_CHECK_LIBRARY(score, ext_score, 32 | [ 33 | PHP_ADD_LIBRARY_WITH_PATH(score, $SCORE_LIB_DIR, SCORE_SHARED_LIBADD) 34 | AC_DEFINE(HAVE_SCORE, 1, [whether you have score]) 35 | ],[ 36 | AC_MSG_ERROR([ext_score function not found in libscore]) 37 | ],[ 38 | -L$SCORE_LIB_DIR -R$SCORE_LIB_DIR 39 | ]) 40 | 41 | PHP_SUBST(SCORE_SHARED_LIBADD) 42 | PHP_NEW_EXTENSION(score, score.c, $ext_shared) 43 | fi 44 | --------------------------------------------------------------------------------