below a new version of the PHP extension file from here; see my comments at the head - hope for some comments and help of others....
- Code: Select all
I created a PHP extension that allows you to find the location of an IP address - City, State,
Country, Longitude, Latitude, and other information as all, such as ISP and connection type.
To compile it, you will need to have the GeoIP c library installed.
For installation instructions for PHP extensions, see:
http://www.php.net/manual/en/install.pecl.php
Various changes introduced by G. Knauf:
- moved argument parsing up at various places before we create a GeoIP.
- moved GeoIP_delete() call at various places up to get it called before we error out.
- fixed one place where a missing return caused a segfault on all platforms because
RETURN_STRING() was called with NULL when GeoIP lookup failed.
- changed error 'host not found' from E_WARNING to E_NOTICE since I think its a usual
thing that the GeoIP doesnt return something in case the hostname was invalid;
even more I think it would be better to just return an empty string, or 'N/A' and
'--' like mod_geoip2 does, but write no error out at all.
- added php.ini entry parsing; prepared for usage of geoip.database_standard var.
This works fine so far as you can test with the geoip_database_info() call which uses
the database configured with the geoip.database_standard var; if not set it defaults
to the usual place specified during compile time with GEOIPDATADIR. All other functions
currently still ignore the geoip.database_standard setting, but default usually also
to GEOIPDATADIR except on Win32 from what I see. Search for the dirty '//' comments ....
Since I'm very new to the GeoIP API I dont know how to specify properly the database file
when calling the functions; first trial worked with geoip_database_info(), but next did
not with geoip_country_code_by_name() - and again segfault...
So for now I would like to leave the proper calls of the GeoIP lib to someone with more
insigt - I did the PHP stuff where I'm a bit more familar with....
Also from what I see it would probably make more sense to specify a base directory rather
then the fullpath filename; but the Apache2 mod_geoip2 also takes the filename....;
so I'm asking me what about the other libs? Or can we only have one at same time?
php_geoip.c
Code:
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 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_0.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: Matthew Fonda |
+----------------------------------------------------------------------+
Please contact support@maxmind.com with any comments
*/
#define EXTENSION_VERSION "0.1.1"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <GeoIP.h>
#include <GeoIPCity.h>
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_geoip.h"
ZEND_DECLARE_MODULE_GLOBALS(geoip)
static int le_geoip;
function_entry geoip_functions[] = {
PHP_FE(geoip_database_info, NULL)
PHP_FE(geoip_country_code_by_name, NULL)
PHP_FE(geoip_country_name_by_name, NULL)
PHP_FE(geoip_org_by_name, NULL)
PHP_FE(geoip_record_by_name, NULL)
PHP_FE(geoip_id_by_name, NULL)
PHP_FE(geoip_region_by_name, NULL)
{NULL, NULL, NULL}
};
/* }}} */
/* {{{ geoip_module_entry
*/
zend_module_entry geoip_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"geoip",
geoip_functions,
PHP_MINIT(geoip),
PHP_MSHUTDOWN(geoip),
PHP_RINIT(geoip),
PHP_RSHUTDOWN(geoip),
PHP_MINFO(geoip),
#if ZEND_MODULE_API_NO >= 20010901
EXTENSION_VERSION, /* version number of the extension */
#endif
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_GEOIP
ZEND_GET_MODULE(geoip)
#endif
#ifdef GEOIPDATADIR
#define GEOIPDATABASE GEOIPDATADIR "/GeoIP.dat"
#else
#define GEOIPDATABASE "GeoIP.dat"
#endif
/* }}} */
/* {{{ PHP_INI
*/
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("geoip.database_standard", GEOIPDATABASE, PHP_INI_ALL, OnUpdateString, database_standard, zend_geoip_globals, geoip_globals)
PHP_INI_END()
/* }}} */
/* {{{ php_geoip_init_globals
*/
static void php_geoip_init_globals(zend_geoip_globals *geoip_globals)
{
geoip_globals->database_standard = NULL;
}
/* }}} */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(geoip)
{
ZEND_INIT_MODULE_GLOBALS(geoip, php_geoip_init_globals, NULL);
REGISTER_INI_ENTRIES();
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(geoip)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(geoip)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(geoip)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(geoip)
{
php_info_print_table_start();
php_info_print_table_header(2, "geoip support", "enabled");
php_info_print_table_row(2, "geoip extension version", EXTENSION_VERSION);
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
/* }}} */
PHP_FUNCTION(geoip_database_info)
{
GeoIP * gi;
char * db_info;
if(ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
//gi = GeoIP_new(GEOIP_STANDARD);
gi = GeoIP_open(GEOIP_G(database_standard), GEOIP_STANDARD);
db_info = GeoIP_database_info(gi);
GeoIP_delete(gi);
RETURN_STRING(db_info, 1);
efree(db_info);
}
PHP_FUNCTION(geoip_country_code_by_name)
{
GeoIP * gi;
char * hostname = NULL;
const char * country_code;
int arglen;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &arglen) == FAILURE) {
return;
}
gi = GeoIP_new(GEOIP_COUNTRY_EDITION);
//gi = GeoIP_open(GEOIP_G(database_standard), GEOIP_COUNTRY_EDITION);
country_code = GeoIP_country_code_by_name(gi, hostname);
GeoIP_delete(gi);
if (country_code == NULL) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Host %s not found", hostname);
return;
}
RETURN_STRING((char*)country_code, 1);
}
PHP_FUNCTION(geoip_country_name_by_name)
{
GeoIP * gi;
char * hostname = NULL;
const char * country_name;
int arglen;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &arglen) == FAILURE) {
return;
}
if (GeoIP_db_avail(GEOIP_COUNTRY_EDITION)) {
gi = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Required database not available.");
return;
}
country_name = GeoIP_country_name_by_name(gi, hostname);
GeoIP_delete(gi);
if (country_name == NULL) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Host %s not found", hostname);
return;
}
RETURN_STRING((char*)country_name, 1);
}
PHP_FUNCTION(geoip_org_by_name)
{
GeoIP * gi;
char * hostname = NULL;
const char * org;
int arglen;
if (GeoIP_db_avail(GEOIP_ORG_EDITION)) {
gi = GeoIP_open_type(GEOIP_ORG_EDITION, GEOIP_STANDARD);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Required database not available");
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &arglen) == FAILURE) {
return;
}
org = GeoIP_org_by_name(gi, hostname);
GeoIP_delete(gi);
if (org == NULL) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Host %s not found", hostname);
return;
}
RETURN_STRING((char*)org, 1);
}
PHP_FUNCTION(geoip_record_by_name)
{
GeoIP * gi;
char * hostname = NULL;
int arglen;
GeoIPRecord * gir;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &arglen) == FAILURE) {
return;
}
if (GeoIP_db_avail(GEOIP_CITY_EDITION_REV1)) {
gi = GeoIP_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_STANDARD);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Required database not available");
return;
}
gir = GeoIP_record_by_name(gi, hostname);
if (NULL == gir) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Host %s not found", hostname);
return;
}
if (NULL == gir->country_code) {
gir->country_code = "";
}
if (NULL == gir->region) {
gir->region = "";
}
if (NULL == gir->city) {
gir->city = "";
}
if (NULL == gir->postal_code) {
gir->postal_code = "";
}
array_init(return_value);
add_assoc_string(return_value, "country_code", gir->country_code, 1);
add_assoc_string(return_value, "region", gir->region, 1);
add_assoc_string(return_value, "city", gir->city, 1);
add_assoc_string(return_value, "postal_code", gir->postal_code, 1);
add_assoc_long(return_value, "latitude", gir->latitude);
add_assoc_long(return_value, "longitude", gir->longitude);
add_assoc_long(return_value, "dma_code", gir->dma_code);
add_assoc_long(return_value, "area_code", gir->area_code);
GeoIP_delete(gi);
}
PHP_FUNCTION(geoip_id_by_name)
{
GeoIP * gi;
char * hostname = NULL;
int arglen;
int netspeed;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &arglen) == FAILURE) {
return;
}
if (GeoIP_db_avail(GEOIP_NETSPEED_EDITION)) {
gi = GeoIP_open_type(GEOIP_NETSPEED_EDITION, GEOIP_STANDARD);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Required database not available");
return;
}
netspeed = GeoIP_id_by_name(gi, hostname);
GeoIP_delete(gi);
RETURN_LONG(netspeed);
}
PHP_FUNCTION(geoip_region_by_name)
{
GeoIP * gi;
char * hostname = NULL;
int arglen;
GeoIPRegion * region;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &arglen) == FAILURE) {
return;
}
if (GeoIP_db_avail(GEOIP_REGION_EDITION_REV0) || GeoIP_db_avail(GEOIP_REGION_EDITION_REV1)) {
if (GeoIP_db_avail(GEOIP_REGION_EDITION_REV1)) {
gi = GeoIP_open_type(GEOIP_REGION_EDITION_REV1, GEOIP_STANDARD);
} else {
gi = GeoIP_open_type(GEOIP_REGION_EDITION_REV0, GEOIP_STANDARD);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Required database not available");
return;
}
region = GeoIP_region_by_name(gi, hostname);
GeoIP_delete(gi);
if (NULL == region) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Host %s not found", hostname);
return;
}
array_init(return_value);
add_assoc_string(return_value, "country_code", region->country_code, 1);
add_assoc_string(return_value, "region", region->region, 1);
}
/* }}} */
/*
* 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
*/
php_geoip.h
Code:
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 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_0.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: Matthew Fonda |
+----------------------------------------------------------------------+
Please contact support@maxmind.com with any comments
*/
#ifndef PHP_GEOIP_H
#define PHP_GEOIP_H
extern zend_module_entry geoip_module_entry;
#define phpext_geoip_ptr &geoip_module_entry
#ifdef PHP_WIN32
#define PHP_GEOIP_API __declspec(dllexport)
#else
#define PHP_GEOIP_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
#include <GeoIP.h>
#include <GeoIPCity.h>
PHP_MINIT_FUNCTION(geoip);
PHP_MSHUTDOWN_FUNCTION(geoip);
PHP_RINIT_FUNCTION(geoip);
PHP_RSHUTDOWN_FUNCTION(geoip);
PHP_MINFO_FUNCTION(geoip);
PHP_FUNCTION(geoip_database_info);
PHP_FUNCTION(geoip_country_code_by_name);
PHP_FUNCTION(geoip_country_name_by_name);
PHP_FUNCTION(geoip_org_by_name);
PHP_FUNCTION(geoip_record_by_name);
PHP_FUNCTION(geoip_id_by_name);
PHP_FUNCTION(geoip_region_by_name);
ZEND_BEGIN_MODULE_GLOBALS(geoip)
char* database_standard;
ZEND_END_MODULE_GLOBALS(geoip)
#ifdef ZTS
#define GEOIP_G(v) TSRMG(geoip_globals_id, zend_geoip_globals *, v)
#else
#define GEOIP_G(v) (geoip_globals.v)
#endif
#endif /* PHP_GEOIP_H */
/*
* 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
*/
