From 88e8bef6b42bbd1a831e5c8a37e0567251df83bb Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Wed, 24 Jan 2018 13:21:47 +0100
Subject: initial work, only PoC for now

---
 .gitignore              |  54 +++++++++++++++
 CREDITS                 |   2 +
 EXPERIMENTAL            |   0
 LICENSE                 |  68 ++++++++++++++++++
 README.md               |  36 ++++++++++
 config.m4               |  30 ++++++++
 examples/repomanage.php |  74 ++++++++++++++++++++
 php_rpminfo.h           |  60 ++++++++++++++++
 rpminfo.c               | 178 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/001.phpt          |  10 +++
 tests/002.phpt          |  29 ++++++++
 11 files changed, 541 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 CREDITS
 create mode 100644 EXPERIMENTAL
 create mode 100644 LICENSE
 create mode 100644 README.md
 create mode 100644 config.m4
 create mode 100644 examples/repomanage.php
 create mode 100644 php_rpminfo.h
 create mode 100644 rpminfo.c
 create mode 100644 tests/001.phpt
 create mode 100644 tests/002.phpt

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c022dd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,54 @@
+# Object files
+*.o
+*.lo
+
+# Libraries
+*.lib
+*.a
+*.la
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+
+
+# autotools
+.deps
+.libs
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.in
+conftest
+conftest.c
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+acinclude.m4
+aclocal.m4
+autom4te.cache
+build
+install-sh
+libtool
+ltmain.sh
+ltmain.sh.backup
+missing
+mkinstalldirs
+modules
+run-tests.php
+run-tests.log
+tmp-php.ini
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..b90dc70
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,2 @@
+rpminfo
+Remi Collet
diff --git a/EXPERIMENTAL b/EXPERIMENTAL
new file mode 100644
index 0000000..e69de29
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9964e07
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,68 @@
+-------------------------------------------------------------------- 
+                  The PHP License, version 3.01
+Copyright (c) 1999 - 2017 The PHP Group. All rights reserved.
+-------------------------------------------------------------------- 
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+ 
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+ 
+  3. The name "PHP" must not be used to endorse or promote products
+     derived from this software without prior written permission. For
+     written permission, please contact group@php.net.
+  
+  4. Products derived from this software may not be called "PHP", nor
+     may "PHP" appear in their name, without prior written permission
+     from group@php.net.  You may indicate that your software works in
+     conjunction with PHP by saying "Foo for PHP" instead of calling
+     it "PHP Foo" or "phpfoo"
+ 
+  5. The PHP Group may publish revised and/or new versions of the
+     license from time to time. Each version will be given a
+     distinguishing version number.
+     Once covered code has been published under a particular version
+     of the license, you may always continue to use it under the terms
+     of that version. You may also choose to use such covered code
+     under the terms of any subsequent version of the license
+     published by the PHP Group. No one other than the PHP Group has
+     the right to modify the terms applicable to covered code created
+     under this License.
+
+  6. Redistributions of any form whatsoever must retain the following
+     acknowledgment:
+     "This product includes PHP software, freely available from
+     <http://www.php.net/software/>".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------- 
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the PHP Group.
+
+The PHP Group can be contacted via Email at group@php.net.
+
+For more information on the PHP Group and the PHP project, 
+please see <http://www.php.net>.
+
+PHP includes the Zend Engine, freely available at
+<http://www.zend.com>.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e3c75aa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+rpminfo
+=======
+
+Experimental wrapper for librpm
+
+For now, only expose
+
+	int rpmvercmp(string evr1, string evr2);
+
+
+Mostly a PoC build for fun because of
+
+https://bugzilla.redhat.com/1537981
+performance issue: dnf repomanage is really slow
+
+
+Some benchmark results (find 15 old RPMs among 5000)
+
+# DNF on Fedora
+
+    $ time dnf repomanage --old --keep 5 .
+    ...
+    real	0m15,971s
+
+# YUM on RHEL / CentOS
+
+    $ time repomanage --old --keep 5 .
+    ...
+    real	0m5.519s
+
+# PHP script
+
+    $ time php repomanage.php --old --keep 5 .
+    ...
+    real	0m0,634s
+
diff --git a/config.m4 b/config.m4
new file mode 100644
index 0000000..8d117a9
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,30 @@
+dnl config.m4 for extension rpminfo
+
+PHP_ARG_ENABLE(rpminfo, whether to enable rpminfo support,
+   [  --enable-rpminfo        Enable rpminfo support])
+
+if test "$PHP_RPMINFO" != "no"; then
+  dnl Write more examples of tests here...
+
+  AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+
+  AC_MSG_CHECKING(for librpm)
+  if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists rpm; then
+    if $PKG_CONFIG rpm --atleast-version 4; then
+      LIBRPM_CFLAGS=`$PKG_CONFIG rpm --cflags`
+      LIBRPM_LIBDIR=`$PKG_CONFIG rpm --libs`
+      LIBRPM_VERSON=`$PKG_CONFIG rpm --modversion`
+      AC_MSG_RESULT(from pkgconfig: version $LIBRPM_VERSON)
+    else
+      AC_MSG_ERROR(system librpm is too old)
+    fi
+  else
+    AC_MSG_ERROR(pkg-config not found)
+  fi
+  PHP_EVAL_LIBLINE($LIBRPM_LIBDIR, RPMINFO_SHARED_LIBADD)
+  PHP_EVAL_INCLINE($LIBRPM_CFLAGS)
+
+  PHP_SUBST(RPMINFO_SHARED_LIBADD)
+
+  PHP_NEW_EXTENSION(rpminfo, rpminfo.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
+fi
diff --git a/examples/repomanage.php b/examples/repomanage.php
new file mode 100644
index 0000000..8428118
--- /dev/null
+++ b/examples/repomanage.php
@@ -0,0 +1,74 @@
+#!/usr/bin/php
+<?php
+
+extension_loaded("rpminfo") or die("rpminfo extension missing");
+
+for ($i=1 ; $i<$_SERVER['argc'] ; $i++) {
+	switch($_SERVER['argv'][$i]) {
+		case '-o':
+		case '--old':
+			if (isset($old)) {
+				die("--old option incompatible with --new\n");
+			}
+			$old = true;
+			break;
+		case '-n':
+		case '--new':
+			if (isset($new)) {
+				die("--new option incompatible with --old\n");
+			}
+		case '-k':
+		case '--keep':
+			if (!isset($_SERVER['argv'][$i+1])) {
+				die("Missing argument for --keep option\n");
+			}
+			$keep = (int)$_SERVER['argv'][$i+1];
+			$i++;
+			break;
+		default:
+			$rpms = $_SERVER['argv'][$i];
+	}
+}
+if (!isset($old)) {
+	die("Missing --old or --new option\n");
+}
+if (!isset($keep)) {
+	$keep = 1;
+}
+if (!isset($rpms)) {
+	die("Missing rpm directory argument\n");
+}
+if (is_dir($rpms)) {
+} else {
+	die("$rpms is not a directory\n");
+}
+
+$tree = [];
+$handle = popen('rpm -qp --qf "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH} %{NEVR}\n" ' . $rpms . "/*.rpm", "r");
+while ($line = fgets($handle)) {
+	$tab = explode(' ', trim($line));
+	if (count($tab) == 6) {
+		$tree[$tab[4]][$tab[0]][] = [
+			'name' => $tab[0],
+			'path' => $tab[5],
+			'evr'  => "${tab[1]}:${tab[2]}-${tab[3]}",
+		];
+	} else {
+		echo "Ignore $line\n";
+	}
+}
+foreach($tree as $arch => $subtree) {
+	foreach ($subtree as $name => $versions) {
+		if (count($versions) > $keep) {
+			if ($old) {
+				usort($versions, function($a, $b) { return -rpmvercmp($a['evr'], $b['evr']);} );
+			} else {
+				usort($versions, function($a, $b) { return rpmvercmp($a['evr'], $b['evr']);} );
+			}
+			for ($i = $keep ; $i < count($versions) ; $i++) {
+				echo $versions[$i]['path'] . "\n";
+			}
+		}
+	}
+}
+
diff --git a/php_rpminfo.h b/php_rpminfo.h
new file mode 100644
index 0000000..9c8319c
--- /dev/null
+++ b/php_rpminfo.h
@@ -0,0 +1,60 @@
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 7                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2018 The PHP Group                                     |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author: Remi Collet <remi@php.net>                                   |
+  +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_RPMINFO_H
+#define PHP_RPMINFO_H
+
+extern zend_module_entry rpminfo_module_entry;
+#define phpext_rpminfo_ptr &rpminfo_module_entry
+
+#define PHP_RPMINFO_VERSION "0.1.0" /* Replace with version number for your extension */
+
+#ifdef PHP_WIN32
+#	define PHP_RPMINFO_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+#	define PHP_RPMINFO_API __attribute__ ((visibility("default")))
+#else
+#	define PHP_RPMINFO_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+/*
+  	Declare any global variables you may need between the BEGIN
+	and END macros here:
+
+ZEND_BEGIN_MODULE_GLOBALS(rpminfo)
+	zend_long  global_value;
+	char *global_string;
+ZEND_END_MODULE_GLOBALS(rpminfo)
+*/
+
+/* Always refer to the globals in your function as RPMINFO_G(variable).
+   You are encouraged to rename these macros something shorter, see
+   examples in any other php module directory.
+*/
+#define RPMINFO_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(rpminfo, v)
+
+#if defined(ZTS) && defined(COMPILE_DL_RPMINFO)
+ZEND_TSRMLS_CACHE_EXTERN()
+#endif
+
+#endif	/* PHP_RPMINFO_H */
+
diff --git a/rpminfo.c b/rpminfo.c
new file mode 100644
index 0000000..b08e05b
--- /dev/null
+++ b/rpminfo.c
@@ -0,0 +1,178 @@
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 7                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2018 The PHP Group                                     |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author: Remi Collet <remi@php.net>                                   |
+  +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_rpminfo.h"
+
+#include <rpm/rpmlib.h>
+
+/* If you declare any globals in php_rpminfo.h uncomment this:
+ZEND_DECLARE_MODULE_GLOBALS(rpminfo)
+*/
+
+/* True global resources - no need for thread safety here */
+static int le_rpminfo;
+
+/* {{{ PHP_INI
+ */
+/* Remove comments and fill if you need to have entries in php.ini
+PHP_INI_BEGIN()
+    STD_PHP_INI_ENTRY("rpminfo.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_rpminfo_globals, rpminfo_globals)
+    STD_PHP_INI_ENTRY("rpminfo.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_rpminfo_globals, rpminfo_globals)
+PHP_INI_END()
+*/
+/* }}} */
+
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_rpmvercmp, 0, 0, 2)
+	ZEND_ARG_INFO(0, evr1)
+	ZEND_ARG_INFO(0, evr2)
+ZEND_END_ARG_INFO()
+
+/* Every user-visible function in PHP should document itself in the source */
+/* {{{ proto string rpmcmpver(string evr1, string evr2)
+   Return a string to confirm that the module is compiled in */
+PHP_FUNCTION(rpmvercmp)
+{
+	char *evr1, *evr2;
+	size_t len1, len2;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &evr1, &len1, &evr2, &len2) == FAILURE) {
+		return;
+	}
+
+	RETURN_LONG(rpmvercmp(evr1, evr2));
+}
+
+/* {{{ php_rpminfo_init_globals
+ */
+/* Uncomment this function if you have INI entries
+static void php_rpminfo_init_globals(zend_rpminfo_globals *rpminfo_globals)
+{
+	rpminfo_globals->global_value = 0;
+	rpminfo_globals->global_string = NULL;
+}
+*/
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(rpminfo)
+{
+	/* If you have INI entries, uncomment these lines
+	REGISTER_INI_ENTRIES();
+	*/
+	return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(rpminfo)
+{
+	/* uncomment this line if you have INI entries
+	UNREGISTER_INI_ENTRIES();
+	*/
+	return SUCCESS;
+}
+/* }}} */
+
+/* Remove if there's nothing to do at request start */
+/* {{{ PHP_RINIT_FUNCTION
+ */
+PHP_RINIT_FUNCTION(rpminfo)
+{
+#if defined(COMPILE_DL_RPMINFO) && defined(ZTS)
+	ZEND_TSRMLS_CACHE_UPDATE();
+#endif
+	return SUCCESS;
+}
+/* }}} */
+
+/* Remove if there's nothing to do at request end */
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+PHP_RSHUTDOWN_FUNCTION(rpminfo)
+{
+	return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(rpminfo)
+{
+	php_info_print_table_start();
+	php_info_print_table_header(2, "rpminfo support", "enabled");
+	php_info_print_table_row(2, "Extension version", PHP_RPMINFO_VERSION);
+	php_info_print_table_row(2, "RPM library version", RPMVERSION);
+	php_info_print_table_end();
+
+	/* Remove comments if you have entries in php.ini
+	DISPLAY_INI_ENTRIES();
+	*/
+}
+/* }}} */
+
+/* {{{ rpminfo_functions[]
+ *
+ * Every user visible function must have an entry in rpminfo_functions[].
+ */
+const zend_function_entry rpminfo_functions[] = {
+	PHP_FE(rpmvercmp,	      arginfo_rpmvercmp)
+	PHP_FE_END
+};
+/* }}} */
+
+/* {{{ rpminfo_module_entry
+ */
+zend_module_entry rpminfo_module_entry = {
+	STANDARD_MODULE_HEADER,
+	"rpminfo",
+	rpminfo_functions,
+	PHP_MINIT(rpminfo),
+	PHP_MSHUTDOWN(rpminfo),
+	PHP_RINIT(rpminfo),		/* Replace with NULL if there's nothing to do at request start */
+	PHP_RSHUTDOWN(rpminfo),	/* Replace with NULL if there's nothing to do at request end */
+	PHP_MINFO(rpminfo),
+	PHP_RPMINFO_VERSION,
+	STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_RPMINFO
+#ifdef ZTS
+ZEND_TSRMLS_CACHE_DEFINE()
+#endif
+ZEND_GET_MODULE(rpminfo)
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/tests/001.phpt b/tests/001.phpt
new file mode 100644
index 0000000..69f6aff
--- /dev/null
+++ b/tests/001.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Check for rpminfo presence
+--SKIPIF--
+<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
+--FILE--
+<?php 
+echo "rpminfo extension is available";
+?>
+--EXPECT--
+rpminfo extension is available
diff --git a/tests/002.phpt b/tests/002.phpt
new file mode 100644
index 0000000..513339c
--- /dev/null
+++ b/tests/002.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Check for rpmvercmp function
+--SKIPIF--
+<?php if (!extension_loaded("rpminfo")) print "skip"; ?>
+--FILE--
+<?php 
+var_dump(rpmvercmp("1.0", "1.1"));
+var_dump(rpmvercmp("1.1", "1.0"));
+var_dump(rpmvercmp("1.0", "1.0"));
+// Errors
+var_dump(rpmvercmp());
+var_dump(rpmvercmp("a"));
+var_dump(rpmvercmp("a", "b", "c"));
+?>
+Done
+--EXPECT--
+int(-1)
+int(1)
+int(0)
+
+Warning: rpmvercmp() expects exactly 2 parameters, 0 given in /work/GIT/php-rpminfo/tests/002.php on line 6
+NULL
+
+Warning: rpmvercmp() expects exactly 2 parameters, 1 given in /work/GIT/php-rpminfo/tests/002.php on line 7
+NULL
+
+Warning: rpmvercmp() expects exactly 2 parameters, 3 given in /work/GIT/php-rpminfo/tests/002.php on line 8
+NULL
+Done
-- 
cgit