ext_skel
utility. It allows you to create a blank for a new expansion. We will edit the code that came out after executing this command./ext
source code PHP and from there runext_skel
with the name of the new extension: ./ext_skel --extname=phpcv
tests
folder and the files config.m4
, php_phpcv.h
and phpcv.c
. The phpcv.c
file phpcv.c
immediately renamed to phpcv.cpp
.phpize
utility to prepare our extension for compiling.acinclude.m4
and aclocal.m4
in php source code, and are written in the language of parentheses and punctuation marks. In fact, it is enough to read comments that start with the lines “dnl” and it will be more or less clear what these macros do. PHP_ARG_ENABLE(phpcv, whether to enable phpcv support, [ --enable-phpcv Enable phpcv support]) if test "$PHP_PHPCV" != "no"; then PHP_REQUIRE_CXX() SEARCH_PATH="/usr/local /usr /opt/local" SEARCH_FOR="/include/opencv2/opencv.hpp" if test -r $PHP_PHPCV/$SEARCH_FOR; then CV_DIR=$PHP_PHPCV else AC_MSG_CHECKING([for opencv in default path]) for i in $SEARCH_PATH ; do if test -r $i/$SEARCH_FOR; then CV_DIR=$i AC_MSG_RESULT(found in $i) break fi done fi if test -z "$CV_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Please reinstall the OpenCV distribution]) fi AC_CHECK_HEADER([$CV_DIR/include/opencv2/objdetect/objdetect.hpp], [], AC_MSG_ERROR('opencv2/objdetect/objdetect.hpp' header not found)) AC_CHECK_HEADER([$CV_DIR/include/opencv2/highgui/highgui.hpp], [], AC_MSG_ERROR('opencv2/highgui/highgui.hpp' header not found)) PHP_ADD_LIBRARY_WITH_PATH(opencv_objdetect, $CV_DIR/lib, PHPCV_SHARED_LIBADD) PHP_ADD_LIBRARY_WITH_PATH(opencv_highgui, $CV_DIR/lib, PHPCV_SHARED_LIBADD) PHP_ADD_LIBRARY_WITH_PATH(opencv_imgproc, $CV_DIR/lib, PHPCV_SHARED_LIBADD) PHP_SUBST(PHPCV_SHARED_LIBADD) PHP_NEW_EXTENSION(phpcv, phpcv.cpp, $ext_shared,, -std=c++0x -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi
#ifndef PHP_PHPCV_H #define PHP_PHPCV_H #define PHP_PHPCV_EXTNAME "phpcv" #define PHP_PHPCV_VERSION "0.2.0" #ifdef HAVE_CONFIG_H #include "config.h" #endif extern "C" { #include "php.h" #include "ext/standard/info.h" } #ifdef ZTS #include "TSRM.h" #endif extern zend_module_entry phpcv_module_entry; #define phpext_phpcv_ptr &phpcv_module_entry #if defined(ZTS) && defined(COMPILE_DL_PHPCV) ZEND_TSRMLS_CACHE_EXTERN(); #endif #endif /* PHP_PHPCV_H */
extern "C" { ... }
/** * @see cv::CascadeClassifier::detectMultiScale() * @param string $imgPath * @param string $cascadePath * @param double $scaleFactor * @param int $minNeighbors * * @return array */ function cv_detect_multiscale($imgPath, $cascadePath, $scaleFactor, $minNeighbors) { }
#include "php_phpcv.h" #include <opencv2/objdetect/objdetect.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> PHP_MINFO_FUNCTION(phpcv) { php_info_print_table_start(); php_info_print_table_header(2, "phpcv support", "enabled"); php_info_print_table_end(); } PHP_FUNCTION(cv_detect_multiscale) { char *imgPath = NULL, *cascadePath = NULL; long imgPathLen, cascadePathLen, minNeighbors; double scaleFactor, minWidth, minHeight; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssdl", &imgPath, &imgPathLen, &cascadePath, &cascadePathLen, &scaleFactor, &minNeighbors) == FAILURE) { RETURN_FALSE; } // Read Image cv::Mat image; image = cv::imread(imgPath, CV_LOAD_IMAGE_GRAYSCALE); if (image.empty()) { RETURN_FALSE; } equalizeHist(image, image); //min size for detected object, discarding objects smaller than this minWidth = image.size().width / 10; minHeight = image.size().height / 10; // Load Face cascade (.xml file) cv::CascadeClassifier faceCascade; if (!faceCascade.load(cascadePath)) { RETURN_FALSE; } // Detect faces std::vector<cv::Rect> faces; faceCascade.detectMultiScale(image, faces, scaleFactor, minNeighbors, 0, cv::Size(minWidth, minHeight)); array_init(return_value); // Build array to return for ( int i = 0; i < faces.size(); i++ ) { // Now we have: faces[i].x faces[i].y faces[i].width faces[i].height zval face; array_init(&face); add_assoc_long(&face, "x", faces[i].x); add_assoc_long(&face, "y", faces[i].y); add_assoc_long(&face, "w", faces[i].width); add_assoc_long(&face, "h", faces[i].height); add_next_index_zval(return_value, &face); } } const zend_function_entry phpcv_functions[] = { PHP_FE(cv_detect_multiscale, NULL) PHP_FE_END }; zend_module_entry phpcv_module_entry = { STANDARD_MODULE_HEADER, PHP_PHPCV_EXTNAME, phpcv_functions, NULL, NULL, NULL, NULL, PHP_MINFO(phpcv), PHP_PHPCV_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_PHPCV #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE(); #endif ZEND_GET_MODULE(phpcv) #endif
zend_parse_parameters
C ++ code is in it. Using the opencv library, we find faces and form the output array with the coordinates of the faces found.NULL
in a row are instead of methods that are executed during initialization and shutdown extensions and queries, we simply have nothing to do during these phases.tests
--TEST-- Test face detection --SKIPIF-- <?php if (!extension_loaded("phpcv")) print "skip"; ?> --FILE-- <?php $cascade = '/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml'; $img = __DIR__ . '/img.jpg'; $faces = cv_detect_multiscale($img, $cascade, 1.1, 5); if (is_array($faces) && count($faces) > 0) { echo 'face detection works'; } ?> --EXPECT-- face detection works
phpize && ./configure && make
make test
PHP_ARG_ENABLE(intl-dtpg, whether to enable intl-dtpg support, [ --enable-intl-dtpg Enable intl-dtpg support]) if test "$PHP_INTL_DTPG" != "no"; then PHP_SETUP_ICU(INTL_DTPG_SHARED_LIBADD) PHP_SUBST(INTL_DTPG_SHARED_LIBADD) PHP_REQUIRE_CXX() PHP_NEW_EXTENSION(intl_dtpg, intl_dtpg.cpp, $ext_shared,,-std=c++0x $ICU_INCS -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi
$ICU_INCS
- ICU specific flags. #ifndef INTL_DTPG_H #define INTL_DTPG_H #include <zend_modules.h> #include <zend_types.h> #include <unicode/dtptngen.h> extern "C" { #include "php.h" #include "ext/standard/info.h" } extern zend_module_entry intl_dtpg_module_entry; #define phpext_intl_dtpg_ptr &intl_dtpg_module_entry #define INTL_DTPG_VERSION "1.0.0" #ifdef ZTS #include "TSRM.h" #endif typedef struct { DateTimePatternGenerator *dtpg; UErrorCode status; zend_object zo; } IntlDateTimePatternGenerator_object; static inline IntlDateTimePatternGenerator_object *php_intl_datetimepatterngenerator_fetch_object(zend_object *obj) { return (IntlDateTimePatternGenerator_object *)((char*)(obj) - XtOffsetOf(IntlDateTimePatternGenerator_object, zo)); } #if defined(ZTS) && defined(COMPILE_DL_INTL_DTPG) ZEND_TSRMLS_CACHE_EXTERN() #endif #endif /* INTL_DTPG_H */
IntlDateTimePatternGenerator_object
. It stores a pointer to a DateTimePatternGenerator
object from the ICU library, a variable to store the status, and a zend_object
object, which is a PHP class. This is such a wrapper for the C ++ class. The PHP API operates with a zend_object
object, and we wrapped it in a structure and always have access to what is “next door” to zend_object
.inline
function for extracting the IntlDateTimePatternGenerator_object
structure with a zend_object
object. Under the hood, we take a step back from the start of the zend_object
to the size of our structure minus the size of the zend_object
. Thus, we find ourselves at the beginning of the structure, the pointer to which we are returned. Such a clever way is peeped in the source codes of the intl extension. class IntlDateTimePatternGenerator { /** * @param string $locale */ public function __construct(string $locale) {} /** * Return the best pattern matching the input skeleton. * It is guaranteed to have all of the fields in the skeleton. * * @param string $skeleton The skeleton is a pattern containing only the variable fields. * For example, "MMMdd" and "mmhh" are skeletons. * @return string The best pattern found from the given skeleton. */ public function findBestPattern(string $skeleton) {} }
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "intl_dtpg.h" #include <unicode/ustdio.h> #include <unicode/smpdtfmt.h> zend_class_entry *IntlDateTimePatternGenerator_ce; zend_object_handlers IntlDateTimePatternGenerator_object_handlers; /* {{{ IntlDateTimePatternGenerator_objects_dtor */ static void IntlDateTimePatternGenerator_object_dtor(zend_object *object) { zend_objects_destroy_object(object); } /* }}} */ /* {{{ IntlDateTimePatternGenerator_objects_free */ void IntlDateTimePatternGenerator_object_free(zend_object *object) { IntlDateTimePatternGenerator_object *dtpgo = php_intl_datetimepatterngenerator_fetch_object(object); zend_object_std_dtor(&dtpgo->zo); dtpgo->status = U_ZERO_ERROR; if (dtpgo->dtpg) { delete dtpgo->dtpg; dtpgo->dtpg = nullptr; } } /* }}} */ /* {{{ IntlDateTimePatternGenerator_object_create */ zend_object *IntlDateTimePatternGenerator_object_create(zend_class_entry *ce) { IntlDateTimePatternGenerator_object* intern; intern = (IntlDateTimePatternGenerator_object*)ecalloc(1, sizeof(IntlDateTimePatternGenerator_object) + zend_object_properties_size(ce)); zend_object_std_init(&intern->zo, ce); object_properties_init(&intern->zo, ce); intern->dtpg = nullptr; intern->status = U_ZERO_ERROR; intern->zo.handlers = &IntlDateTimePatternGenerator_object_handlers; return &intern->zo; } /* }}} */ /* {{{ proto void IntlDateTimePatternGenerator::__construct(string $locale) * IntlDateTimePatternGenerator object constructor. */ PHP_METHOD(IntlDateTimePatternGenerator, __construct) { zend_string *locale; zval *object; IntlDateTimePatternGenerator_object* dtpg = nullptr; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &locale) == FAILURE) { return; } object = getThis(); dtpg = php_intl_datetimepatterngenerator_fetch_object(Z_OBJ_P(object)); dtpg->status = U_ZERO_ERROR; dtpg->dtpg = DateTimePatternGenerator::createInstance(Locale(ZSTR_VAL(locale)), dtpg->status); } /* }}} */ /* {{{ proto string IntlDateTimePatternGenerator::findBestPattern(string $skeleton) * Return the best pattern matching the input skeleton. */ PHP_METHOD(IntlDateTimePatternGenerator, findBestPattern) { zend_string *skeleton; zval *object; IntlDateTimePatternGenerator_object* dtpg = nullptr; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &skeleton) == FAILURE) { return; } object = getThis(); dtpg = php_intl_datetimepatterngenerator_fetch_object(Z_OBJ_P(object)); UnicodeString pattern = dtpg->dtpg->getBestPattern(UnicodeString(ZSTR_VAL(skeleton)), dtpg->status); std::string s; pattern.toUTF8String(s); RETURN_STRING(s.c_str()); } /* }}} */ ZEND_BEGIN_ARG_INFO_EX(arginfo_findBestPattern, 0, 0, 1) ZEND_ARG_INFO(0, skeleton) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 1) ZEND_ARG_INFO(0, locale) ZEND_END_ARG_INFO() const zend_function_entry IntlDateTimePatternGenerator_functions[] = { PHP_ME(IntlDateTimePatternGenerator, __construct, arginfo___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) PHP_ME(IntlDateTimePatternGenerator, findBestPattern, arginfo_findBestPattern, ZEND_ACC_PUBLIC) PHP_FE_END }; /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(intl_dtpg) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "IntlDateTimePatternGenerator", IntlDateTimePatternGenerator_functions); ce.create_object = IntlDateTimePatternGenerator_object_create; IntlDateTimePatternGenerator_ce = zend_register_internal_class(&ce); memcpy(&IntlDateTimePatternGenerator_object_handlers, zend_get_std_object_handlers(), sizeof IntlDateTimePatternGenerator_object_handlers); IntlDateTimePatternGenerator_object_handlers.offset = XtOffsetOf(IntlDateTimePatternGenerator_object, zo); IntlDateTimePatternGenerator_object_handlers.clone_obj = NULL; //no clone support IntlDateTimePatternGenerator_object_handlers.dtor_obj = IntlDateTimePatternGenerator_object_dtor; IntlDateTimePatternGenerator_object_handlers.free_obj = IntlDateTimePatternGenerator_object_free; if(!IntlDateTimePatternGenerator_ce) { zend_error(E_ERROR, "Failed to register IntlDateTimePatternGenerator class"); return FAILURE; } return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(intl_dtpg) { return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(intl_dtpg) { php_info_print_table_start(); php_info_print_table_header(2, "intl_dtpg support", "enabled"); php_info_print_table_header(2, "intl_dtpg version", INTL_DTPG_VERSION); php_info_print_table_end(); } /* }}} */ /* {{{ intl_dtpg_functions[] */ const zend_function_entry intl_dtpg_functions[] = { PHP_FE_END }; /* }}} */ /* {{{ intl_dtpg_module_entry */ zend_module_entry intl_dtpg_module_entry = { STANDARD_MODULE_HEADER, "intl_dtpg", intl_dtpg_functions, PHP_MINIT(intl_dtpg), PHP_MSHUTDOWN(intl_dtpg), NULL, NULL, PHP_MINFO(intl_dtpg), INTL_DTPG_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_INTL_DTPG #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(intl_dtpg) #endif
zend_object
our structure, call the destructor for zend_object
, reset the status and destroy the C ++ class object.DateTimePatternGenerator
object is created.zend_string
object, while a large “S” is indicated in zend_parse_parameters . This is an innovation in PHP 7. But the old way of indicating a small “s” and getting a separate C-style line and its length also works, as we saw in the previous extension.zend_API.h
file, zend_API.h
can understand what they mean.SUCCESS
, but you can also not specify in the zend_module_entry , specify NULL
below.NULL
are about request, we do nothing at the moment of initialization and shutdown of the request. --TEST-- Check for intl_dtpg presence --SKIPIF-- <?php if (!extension_loaded("intl_dtpg")) print "skip"; ?> --FILE-- <?php $dtpg = new IntlDateTimePatternGenerator('ru_RU'); $ruPattern = $dtpg->findBestPattern('MMMMd'); $dtpg = new IntlDateTimePatternGenerator('en_US'); $enPattern = $dtpg->findBestPattern('MMMMd'); echo $ruPattern . ';' . $enPattern; ?> --EXPECT-- d MMMM;MMMM d
phpize && ./configure && make make test
extension=modules/intl_dtpg.so
<?php $dtpg = new IntlDateTimePatternGenerator('ru_RU'); $ruPattern = $dtpg->findBestPattern('MMMMd'); $dtpg = new IntlDateTimePatternGenerator('en_US'); $enPattern = $dtpg->findBestPattern('MMMMd'); echo $ruPattern . ';' . $enPattern . "\n";
valgrind php -c test-php.ini test.php
==30326== HEAP SUMMARY:
==30326== in use at exit: 478,022 bytes in 2,151 blocks
==30326== total heap usage: 22,863 allocs, 20,712 frees, 4,718,469 bytes allocated
==30326==
==30326== LEAK SUMMARY:
==30326== definitely lost: 0 bytes in 0 blocks
==30326== indirectly lost: 0 bytes in 0 blocks
==30326== possibly lost: 1,076 bytes in 14 blocks
==30326== still reachable: 476,946 bytes in 2,137 blocks
==30326== of which reachable via heuristic:
==30326== newarray : 30,416 bytes in 74 blocks
==30326== suppressed: 0 bytes in 0 blocks
DateTimePatternGenerator
in the IntlDateTimePatternGenerator_object_free IntlDateTimePatternGenerator_object_free
/* {{{ IntlDateTimePatternGenerator_objects_free */ void IntlDateTimePatternGenerator_object_free(zend_object *object) { IntlDateTimePatternGenerator_object *dtpgo = php_intl_datetimepatterngenerator_fetch_object(object); zend_object_std_dtor(&dtpgo->zo); dtpgo->status = U_ZERO_ERROR; // if (dtpgo->dtpg) { // delete dtpgo->dtpg; // dtpgo->dtpg = nullptr; // } } /* }}} */
valgrind php -c test-php.ini test.php
==411== HEAP SUMMARY:
==411== in use at exit: 770,710 bytes in 2,477 blocks
==411== total heap usage: 22,863 allocs, 20,386 frees, 4,718,469 bytes allocated
==411==
==411== LEAK SUMMARY:
==411== definitely lost: 5,232 bytes in 2 blocks
==411== indirectly lost: 287,456 bytes in 324 blocks
==411== possibly lost: 1,076 bytes in 14 blocks
==411== still reachable: 476,946 bytes in 2,137 blocks
==411== of which reachable via heuristic:
==411== newarray : 30,416 bytes in 74 blocks
==411== suppressed: 0 bytes in 0 blocks
Source: https://habr.com/ru/post/335478/
All Articles