├── API-1.0.0 ├── CREDITS ├── ssdeep.php ├── config.w32 ├── config.m4 ├── README ├── LICENSE ├── php_ssdeep.h ├── ax_libssdeep.m4 ├── tests └── ssdeep.phpt ├── examples └── example.php ├── ssdeep.c ├── package.xml └── .github └── workflows └── build-test-and-release.yml /API-1.0.0: -------------------------------------------------------------------------------- 1 | API release notes. 2 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | ssdeep 2 | Simon Holywell 3 | -------------------------------------------------------------------------------- /ssdeep.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('ssdeep')) { 5 | dl('ssdeep.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'ssdeep'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_WITH("ssdeep", "ssdeep support", "no"); 4 | 5 | if (PHP_SSDEEP != "no") { 6 | if (CHECK_LIB("fuzzy_a.lib;fuzzy.lib", "ssdeep", PHP_SSDEEP) && 7 | CHECK_HEADER_ADD_INCLUDE("fuzzy.h", "CFLAGS_SSDEEP") && 8 | CHECK_HEADER_ADD_INCLUDE("edit_dist.h", "CFLAGS_SSDEEP")) { 9 | EXTENSION("ssdeep", "ssdeep.c"); 10 | } else { 11 | WARNING("ssdeep not enabled; libraries and headers not found"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | PHP_ARG_WITH(ssdeep, for ssdeep support, 2 | [ --with-ssdeep[=DIR] Include ssdeep support. DIR is the optional path to the ssdeep directory.], yes) 3 | PHP_ARG_ENABLE(ssdeep-debug, whether to enable build debug output, 4 | [ --enable-ssdeep-debug ssdeep: Enable debugging during build], no, no) 5 | 6 | if test "$PHP_SSDEEP" != "no"; then 7 | withssdeep="$PHP_SSDEEP" 8 | ssdeep_enabledebug="$PHP_SSDEEP_DEBUG" 9 | dnl Pass in the library directory name specified by PHP - defaults to lib 10 | ssdeep_libdirname="$PHP_LIBDIR" 11 | 12 | dnl Include common ssdeep availability test function 13 | m4_include(ax_libssdeep.m4) 14 | AX_SSDEEP 15 | 16 | if test $SSDEEP_FOUND = "yes"; then 17 | PHP_CHECK_LIBRARY($SSDEEP_LIB_NAME, fuzzy_compare, 18 | [ 19 | dnl Add the neccessary paths (vars declared in ax_libssdeep.m4) 20 | PHP_ADD_INCLUDE($SSDEEP_INCLUDEDIR_NO_FLAG) 21 | PHP_ADD_LIBRARY_WITH_PATH($SSDEEP_LIB_NAME, $SSDEEP_LIBDIR_NO_FLAG, SSDEEP_SHARED_LIBADD) 22 | AC_DEFINE(HAVE_SSDEEP, 1, [Whether you have SSDEEP]) 23 | ],[ 24 | AC_MSG_ERROR([ssdeep lib not found. See config.log for more information.]) 25 | ],[$SSDEEP_LIBDIR] 26 | ) 27 | PHP_NEW_EXTENSION(ssdeep, ssdeep.c, $ext_shared) 28 | PHP_SUBST(SSDEEP_SHARED_LIBADD) 29 | fi 30 | else 31 | AC_MSG_RESULT([ssdeep was not enabled]) 32 | AC_MSG_ERROR([Enable ssdeep to build this extension.]) 33 | fi 34 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # ssdeep PECL extension for PHP 2 | 3 | Fuzzy hashing makes it easy to identify similar or almost identical text by simply comparing two hashes against each other. 4 | 5 | The ssdeep project page describes it as a library for "...computing context triggered piecewise hashes (CTPH). Also called fuzzy hashes, CTPH can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length". 6 | 7 | For an in depth paper explaining context triggered piecewise hashes please see http://dfrws.org/2006/proceedings/12-Kornblum.pdf 8 | 9 | ## ssdeep API Information 10 | 11 | Requires at least ssdeep v2.5. 12 | 13 | This extensions wraps the ssdeep fuzzy hashing API created by Jesse Kornblum. 14 | 15 | For more information about ssdeep, the API and context triggered piecewise hashing please see http://ssdeep.sourceforge.net/ 16 | 17 | ## ssdeep Licensing Exemption 18 | 19 | Although ssdeep and the fuzzy hashing API are usually licensed under GNU GPLv2 Jesse Kornblum has licenced them to PECL under the terms of the Perl Artistic Licence with the following declaration: 20 | "I hereby license ssdeep, the current version and all subsequent versions, under the terms of the Perl Artistic License, for inclusion into the PECL." 21 | See http://news.php.net/php.pecl.dev/7625 for the verbatim email. 22 | 23 | ## libfuzzy Windows builds 24 | 25 | Windows PECL dependencies are available from windows.php.net: https://windows.php.net/downloads/pecl/deps/ 26 | 27 | There are build sources on github: https://github.com/winlibs/ssdeep although it is unclear what the source of 2.14.1 builds on windows.php.net is because this has the latest version of ssdeep as 2.13. 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | PECL ssdeep PHP Extension Licence 2 | ------- 3 | This extension is under the BSD Licence: 4 | 5 | Copyright (c) 2018, Simon Holywell 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | 30 | ssdeep Licensing Exemption 31 | ------- 32 | 33 | Although ssdeep and the fuzzy hashing API are usually licensed under GNU GPLv2 Jesse Kornblum has licenced them to PECL under the terms of the Perl Artistic Licence with the following declaration: 34 | "I hereby license ssdeep, the current version and all subsequent versions, under the terms of the Perl Artistic License, for inclusion into the PECL." 35 | See http://news.php.net/php.pecl.dev/7625 for the verbatim email. 36 | -------------------------------------------------------------------------------- /php_ssdeep.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * php_ssdeep 4 | * 5 | * http://treffynnon.github.com/php_ssdeep/ 6 | * 7 | * A PHP extension to expose ssdeep functionality for fuzzy 8 | * hashing and comparing. 9 | * 10 | * Version 1.1.1 11 | * 12 | * BSD Licensed. 13 | * 14 | * Copyright (c) 2018, Simon Holywell 15 | * All rights reserved. 16 | * 17 | * Redistribution and use in source and binary forms, with or without 18 | * modification, are permitted provided that the following conditions are met: 19 | * 20 | * * Redistributions of source code must retain the above copyright notice, this 21 | * list of conditions and the following disclaimer. 22 | * 23 | * * Redistributions in binary form must reproduce the above copyright notice, 24 | * this list of conditions and the following disclaimer in the documentation 25 | * and/or other materials provided with the distribution. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 30 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 31 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 33 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 34 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 35 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | * 38 | */ 39 | 40 | #ifndef PHP_SSDEEP_H 41 | # define PHP_SSDEEP_H 1 42 | # define PHP_SSDEEP_VERSION "1.1.1" 43 | # define PHP_SSDEEP_EXTNAME "ssdeep" 44 | PHP_FUNCTION(ssdeep_fuzzy_hash); 45 | PHP_FUNCTION(ssdeep_fuzzy_compare); 46 | PHP_FUNCTION(ssdeep_fuzzy_hash_filename); 47 | 48 | extern zend_module_entry php_ssdeep_module_entry; 49 | # define phpext_php_ssdeep_ptr &php_ssdeep_module_entry 50 | 51 | #if PHP_MAJOR_VERSION < 7 52 | typedef int strsize_t; 53 | #define _RETURN_STRING(s) RETURN_STRING(s, 0); 54 | #else 55 | typedef size_t strsize_t; 56 | #define _RETURN_STRING(s) { RETVAL_STRING(s); efree(s); return; } 57 | #endif 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /ax_libssdeep.m4: -------------------------------------------------------------------------------- 1 | ##### 2 | # 3 | # SYNOPSIS 4 | # 5 | # AX_SSDEEP() 6 | # 7 | # DESCRIPTION 8 | # 9 | # This macro provides tests of availability for the SSDEEP library and headers. 10 | # 11 | # This macro calls: 12 | # 13 | # AC_SUBST(SSDEEP_LIBDIR) 14 | # AC_SUBST(SSDEEP_LIBS) 15 | # AC_SUBST(SSDEEP_INCLUDEDIR) 16 | # 17 | ##### 18 | AC_DEFUN([AX_SSDEEP], [ 19 | AC_MSG_CHECKING(for ssdeep) 20 | 21 | AC_CANONICAL_HOST 22 | LIB_EXTENSION="so" 23 | case $host_os in 24 | darwin*) 25 | LIB_EXTENSION="dylib" 26 | ;; 27 | esac 28 | 29 | SSDEEP_LIB_NAME="fuzzy" 30 | SSDEEP_LIB_FILENAME="lib$SSDEEP_LIB_NAME.$LIB_EXTENSION" 31 | SSDEEP_INCLUDE_FILENAME="$SSDEEP_LIB_NAME.h" 32 | 33 | if test -z "$withssdeep" -o "$withssdeep" = "yes"; then 34 | for i in "/usr/$ssdeep_libdirname" "/usr/$ssdeep_libdirname/x86_64-linux-gnu" "/usr/$ssdeep_libdirname/aarch64-linux-gnu" "/usr/local/$ssdeep_libdirname"; do 35 | if test -f "$i/$SSDEEP_LIB_FILENAME"; then 36 | SSDEEP_LIB_PATH="$i" 37 | fi 38 | done 39 | elif test "$withssdeep" != "no"; then 40 | for i in "$withssdeep" "$withssdeep/$ssdeep_libdirname" "$withssdeep/.libs"; do 41 | if test -f "$i/$SSDEEP_LIB_FILENAME"; then 42 | SSDEEP_LIB_PATH="$i" 43 | fi 44 | done 45 | else 46 | AC_MSG_ERROR(["Cannot build whilst ssdeep is disabled."]) 47 | fi 48 | 49 | if test "$SSDEEP_LIB_PATH" = ""; then 50 | AC_MSG_ERROR(["Could not find '$SSDEEP_LIB_FILENAME'. Try specifying the path to the ssdeep build directory."]) 51 | fi 52 | 53 | SSDEEP_LIBDIR="-L$SSDEEP_LIB_PATH" 54 | SSDEEP_LIBS="-l$SSDEEP_LIB_NAME" 55 | SSDEEP_LIBDIR_NO_FLAG="$SSDEEP_LIB_PATH" 56 | 57 | if test -z "$withssdeep" -o "$withssdeep" = "yes"; then 58 | for i in /usr/include /usr/local/include; do 59 | if test -f "$i/$SSDEEP_INCLUDE_FILENAME"; then 60 | SSDEEP_INCLUDEDIR="$i" 61 | fi 62 | done 63 | elif test "$withssdeep" != "no"; then 64 | for i in "$withssdeep" "$withssdeep/.." "$withssdeep/include"; do 65 | if test -f "$i/$SSDEEP_INCLUDE_FILENAME"; then 66 | SSDEEP_INCLUDEDIR="$i" 67 | fi 68 | done 69 | else 70 | AC_MSG_ERROR(["Cannot build whilst ssdeep is disabled."]) 71 | fi 72 | 73 | if test "$SSDEEP_INCLUDEDIR" = ""; then 74 | AC_MSG_ERROR(["Could not find ssdeep '$SSDEEP_INCLUDE_FILENAME' header file. Try specifying the path to the ssdeep build directory."]) 75 | fi 76 | 77 | SSDEEP_INCLUDEDIR_NO_FLAG="$SSDEEP_INCLUDEDIR" 78 | SSDEEP_INCLUDEDIR="-I$SSDEEP_INCLUDEDIR" 79 | 80 | AC_MSG_RESULT([$SSDEEP_LIBDIR, $SSDEEP_INCLUDEDIR_NO_FLAG]) 81 | 82 | AC_DEFINE([SSDEEP_ENABLED], [1], [Enables ssdeep]) 83 | 84 | SSDEEP_FOUND="yes" 85 | 86 | if test "$ssdeep_enabledebug" = "yes"; then 87 | echo " " 88 | echo " " 89 | echo " " 90 | echo "======================== Debug ==============================" 91 | echo " " 92 | echo "\$host_os : $host_os" 93 | echo "\$SSDEEP_LIB_NAME : $SSDEEP_LIB_NAME" 94 | echo "\$SSDEEP_LIB_FILENAME : $SSDEEP_LIB_FILENAME" 95 | echo "\$SSDEEP_INCLUDE_FILENAME : $SSDEEP_INCLUDE_FILENAME" 96 | echo "\$SSDEEP_INC_DIR : $SSDEEP_INCLUDEDIR" 97 | echo "\$SSDEEP_INCLUDEDIR_NO_FLAG : $SSDEEP_INCLUDEDIR_NO_FLAG" 98 | echo "\$SSDEEP_LIBDIR : $SSDEEP_LIBDIR" 99 | echo "\$SSDEEP_LIBDIR_NO_FLAG : $SSDEEP_LIBDIR_NO_FLAG" 100 | echo "\$SSDEEP_FOUND : $SSDEEP_FOUND" 101 | echo "\$withssdeep : $withssdeep" 102 | echo "\$ssdeep_enabledebug : $ssdeep_enabledebug" 103 | echo " " 104 | echo "=============================================================" 105 | echo " " 106 | echo " " 107 | echo " " 108 | fi 109 | 110 | AC_SUBST(SSDEEP_LIBDIR) 111 | AC_SUBST(SSDEEP_LIBS) 112 | AC_SUBST(SSDEEP_INCLUDEDIR) 113 | ]) 114 | -------------------------------------------------------------------------------- /tests/ssdeep.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test ssdeep fuzzy hashing functions 3 | --FILE-- 4 | 41 | --CLEAN-- 42 | 48 | --EXPECT-- 49 | string(78) "24:FPlUMKVsgNfgmjFadP6WboWjb8tsH4RSXqMbLFpjwPDt4tFF:9lUajiiPbbnr4RSXqMbppMZ4t3" 50 | string(76) "48:9lUajiiPbbnr4RSXqMbLbmo03Rcq0K/cvhQ+3/M8M5BEaB6:9HFHsGqabmoMR18hQ+308sBdk" 51 | string(100) "48:9lUajiiPbbnr4RSXqMbppMZ4twUlUajiiPbbnr4RSXqMbLbmo03Rcq0K/cvhQ+3X:9HFHsGqCM6tNHFHsGqabmoMR18hQ+30k" 52 | string(100) "48:9lUajiiPbbnr4RSXqMbLbmo03Rcq0K/cvhQ+3/M8M5BEaB1UlUajiiPbbnr4RSXX:9HFHsGqabmoMR18hQ+308sBdsHFHsGqy" 53 | int(57) 54 | int(75) 55 | int(63) 56 | -------------------------------------------------------------------------------- /examples/example.php: -------------------------------------------------------------------------------- 1 | 50 | 51 | /* For PHP 8 */ 52 | #ifndef TSRMLS_D 53 | #define TSRMLS_D void 54 | #define TSRMLS_DC 55 | #define TSRMLS_C 56 | #define TSRMLS_CC 57 | #define TSRMLS_FETCH() 58 | #endif 59 | 60 | /* True global resources - no need for thread safety here */ 61 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssdeep_fuzzy_hash, 0, 0, 1) 62 | ZEND_ARG_INFO(0, to_hash) 63 | ZEND_END_ARG_INFO() 64 | 65 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssdeep_fuzzy_hash_filename, 0, 0, 1) 66 | ZEND_ARG_INFO(0, file_name) 67 | ZEND_END_ARG_INFO() 68 | 69 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssdeep_fuzzy_compare, 0, 0, 2) 70 | ZEND_ARG_INFO(0, signature1) 71 | ZEND_ARG_INFO(0, signature2) 72 | ZEND_END_ARG_INFO() 73 | 74 | /* {{{ ssdeep_functions[] 75 | */ 76 | const zend_function_entry ssdeep_functions[] = { 77 | PHP_FE(ssdeep_fuzzy_hash, arginfo_ssdeep_fuzzy_hash) 78 | PHP_FE(ssdeep_fuzzy_hash_filename, arginfo_ssdeep_fuzzy_hash_filename) 79 | PHP_FE(ssdeep_fuzzy_compare, arginfo_ssdeep_fuzzy_compare) 80 | #ifdef PHP_FE_END 81 | PHP_FE_END 82 | #else 83 | { NULL, NULL, NULL } /* Must be the last line in ssdeep_functions[] */ 84 | #endif 85 | }; 86 | /* }}} */ 87 | 88 | /* {{{ PHP_MINFO_FUNCTION 89 | */ 90 | PHP_MINFO_FUNCTION(ssdeep) { 91 | php_info_print_table_start(); 92 | php_info_print_table_row(2, PHP_SSDEEP_EXTNAME " Module", "enabled"); 93 | php_info_print_table_row(2, "version", PHP_SSDEEP_VERSION); 94 | if (sapi_module.phpinfo_as_text) { 95 | /* No HTML for you */ 96 | php_info_print_table_row(2, "By", 97 | "Simon Holywell\nhttp://www.simonholywell.com"); 98 | } else { 99 | /* HTMLified version */ 100 | php_printf("" 101 | "By" 102 | "" 103 | "" 105 | "Simon Holywell" 106 | ""); 107 | } 108 | php_info_print_table_end(); 109 | } 110 | /* }}} */ 111 | 112 | /* {{{ proto string ssdeep_fuzzy_hash(string to_hash) 113 | */ 114 | PHP_FUNCTION(ssdeep_fuzzy_hash) { 115 | char *hash = (char *) emalloc(FUZZY_MAX_RESULT); 116 | char *to_hash; 117 | strsize_t to_hash_len; 118 | 119 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &to_hash, &to_hash_len) == FAILURE) { 120 | RETURN_NULL(); 121 | } 122 | 123 | if (0 != fuzzy_hash_buf((unsigned char *) to_hash, (uint32_t)to_hash_len, hash)) { 124 | RETURN_FALSE; 125 | } else { 126 | _RETURN_STRING(hash); 127 | } 128 | } 129 | /* }}} */ 130 | 131 | /* {{{ proto string ssdeep_fuzzy_hash_filename(string file_name) 132 | */ 133 | PHP_FUNCTION(ssdeep_fuzzy_hash_filename) { 134 | char *hash = (char *) emalloc(FUZZY_MAX_RESULT); 135 | char *file_name; 136 | strsize_t file_name_len; 137 | 138 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file_name, &file_name_len) == FAILURE) { 139 | RETURN_NULL(); 140 | } 141 | 142 | if (0 != fuzzy_hash_filename(file_name, hash)) { 143 | RETURN_FALSE; 144 | } else { 145 | _RETURN_STRING(hash); 146 | } 147 | } 148 | /* }}} */ 149 | 150 | /* {{{ proto long ssdeep_fuzzy_compare(string signature1, string signature2) 151 | */ 152 | PHP_FUNCTION(ssdeep_fuzzy_compare) { 153 | char *signature1 = NULL; 154 | strsize_t signature1_len; 155 | char *signature2 = NULL; 156 | strsize_t signature2_len; 157 | int match; 158 | 159 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &signature1, &signature1_len, &signature2, &signature2_len) == FAILURE) { 160 | RETURN_NULL(); 161 | } 162 | match = fuzzy_compare(signature1, signature2); 163 | 164 | if(match < 0 || match > 100) { 165 | RETURN_FALSE; 166 | } else { 167 | RETURN_LONG(match); 168 | } 169 | } 170 | /* }}} */ 171 | 172 | /* {{{ ssdeep_module_entry 173 | */ 174 | zend_module_entry ssdeep_module_entry = { 175 | #if ZEND_MODULE_API_NO >= 20010901 176 | STANDARD_MODULE_HEADER, 177 | #endif 178 | PHP_SSDEEP_EXTNAME, 179 | ssdeep_functions, 180 | NULL /* PHP_MINIT(ssdeep) */, 181 | NULL /* PHP_MSHUTDOWN(ssdeep) */, 182 | NULL /* PHP_RINIT(ssdeep) */, /* Replace with NULL if there's nothing to do at request start */ 183 | NULL /* PHP_RSHUTDOWN(ssdeep)*/, /* Replace with NULL if there's nothing to do at request end */ 184 | PHP_MINFO(ssdeep), 185 | #if ZEND_MODULE_API_NO >= 20010901 186 | PHP_SSDEEP_VERSION, 187 | #endif 188 | STANDARD_MODULE_PROPERTIES 189 | }; 190 | /* }}} */ 191 | 192 | #ifdef COMPILE_DL_SSDEEP 193 | ZEND_GET_MODULE(ssdeep) 194 | #endif 195 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | ssdeep 8 | pecl.php.net 9 | Fuzzy hashing makes it easy to identify similar or almost identical text by simply comparing two hashes against each other. 10 | 33 | 34 | Simon Holywell 35 | Treffynnon 36 | treffynnon@php.net 37 | yes 38 | 39 | 2024-09-27 40 | 41 | 42 | 1.1.1 43 | 1.0.0 44 | 45 | 46 | stable 47 | stable 48 | 49 | BSD 2-Clause License 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 5.2.0 80 | 81 | 82 | 1.4.0b1 83 | 84 | 85 | 86 | ssdeep 87 | 88 | 89 | 90 | 2024-09-27 91 | 92 | 93 | 1.1.1 94 | 1.0.0 95 | 96 | 97 | stable 98 | stable 99 | 100 | BSD 2-Clause License 101 | 107 | 108 | 109 | 2018-02-20 110 | 111 | 112 | 1.1.0 113 | 1.0.0 114 | 115 | 116 | stable 117 | stable 118 | 119 | BSD 2-Clause License 120 | 127 | 128 | 129 | 2014-01-22 130 | 131 | 132 | 1.0.4 133 | 1.0.0 134 | 135 | 136 | stable 137 | stable 138 | 139 | BSD 2-Clause License 140 | 150 | 151 | 152 | 2014-01-21 153 | 154 | 155 | 1.0.3 156 | 1.0.0 157 | 158 | 159 | stable 160 | stable 161 | 162 | BSD 2-Clause License 163 | 169 | 170 | 171 | 2010-11-10 172 | 173 | 174 | 1.0.2 175 | 1.0.0 176 | 177 | 178 | stable 179 | stable 180 | 181 | BSD 2-Clause License 182 | 185 | 186 | 187 | 2010-11-06 188 | 189 | 190 | 1.0.1 191 | 1.0.0 192 | 193 | 194 | stable 195 | stable 196 | 197 | BSD 2-Clause License 198 | 201 | 202 | 203 | 2010-09-15 204 | 205 | 206 | 1.0.0 207 | 1.0.0 208 | 209 | 210 | stable 211 | stable 212 | 213 | BSD 2-Clause License 214 | Initial PECL compatible release. 215 | 216 | 217 | -------------------------------------------------------------------------------- /.github/workflows/build-test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: Build, test and Release PECL Extension 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*.*.*" # Trigger on version tags 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: # Allows manual trigger 11 | 12 | jobs: 13 | check-version: 14 | runs-on: ubuntu-latest 15 | if: github.ref_type == 'tag' 16 | steps: 17 | - name: Check if tag is a valid version 18 | run: | 19 | TAG_NAME=$(echo "${GITHUB_REF#refs/tags/}") 20 | if [[ ! "$TAG_NAME" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 21 | echo "Error: Tag '$TAG_NAME' does not match semantic versioning format." 22 | exit 1 23 | fi 24 | echo "Tag '$TAG_NAME' is a valid version." 25 | 26 | build-linux: 27 | runs-on: ubuntu-latest 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | php-version: ["7.4", "8.0", "8.1", "8.2", "8.3"] 32 | outputs: 33 | latest-php-version: ${{ steps.set-latest-php-version.outputs.latest }} 34 | defaults: 35 | run: 36 | shell: bash 37 | 38 | steps: 39 | - name: Set latest PHP version 40 | id: set-latest-php-version 41 | run: echo "latest=${{ matrix.php-version }}" >> $GITHUB_ENV 42 | if: ${{ matrix.php-version == format('max', join(matrix.php-version)) }} 43 | 44 | - name: Checkout code 45 | uses: actions/checkout@v4 46 | 47 | - name: Setup PHP 48 | id: setup-php 49 | uses: shivammathur/setup-php@v2 50 | with: 51 | php-version: ${{ matrix.php-version }} 52 | 53 | - name: Install dependencies 54 | run: | 55 | sudo apt-get update 56 | sudo apt-get install -y build-essential autoconf libtool pkg-config git libfuzzy-dev 57 | 58 | - name: Build PECL Extension 59 | run: | 60 | phpize 61 | ./configure 62 | make 63 | 64 | - name: Run tests 65 | run: | 66 | make test 67 | 68 | build-windows: 69 | runs-on: ${{ matrix.os }} 70 | strategy: 71 | fail-fast: false 72 | matrix: 73 | os: [windows-2019, windows-latest] 74 | # not testing on PHP8 for windows at the moment because there is no libfuzzy dependency 75 | # compiled for it 76 | php-version: ["7.4"] #, "8.0", "8.1", "8.2", "8.3"] 77 | arch: [x64, x86] 78 | ts: [nts, ts] 79 | exclude: 80 | - { os: windows-latest, php-version: "7.4" } 81 | - { os: windows-2019, php-version: "8.0" } 82 | - { os: windows-2019, php-version: "8.1" } 83 | - { os: windows-2019, php-version: "8.2" } 84 | - { os: windows-2019, php-version: "8.3" } 85 | 86 | defaults: 87 | run: 88 | shell: powershell 89 | 90 | env: 91 | LIBFUZZY_DIR: ${{ github.workspace }}/libfuzzy 92 | 93 | steps: 94 | - name: Checkout code 95 | uses: actions/checkout@v4 96 | 97 | - name: Setup PHP 98 | id: setup-php-sdk 99 | uses: php/setup-php-sdk@v0.9 100 | with: 101 | arch: ${{ matrix.arch }} 102 | ts: ${{ matrix.ts }} 103 | version: ${{ matrix.php-version }} 104 | 105 | - name: Print PHP SDK properties 106 | run: | 107 | Write-Host "PHP SDK Toolset: ${{ steps.setup-php-sdk.outputs.toolset }}" 108 | Write-Host "PHP SDK Prefix: ${{ steps.setup-php-sdk.outputs.prefix }}" 109 | Write-Host "PHP SDK VS: ${{ steps.setup-php-sdk.outputs.vs }}" 110 | 111 | - name: Enable Developer Command Prompt 112 | uses: ilammy/msvc-dev-cmd@v1 113 | with: 114 | arch: ${{matrix.arch}} 115 | toolset: ${{steps.setup-php-sdk.outputs.toolset}} 116 | 117 | - name: Get latest ssdeep release info 118 | id: ssdeep_release 119 | env: 120 | VC_VERSION: ${{ steps.setup-php-sdk.outputs.vs }} 121 | ARCH: ${{ matrix.arch }} 122 | run: | 123 | $DOWNLOAD_URL = "https://windows.php.net/downloads/pecl/deps/" 124 | 125 | Write-Host "Looking up the latest libfuzzy (ssdeep) release for $env:ARCH architecture on $DOWNLOAD_URL" 126 | 127 | $webContent = Invoke-WebRequest -Uri $DOWNLOAD_URL 128 | $regexPattern = "libfuzzy-[0-9\.]+-$env:VC_VERSION-$env:ARCH\.zip" 129 | $matches = Select-String -InputObject $webContent.Content -Pattern $regexPattern 130 | 131 | if ($matches -eq $null) { 132 | Write-Host "Error: No libfuzzy version found for architecture: $env:ARCH and VC version: $env:VC_VERSION" 133 | exit 1 134 | } 135 | 136 | $latestFile = $matches.Matches | ForEach-Object { $_.Value } | Sort-Object | Select-Object -Last 1 137 | 138 | Write-Host "Latest version found: $latestFile on $DOWNLOAD_URL" 139 | 140 | $zip_url = "$DOWNLOAD_URL$latestFile" 141 | Write-Host "Download URL: $zip_url" 142 | 143 | echo "zip_url=$zip_url" >> $env:GITHUB_OUTPUT 144 | echo "zip_file_name=$latestFile" >> $env:GITHUB_OUTPUT 145 | 146 | - name: Cache ssdeep download 147 | id: cache_ssdeep 148 | # explicit restore used to avoid slow cache issue 149 | # https://github.com/actions/toolkit/issues/1578 150 | uses: actions/cache/restore@v4 151 | with: 152 | path: | 153 | ${{ github.workspace }}/libfuzzy 154 | key: ssdeep-${{matrix.php-version}}-${{ steps.setup-php-sdk.outputs.vs }}-${{matrix.arch}}-${{ steps.ssdeep_release.outputs.zip_file_name }} 155 | 156 | - name: Download the latest libfuzzy Windows release 157 | if: steps.cache_ssdeep.outputs.cache-hit != 'true' 158 | env: 159 | ZIP_URL: ${{ steps.ssdeep_release.outputs.zip_url }} 160 | ZIP_FILE: "${{ github.workspace }}\\${{ steps.ssdeep_release.outputs.zip_file_name }}" 161 | run: | 162 | Invoke-WebRequest -Uri $env:ZIP_URL -OutFile $env:ZIP_FILE 163 | 164 | - name: Extract the downloaded zip file into the ssdeep directory 165 | if: steps.cache_ssdeep.outputs.cache-hit != 'true' 166 | id: extract_libfuzzy 167 | env: 168 | ZIP_FILE: "${{ github.workspace }}\\${{ steps.ssdeep_release.outputs.zip_file_name }}" 169 | run: | 170 | if ($env:ZIP_FILE -like "*.zip") { 171 | Write-Host "Unzipping ${env:ZIP_FILE}..." 172 | Expand-Archive -Path $env:ZIP_FILE -DestinationPath $env:LIBFUZZY_DIR -Force 173 | Write-Host "libfuzzy extracted to $env:LIBFUZZY_DIR" 174 | } else { 175 | Write-Host "Error: The downloaded file is not a zip archive." 176 | exit 1 177 | } 178 | 179 | - name: Cache ssdeep save 180 | id: cache_ssdeep_save 181 | # explicit save used to avoid slow cache issue 182 | # https://github.com/actions/toolkit/issues/1578 183 | uses: actions/cache/save@v4 184 | with: 185 | path: | 186 | ${{ github.workspace }}/libfuzzy 187 | key: ssdeep-${{matrix.php-version}}-${{ steps.setup-php-sdk.outputs.vs }}-${{matrix.arch}}-${{ steps.ssdeep_release.outputs.zip_file_name }} 188 | 189 | - name: Add libfuzzy to LIB and INCLUDE 190 | run: | 191 | Write-Host "Adding $env:LIBFUZZY_DIR to LIB and INCLUDE environment variables" 192 | 193 | $libPath = Join-Path $env:LIBFUZZY_DIR "lib" 194 | $includePath = Join-Path $env:LIBFUZZY_DIR "include" 195 | 196 | if ((Test-Path $libPath) -and (Test-Path $includePath)) { 197 | $env:LIB += ";$libPath" 198 | $env:INCLUDE += ";$includePath" 199 | 200 | # propagate the changes to the next steps 201 | echo "LIB=$env:LIB" >> $env:GITHUB_ENV 202 | echo "INCLUDE=$env:INCLUDE" >> $env:GITHUB_ENV 203 | 204 | Write-Host "LIB is now: $env:LIB" 205 | Write-Host "INCLUDE is now: $env:INCLUDE" 206 | } else { 207 | Write-Host "Error: lib and include directories not found at $libPath and $includePath" 208 | exit 1 209 | } 210 | 211 | - name: List the contents of the working directory 212 | run: | 213 | Get-ChildItem -Path ${{ github.workspace }} 214 | 215 | - name: PHPize PECL Extension 216 | shell: cmd 217 | run: | 218 | phpize 219 | 220 | - name: Configure PHP Extension 221 | shell: cmd 222 | run: | 223 | configure --with-ssdeep --with-php-build=.\..\deps --with-prefix=${{ steps.setup-php-sdk.outputs.prefix }} 224 | 225 | - name: extract variables from Makefile (build_dir and dll_file) 226 | id: extract_makefile_vars 227 | shell: powershell 228 | env: 229 | DLL_FILE_NAME: php_ssdeep.dll 230 | run: | 231 | $makefileContent = Get-Content -Path ./Makefile -Raw 232 | $buildDirsSub = [regex]::Match($makefileContent, "BUILD_DIRS_SUB=(.*)").Groups[1].Value.Trim() 233 | $dllFullPath = Join-Path -Path $buildDirsSub -ChildPath $env:DLL_FILE_NAME 234 | 235 | echo "dll_path=$dllFullPath" >> $env:GITHUB_OUTPUT 236 | echo "dll_file_name=$env:DLL_FILE_NAME" >> $env:GITHUB_OUTPUT 237 | 238 | - name: Build PHP Extension 239 | shell: cmd 240 | run: | 241 | nmake 242 | 243 | - name: Verify the file was built 244 | env: 245 | dll_path: ${{ steps.extract_makefile_vars.outputs.dll_path }} 246 | run: | 247 | if (Test-Path $env:dll_path) { 248 | Write-Host "The extension was built successfully." 249 | } else { 250 | Write-Host "Error: The extension was not built." 251 | exit 1 252 | } 253 | 254 | - name: Run tests 255 | shell: cmd 256 | run: | 257 | nmake test TESTS="--show-diff -g FAIL,BORK,WARN,LEAK tests" 258 | 259 | - name: Copy the built extension to the workspace 260 | id: copy_dll 261 | run: | 262 | Copy-Item -Path "${{ steps.extract_makefile_vars.outputs.dll_path }}" -Destination "${{ github.workspace }}" 263 | 264 | echo "dll_path_for_packaging=${{ github.workspace }}\\${{ steps.extract_makefile_vars.outputs.dll_file_name }}" >> $env:GITHUB_OUTPUT 265 | 266 | - name: Generate SHA256 sum of the built extension 267 | id: generate_sha256 268 | env: 269 | SHA256_FILE: ${{ steps.copy_dll.outputs.dll_path_for_packaging }}.sha256 270 | run: | 271 | $checksum = Get-FileHash -Path ${{ steps.copy_dll.outputs.dll_path_for_packaging }} -Algorithm SHA256 272 | $lowercaseChecksum = $checksum.Hash.ToLower() 273 | [System.IO.File]::WriteAllText($env:SHA256_FILE, $lowercaseChecksum) 274 | 275 | Write-Host "SHA256 checksum: $lowercaseChecksum" 276 | echo "sha256=$lowercaseChecksum" >> $env:GITHUB_OUTPUT 277 | echo "sha256_file=$env:SHA256_FILE" >> $env:GITHUB_OUTPUT 278 | 279 | - name: Archive build artefacts 280 | uses: actions/upload-artifact@v4 281 | with: 282 | name: php-ssdeep-${{ matrix.os }}-${{ matrix.arch }}-php${{ matrix.php-version }}-${{ matrix.ts }} 283 | path: | 284 | ${{ steps.copy_dll.outputs.dll_path_for_packaging }} 285 | ${{ steps.generate_sha256.outputs.sha256_file }} 286 | 287 | release_for_upload_to_pecl: 288 | if: github.ref_type == 'tag' 289 | needs: [check-version, build-linux] 290 | runs-on: ubuntu-latest 291 | 292 | steps: 293 | - name: Checkout code 294 | uses: actions/checkout@v4 295 | 296 | - name: Create a release of the extension 297 | run: | 298 | pear package 299 | 300 | - name: Check for ssdeep file matching semver in the current directory 301 | id: check_file_exists 302 | run: | 303 | FILE_PATTERN="ssdeep-[0-9]+\.[0-9]+\.[0-9]+\.tgz" 304 | 305 | FILE_FOUND=$(ls -1 | grep -E ${FILE_PATTERN} | head -n 1) 306 | 307 | if [ -n "$FILE_FOUND" ]; then 308 | echo "File ${FILE_FOUND} found in the current directory." 309 | echo "file_name=${FILE_FOUND}" >> $GITHUB_OUTPUT 310 | else 311 | echo "File matching pattern ${FILE_PATTERN} not found." 312 | exit 1 313 | fi 314 | 315 | - name: Archive build artifacts 316 | uses: actions/upload-artifact@v4 317 | with: 318 | name: ${{ steps.check_file_exists.outputs.file_name }} 319 | path: | 320 | ${{ github.workspace }}/${{ steps.check_file_exists.outputs.file_name }} 321 | --------------------------------------------------------------------------------