Composer - update, prenova map v mojih anketah - V DELU

This commit is contained in:
pero1203 2022-09-19 15:18:44 +02:00
parent c45926b660
commit aa404ff469
116 changed files with 8149 additions and 5355 deletions

View File

@ -3548,7 +3548,7 @@ class SurveyAdmin
// VSEBINA POSAMEZNEGA TABA PRI MOJIH ANKETAH
echo '<div id="moje_ankete_edit" class="page_'.$_GET['a'].' subpage_'.$_GET['t'].' '.(isset($_GET['b']) ? 'subpage_b_'.$_GET['b'] : '').' '.($SLCount == 0 ? 'page_ustvari_anketo' : '').' '.$this->layout_width.' '.$this->layout_menu.'">';
echo '<div id="moje_ankete_edit" class="page_'.$_GET['a'].' subpage_'.$_GET['t'].' '.(isset($_GET['b']) ? 'subpage_b_'.$_GET['b'] : '').' '.($SLCount == 0 ? 'page_ustvari_anketo' : '').' '.($SLCount > 0 && $SL->hasFolders() ? 'moje_ankete_folders' : '').' '.$this->layout_width.' '.$this->layout_menu.'">';
// izpis pregledovanja
if ((!isset($_GET['a']) && !isset($_GET['anketa'])) || ($_GET['a'] == 'pregledovanje')) {

View File

@ -508,6 +508,10 @@ elseif ($_GET['t'] == 'surveyList') {
$SL = new SurveyList();
$SL->Ajax();
}
elseif ($_GET['t'] == 'surveyListFolders') {
$SL = new SurveyListFolders();
$SL->Ajax();
}
elseif ($_GET['t'] == 'library') {
$l = new Library();
$l->ajax();

View File

@ -228,6 +228,16 @@ class SurveyList {
}
// Vrnemo, ce imamo vklopljene folderje ali ne
public function hasFolders(){
if($this->show_folders == '1')
return true;
else
return false;
}
/**
* @desc prikaze zgornjo navigacijo
*/
@ -880,6 +890,16 @@ class SurveyList {
global $lang;
// Prikaz novih folderjev - V DELU
/*if($this->show_folders == 1){
$surveyFolders = new SurveyListFolders();
$surveyFolders->display();
return;
}*/
// Gumba za ustvarjanje ankete - nekatere niso prikazane pri searchu
if($this->isSearch != 1){

File diff suppressed because it is too large Load Diff

316
composer.lock generated
View File

@ -61,6 +61,11 @@
"ssl",
"tls"
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
"source": "https://github.com/composer/ca-bundle/tree/1.3.3"
},
"funding": [
{
"url": "https://packagist.com",
@ -154,27 +159,28 @@
},
{
"name": "geoip2/geoip2",
"version": "v2.10.0",
"version": "v2.13.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/GeoIP2-php.git",
"reference": "419557cd21d9fe039721a83490701a58c8ce784a"
"url": "git@github.com:maxmind/GeoIP2-php.git",
"reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/419557cd21d9fe039721a83490701a58c8ce784a",
"reference": "419557cd21d9fe039721a83490701a58c8ce784a",
"url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/6a41d8fbd6b90052bc34dff3b4252d0f88067b23",
"reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23",
"shasum": ""
},
"require": {
"ext-json": "*",
"maxmind-db/reader": "~1.5",
"maxmind/web-service-common": "~0.6",
"php": ">=5.6"
"maxmind-db/reader": "~1.8",
"maxmind/web-service-common": "~0.8",
"php": ">=7.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "5.*",
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"type": "library",
@ -203,7 +209,7 @@
"geolocation",
"maxmind"
],
"time": "2019-12-12T18:48:39+00:00"
"time": "2022-08-05T20:32:58+00:00"
},
{
"name": "guzzlehttp/guzzle",
@ -322,16 +328,16 @@
},
{
"name": "guzzlehttp/promises",
"version": "1.5.1",
"version": "1.5.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
"reference": "b94b2807d85443f9719887892882d0329d1e2598"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
"reference": "b94b2807d85443f9719887892882d0329d1e2598",
"shasum": ""
},
"require": {
@ -347,12 +353,12 @@
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -386,7 +392,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/1.5.1"
"source": "https://github.com/guzzle/promises/tree/1.5.2"
},
"funding": [
{
@ -402,7 +408,7 @@
"type": "tidelift"
}
],
"time": "2021-10-22T20:56:57+00:00"
"time": "2022-08-28T14:55:35+00:00"
},
{
"name": "guzzlehttp/psr7",
@ -516,29 +522,30 @@
},
{
"name": "maxmind-db/reader",
"version": "v1.6.0",
"version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4"
"reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/febd4920bf17c1da84cef58e56a8227dfb37fbe4",
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/b1f3c0699525336d09cc5161a2861268d9f2ae5b",
"reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b",
"shasum": ""
},
"require": {
"php": ">=5.6"
"php": ">=7.2"
},
"conflict": {
"ext-maxminddb": "<1.6.0,>=2.0.0"
"ext-maxminddb": "<1.10.1,>=2.0.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"friendsofphp/php-cs-fixer": "3.*",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpcov": "^3.0",
"phpunit/phpunit": "5.*",
"phpstan/phpstan": "*",
"phpunit/phpcov": ">=6.0.0",
"phpunit/phpunit": ">=8.0.0,<10.0.0",
"squizlabs/php_codesniffer": "3.*"
},
"suggest": {
@ -572,31 +579,36 @@
"geolocation",
"maxmind"
],
"time": "2019-12-19T22:59:03+00:00"
"support": {
"issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues",
"source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.11.0"
},
"time": "2021-10-18T15:23:10+00:00"
},
{
"name": "maxmind/web-service-common",
"version": "v0.7.0",
"version": "v0.9.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/web-service-common-php.git",
"reference": "74c996c218ada5c639c8c2f076756e059f5552fc"
"reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/74c996c218ada5c639c8c2f076756e059f5552fc",
"reference": "74c996c218ada5c639c8c2f076756e059f5552fc",
"url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/4dc5a3e8df38aea4ca3b1096cee3a038094e9b53",
"reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.0.3",
"ext-curl": "*",
"ext-json": "*",
"php": ">=5.6"
"php": ">=7.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0",
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"type": "library",
@ -618,7 +630,11 @@
],
"description": "Internal MaxMind Web Service API",
"homepage": "https://github.com/maxmind/web-service-common-php",
"time": "2020-05-06T14:07:26+00:00"
"support": {
"issues": "https://github.com/maxmind/web-service-common-php/issues",
"source": "https://github.com/maxmind/web-service-common-php/tree/v0.9.0"
},
"time": "2022-03-28T17:43:20+00:00"
},
{
"name": "minishlink/web-push",
@ -674,6 +690,10 @@
"push",
"web"
],
"support": {
"issues": "https://github.com/web-push-libs/web-push-php/issues",
"source": "https://github.com/web-push-libs/web-push-php/tree/v5.2.5"
},
"time": "2020-08-02T08:58:01+00:00"
},
{
@ -719,20 +739,25 @@
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "paragonie/sodium_compat",
"version": "v1.17.1",
"version": "v1.18.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/sodium_compat.git",
"reference": "ac994053faac18d386328c91c7900f930acadf1e"
"reference": "906e0b925895d3a5941eda25f371fbafb3cbc22f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/ac994053faac18d386328c91c7900f930acadf1e",
"reference": "ac994053faac18d386328c91c7900f930acadf1e",
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/906e0b925895d3a5941eda25f371fbafb3cbc22f",
"reference": "906e0b925895d3a5941eda25f371fbafb3cbc22f",
"shasum": ""
},
"require": {
@ -803,9 +828,9 @@
],
"support": {
"issues": "https://github.com/paragonie/sodium_compat/issues",
"source": "https://github.com/paragonie/sodium_compat/tree/v1.17.1"
"source": "https://github.com/paragonie/sodium_compat/tree/v1.18.0"
},
"time": "2022-03-23T19:32:04+00:00"
"time": "2022-09-13T20:54:27+00:00"
},
{
"name": "paypal/paypal-checkout-sdk",
@ -830,8 +855,8 @@
"type": "library",
"autoload": {
"psr-4": {
"PayPalCheckoutSdk\\": "lib/PayPalCheckoutSdk",
"Sample\\": "samples/"
"Sample\\": "samples/",
"PayPalCheckoutSdk\\": "lib/PayPalCheckoutSdk"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -857,6 +882,7 @@
"support": {
"source": "https://github.com/paypal/Checkout-PHP-SDK/tree/1.0.2"
},
"abandoned": true,
"time": "2021-09-21T20:57:38+00:00"
},
{
@ -904,16 +930,16 @@
},
{
"name": "phpmailer/phpmailer",
"version": "v6.6.3",
"version": "v6.6.4",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "9400f305a898f194caff5521f64e5dfa926626f3"
"reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9400f305a898f194caff5521f64e5dfa926626f3",
"reference": "9400f305a898f194caff5521f64e5dfa926626f3",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"shasum": ""
},
"require": {
@ -970,7 +996,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.3"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4"
},
"funding": [
{
@ -978,7 +1004,7 @@
"type": "github"
}
],
"time": "2022-06-20T09:21:02+00:00"
"time": "2022-08-22T09:22:00+00:00"
},
{
"name": "psr/http-message",
@ -1028,6 +1054,9 @@
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"time": "2016-08-06T14:39:51+00:00"
},
{
@ -1068,27 +1097,31 @@
}
],
"description": "A polyfill for getallheaders.",
"support": {
"issues": "https://github.com/ralouphie/getallheaders/issues",
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "sonata-project/google-authenticator",
"version": "2.2.0",
"version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/sonata-project/GoogleAuthenticator.git",
"reference": "feda53899b26af24e3db2fe7a3e5f053ca483762"
"reference": "71a4189228f93a9662574dc8c65e77ef55061b59"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762",
"reference": "feda53899b26af24e3db2fe7a3e5f053ca483762",
"url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/71a4189228f93a9662574dc8c65e77ef55061b59",
"reference": "71a4189228f93a9662574dc8c65e77ef55061b59",
"shasum": ""
},
"require": {
"php": "^7.1"
"php": "^7.3 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.0"
"symfony/phpunit-bridge": "^5.1.8"
},
"type": "library",
"extra": {
@ -1107,6 +1140,10 @@
"MIT"
],
"authors": [
{
"name": "Thomas Rabaix",
"email": "thomas.rabaix@gmail.com"
},
{
"name": "Christian Stocker",
"email": "me@chregu.tv"
@ -1114,10 +1151,6 @@
{
"name": "Andre DeMarre",
"homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989"
},
{
"name": "Thomas Rabaix",
"email": "thomas.rabaix@gmail.com"
}
],
"description": "Library to integrate Google Authenticator into a PHP project",
@ -1125,8 +1158,30 @@
"keywords": [
"google authenticator"
],
"support": {
"issues": "https://github.com/sonata-project/GoogleAuthenticator/issues",
"source": "https://github.com/sonata-project/GoogleAuthenticator/tree/2.3.1"
},
"funding": [
{
"url": "https://github.com/OskarStark",
"type": "github"
},
{
"url": "https://github.com/VincentLanglet",
"type": "github"
},
{
"url": "https://github.com/core23",
"type": "github"
},
{
"url": "https://github.com/wbloszyk",
"type": "github"
}
],
"abandoned": true,
"time": "2018-07-18T22:08:02+00:00"
"time": "2021-02-15T19:23:18+00:00"
},
{
"name": "spomky-labs/base64url",
@ -1177,6 +1232,20 @@
"safe",
"url"
],
"support": {
"issues": "https://github.com/Spomky-Labs/base64url/issues",
"source": "https://github.com/Spomky-Labs/base64url/tree/v2.0.4"
},
"funding": [
{
"url": "https://github.com/Spomky",
"type": "github"
},
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-11-03T09:10:25+00:00"
},
{
@ -1559,6 +1628,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-core/tree/v1.3.10"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -1636,6 +1714,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-key-mgmt/tree/v1.3.10"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -1716,6 +1803,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-signature/tree/v1.3.10"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -1783,6 +1879,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-signature-algorithm-ecdsa/tree/v1.3.10"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -1850,6 +1955,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-signature-algorithm-eddsa/tree/v1.3.10"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -1917,6 +2031,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-signature-algorithm-hmac/tree/v1.3"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -1984,6 +2107,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-signature-algorithm-none/tree/v1.3"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -2051,6 +2183,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-signature-algorithm-rsa/tree/v1.3"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
},
{
@ -2120,6 +2261,15 @@
"jwt",
"symfony"
],
"support": {
"source": "https://github.com/web-token/jwt-util-ecc/tree/v1.3"
},
"funding": [
{
"url": "https://www.patreon.com/FlorentMorselli",
"type": "patreon"
}
],
"time": "2020-03-20T13:29:04+00:00"
}
],
@ -2183,6 +2333,10 @@
"throwable",
"whoops"
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.14.5"
},
"funding": [
{
"url": "https://github.com/denis-sokolov",
@ -2240,20 +2394,24 @@
"kint",
"php"
],
"support": {
"issues": "https://github.com/kint-php/kint/issues",
"source": "https://github.com/kint-php/kint/tree/1.1"
},
"time": "2017-01-15T14:23:43+00:00"
},
{
"name": "maximebf/debugbar",
"version": "v1.18.0",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/maximebf/php-debugbar.git",
"reference": "0d44b75f3b5d6d41ae83b79c7a4bceae7fbc78b6"
"reference": "ba0af68dd4316834701ecb30a00ce9604ced3ee9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/0d44b75f3b5d6d41ae83b79c7a4bceae7fbc78b6",
"reference": "0d44b75f3b5d6d41ae83b79c7a4bceae7fbc78b6",
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/ba0af68dd4316834701ecb30a00ce9604ced3ee9",
"reference": "ba0af68dd4316834701ecb30a00ce9604ced3ee9",
"shasum": ""
},
"require": {
@ -2273,7 +2431,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
"dev-master": "1.18-dev"
}
},
"autoload": {
@ -2302,7 +2460,11 @@
"debug",
"debugbar"
],
"time": "2021-12-27T18:49:48+00:00"
"support": {
"issues": "https://github.com/maximebf/php-debugbar/issues",
"source": "https://github.com/maximebf/php-debugbar/tree/v1.18.1"
},
"time": "2022-03-31T14:55:54+00:00"
},
{
"name": "psr/log",
@ -2349,6 +2511,9 @@
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/1.1.4"
},
"time": "2021-05-03T11:20:27+00:00"
},
{
@ -2415,6 +2580,9 @@
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -2495,6 +2663,9 @@
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -2581,6 +2752,9 @@
"debug",
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v4.4.44"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -2607,5 +2781,5 @@
"php": "^5.5|^7.0"
},
"platform-dev": [],
"plugin-api-version": "1.1.0"
"plugin-api-version": "2.1.0"
}

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,63 @@
Seznam anket pri mojih anketah z vklopljenimi mapami
*/
.moje_ankete_folders{
margin-top: -64px !important;
padding: 0 !important;
max-width: 100% !important;
#survey_list .survey_list_folders{
display: flex;
// Meni na levi
.left_menu{
flex-shrink: 0;
width: 320px;
height: 100vh;
padding: 32px 16px;
border-right: 1px $gray solid;
.title{
margin-bottom: 16px;
font-weight: 600;
font-size: 14px;
text-transform: uppercase;
}
.folder-item{
box-sizing: border-box;
height: 40px;
margin-bottom: 8px;
border: 1px solid $light-gray;
border-radius: 2px;
}
}
// Vsebina na desni
.right_content{
width: auto;
padding: 32px 16px;
.title{
margin-bottom: 24px;
font-weight: 600;
font-size: 16px;
}
}
}
}
#survey_list .div_sl_new.folders{
table#surveyList_new{

View File

@ -37,11 +37,13 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
private $vendorDir;
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
@ -57,6 +59,13 @@ class ClassLoader
private $missingClasses = array();
private $apcuPrefix;
private static $registeredLoaders = array();
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@ -300,6 +309,17 @@ class ClassLoader
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
@ -308,13 +328,17 @@ class ClassLoader
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
@ -323,6 +347,8 @@ class ClassLoader
return true;
}
return null;
}
/**
@ -367,6 +393,16 @@ class ClassLoader
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup

View File

@ -67,6 +67,7 @@ return array(
'CheckboxXml' => $baseDir . '/admin/survey/export/xmlClasses/Vprasanja/CheckboxXml.php',
'Common' => $baseDir . '/admin/survey/classes/class.Common.php',
'Composer\\CaBundle\\CaBundle' => $vendorDir . '/composer/ca-bundle/src/CaBundle.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'CrossRoad' => $baseDir . '/admin/survey/classes/tracking/CrossRoad.php',
'DatumLatex' => $baseDir . '/admin/survey/export/latexclasses/Vprasanja/DatumLatex.php',
'Demografija' => $baseDir . '/admin/survey/classes/class.Demografija.php',
@ -995,6 +996,7 @@ return array(
'SurveyKakovost' => $baseDir . '/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php',
'SurveyLanguageTechnology' => $baseDir . '/admin/survey/classes/class.SurveyLanguageTechnology.php',
'SurveyList' => $baseDir . '/admin/survey/classes/class.SurveyList.php',
'SurveyListFolders' => $baseDir . '/admin/survey/classes/class.SurveyListFolders.php',
'SurveyLog' => $baseDir . '/admin/survey/classes/log/class.SurveyLog.php',
'SurveyMFDPS' => $baseDir . '/admin/survey/modules/mod_MFDPS/class.SurveyMFDPS.php',
'SurveyMJU' => $baseDir . '/admin/survey/modules/mod_MJU/class.SurveyMJU.php',

View File

@ -22,13 +22,15 @@ class ComposerAutoloaderInit6b03163c371c5541881b55b762b8c779
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit6b03163c371c5541881b55b762b8c779', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit6b03163c371c5541881b55b762b8c779', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit6b03163c371c5541881b55b762b8c779::getInitializer($loader));
} else {

View File

@ -271,6 +271,7 @@ class ComposerStaticInit6b03163c371c5541881b55b762b8c779
'CheckboxXml' => __DIR__ . '/../..' . '/admin/survey/export/xmlClasses/Vprasanja/CheckboxXml.php',
'Common' => __DIR__ . '/../..' . '/admin/survey/classes/class.Common.php',
'Composer\\CaBundle\\CaBundle' => __DIR__ . '/..' . '/composer/ca-bundle/src/CaBundle.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'CrossRoad' => __DIR__ . '/../..' . '/admin/survey/classes/tracking/CrossRoad.php',
'DatumLatex' => __DIR__ . '/../..' . '/admin/survey/export/latexclasses/Vprasanja/DatumLatex.php',
'Demografija' => __DIR__ . '/../..' . '/admin/survey/classes/class.Demografija.php',
@ -1199,6 +1200,7 @@ class ComposerStaticInit6b03163c371c5541881b55b762b8c779
'SurveyKakovost' => __DIR__ . '/../..' . '/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php',
'SurveyLanguageTechnology' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyLanguageTechnology.php',
'SurveyList' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyList.php',
'SurveyListFolders' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyListFolders.php',
'SurveyLog' => __DIR__ . '/../..' . '/admin/survey/classes/log/class.SurveyLog.php',
'SurveyMFDPS' => __DIR__ . '/../..' . '/admin/survey/modules/mod_MFDPS/class.SurveyMFDPS.php',
'SurveyMJU' => __DIR__ . '/../..' . '/admin/survey/modules/mod_MJU/class.SurveyMJU.php',

View File

@ -1,4 +1,5 @@
[
{
"packages": [
{
"name": "composer/ca-bundle",
"version": "1.3.3",
@ -70,7 +71,8 @@
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
]
],
"install-path": "./ca-bundle"
},
{
"name": "fgrosse/phpasn1",
@ -147,35 +149,37 @@
"support": {
"issues": "https://github.com/fgrosse/PHPASN1/issues",
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0"
}
},
"install-path": "../fgrosse/phpasn1"
},
{
"name": "geoip2/geoip2",
"version": "v2.10.0",
"version_normalized": "2.10.0.0",
"version": "v2.13.0",
"version_normalized": "2.13.0.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/GeoIP2-php.git",
"reference": "419557cd21d9fe039721a83490701a58c8ce784a"
"url": "git@github.com:maxmind/GeoIP2-php.git",
"reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/419557cd21d9fe039721a83490701a58c8ce784a",
"reference": "419557cd21d9fe039721a83490701a58c8ce784a",
"url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/6a41d8fbd6b90052bc34dff3b4252d0f88067b23",
"reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23",
"shasum": ""
},
"require": {
"ext-json": "*",
"maxmind-db/reader": "~1.5",
"maxmind/web-service-common": "~0.6",
"php": ">=5.6"
"maxmind-db/reader": "~1.8",
"maxmind/web-service-common": "~0.8",
"php": ">=7.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "5.*",
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"time": "2019-12-12T18:48:39+00:00",
"time": "2022-08-05T20:32:58+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -202,7 +206,8 @@
"geoip2",
"geolocation",
"maxmind"
]
],
"install-path": "../geoip2/geoip2"
},
{
"name": "guzzlehttp/guzzle",
@ -319,21 +324,22 @@
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
"type": "tidelift"
}
]
],
"install-path": "../guzzlehttp/guzzle"
},
{
"name": "guzzlehttp/promises",
"version": "1.5.1",
"version_normalized": "1.5.1.0",
"version": "1.5.2",
"version_normalized": "1.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
"reference": "b94b2807d85443f9719887892882d0329d1e2598"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
"reference": "b94b2807d85443f9719887892882d0329d1e2598",
"shasum": ""
},
"require": {
@ -342,7 +348,7 @@
"require-dev": {
"symfony/phpunit-bridge": "^4.4 || ^5.1"
},
"time": "2021-10-22T20:56:57+00:00",
"time": "2022-08-28T14:55:35+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -351,12 +357,12 @@
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -390,7 +396,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/1.5.1"
"source": "https://github.com/guzzle/promises/tree/1.5.2"
},
"funding": [
{
@ -405,7 +411,8 @@
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
"type": "tidelift"
}
]
],
"install-path": "../guzzlehttp/promises"
},
{
"name": "guzzlehttp/psr7",
@ -517,34 +524,36 @@
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
"type": "tidelift"
}
]
],
"install-path": "../guzzlehttp/psr7"
},
{
"name": "maxmind-db/reader",
"version": "v1.6.0",
"version_normalized": "1.6.0.0",
"version": "v1.11.0",
"version_normalized": "1.11.0.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4"
"reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/febd4920bf17c1da84cef58e56a8227dfb37fbe4",
"reference": "febd4920bf17c1da84cef58e56a8227dfb37fbe4",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/b1f3c0699525336d09cc5161a2861268d9f2ae5b",
"reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b",
"shasum": ""
},
"require": {
"php": ">=5.6"
"php": ">=7.2"
},
"conflict": {
"ext-maxminddb": "<1.6.0,>=2.0.0"
"ext-maxminddb": "<1.10.1,>=2.0.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"friendsofphp/php-cs-fixer": "3.*",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpcov": "^3.0",
"phpunit/phpunit": "5.*",
"phpstan/phpstan": "*",
"phpunit/phpcov": ">=6.0.0",
"phpunit/phpunit": ">=8.0.0,<10.0.0",
"squizlabs/php_codesniffer": "3.*"
},
"suggest": {
@ -552,7 +561,7 @@
"ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
"ext-maxminddb": "A C-based database decoder that provides significantly faster lookups"
},
"time": "2019-12-19T22:59:03+00:00",
"time": "2021-10-18T15:23:10+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -579,35 +588,41 @@
"geoip2",
"geolocation",
"maxmind"
]
],
"support": {
"issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues",
"source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.11.0"
},
"install-path": "../maxmind-db/reader"
},
{
"name": "maxmind/web-service-common",
"version": "v0.7.0",
"version_normalized": "0.7.0.0",
"version": "v0.9.0",
"version_normalized": "0.9.0.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/web-service-common-php.git",
"reference": "74c996c218ada5c639c8c2f076756e059f5552fc"
"reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/74c996c218ada5c639c8c2f076756e059f5552fc",
"reference": "74c996c218ada5c639c8c2f076756e059f5552fc",
"url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/4dc5a3e8df38aea4ca3b1096cee3a038094e9b53",
"reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.0.3",
"ext-curl": "*",
"ext-json": "*",
"php": ">=5.6"
"php": ">=7.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0",
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"time": "2020-05-06T14:07:26+00:00",
"time": "2022-03-28T17:43:20+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -627,7 +642,12 @@
}
],
"description": "Internal MaxMind Web Service API",
"homepage": "https://github.com/maxmind/web-service-common-php"
"homepage": "https://github.com/maxmind/web-service-common-php",
"support": {
"issues": "https://github.com/maxmind/web-service-common-php/issues",
"source": "https://github.com/maxmind/web-service-common-php/tree/v0.9.0"
},
"install-path": "../maxmind/web-service-common"
},
{
"name": "minishlink/web-push",
@ -685,7 +705,8 @@
"notifications",
"push",
"web"
]
],
"install-path": "../minishlink/web-push"
},
{
"name": "paragonie/random_compat",
@ -732,21 +753,22 @@
"polyfill",
"pseudorandom",
"random"
]
],
"install-path": "../paragonie/random_compat"
},
{
"name": "paragonie/sodium_compat",
"version": "v1.17.1",
"version_normalized": "1.17.1.0",
"version": "v1.18.0",
"version_normalized": "1.18.0.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/sodium_compat.git",
"reference": "ac994053faac18d386328c91c7900f930acadf1e"
"reference": "906e0b925895d3a5941eda25f371fbafb3cbc22f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/ac994053faac18d386328c91c7900f930acadf1e",
"reference": "ac994053faac18d386328c91c7900f930acadf1e",
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/906e0b925895d3a5941eda25f371fbafb3cbc22f",
"reference": "906e0b925895d3a5941eda25f371fbafb3cbc22f",
"shasum": ""
},
"require": {
@ -760,7 +782,7 @@
"ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.",
"ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security."
},
"time": "2022-03-23T19:32:04+00:00",
"time": "2022-09-13T20:54:27+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -819,8 +841,9 @@
],
"support": {
"issues": "https://github.com/paragonie/sodium_compat/issues",
"source": "https://github.com/paragonie/sodium_compat/tree/v1.17.1"
}
"source": "https://github.com/paragonie/sodium_compat/tree/v1.18.0"
},
"install-path": "../paragonie/sodium_compat"
},
{
"name": "paypal/paypal-checkout-sdk",
@ -874,7 +897,8 @@
],
"support": {
"source": "https://github.com/paypal/Checkout-PHP-SDK/tree/1.0.2"
}
},
"install-path": "../paypal/paypal-checkout-sdk"
},
{
"name": "paypal/paypalhttp",
@ -919,21 +943,22 @@
"support": {
"issues": "https://github.com/paypal/paypalhttp_php/issues",
"source": "https://github.com/paypal/paypalhttp_php/tree/1.0.1"
}
},
"install-path": "../paypal/paypalhttp"
},
{
"name": "phpmailer/phpmailer",
"version": "v6.6.3",
"version_normalized": "6.6.3.0",
"version": "v6.6.4",
"version_normalized": "6.6.4.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "9400f305a898f194caff5521f64e5dfa926626f3"
"reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9400f305a898f194caff5521f64e5dfa926626f3",
"reference": "9400f305a898f194caff5521f64e5dfa926626f3",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"shasum": ""
},
"require": {
@ -960,7 +985,7 @@
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
},
"time": "2022-06-20T09:21:02+00:00",
"time": "2022-08-22T09:22:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -992,14 +1017,15 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.3"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4"
},
"funding": [
{
"url": "https://github.com/Synchro",
"type": "github"
}
]
],
"install-path": "../phpmailer/phpmailer"
},
{
"name": "psr/http-message",
@ -1051,7 +1077,8 @@
"psr-7",
"request",
"response"
]
],
"install-path": "../psr/http-message"
},
{
"name": "ralouphie/getallheaders",
@ -1093,30 +1120,31 @@
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders."
"description": "A polyfill for getallheaders.",
"install-path": "../ralouphie/getallheaders"
},
{
"name": "sonata-project/google-authenticator",
"version": "2.2.0",
"version_normalized": "2.2.0.0",
"version": "2.3.1",
"version_normalized": "2.3.1.0",
"source": {
"type": "git",
"url": "https://github.com/sonata-project/GoogleAuthenticator.git",
"reference": "feda53899b26af24e3db2fe7a3e5f053ca483762"
"reference": "71a4189228f93a9662574dc8c65e77ef55061b59"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762",
"reference": "feda53899b26af24e3db2fe7a3e5f053ca483762",
"url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/71a4189228f93a9662574dc8c65e77ef55061b59",
"reference": "71a4189228f93a9662574dc8c65e77ef55061b59",
"shasum": ""
},
"require": {
"php": "^7.1"
"php": "^7.3 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.0"
"symfony/phpunit-bridge": "^5.1.8"
},
"time": "2018-07-18T22:08:02+00:00",
"time": "2021-02-15T19:23:18+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -1135,6 +1163,10 @@
"MIT"
],
"authors": [
{
"name": "Thomas Rabaix",
"email": "thomas.rabaix@gmail.com"
},
{
"name": "Christian Stocker",
"email": "me@chregu.tv"
@ -1142,10 +1174,6 @@
{
"name": "Andre DeMarre",
"homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989"
},
{
"name": "Thomas Rabaix",
"email": "thomas.rabaix@gmail.com"
}
],
"description": "Library to integrate Google Authenticator into a PHP project",
@ -1153,7 +1181,30 @@
"keywords": [
"google authenticator"
],
"abandoned": true
"support": {
"issues": "https://github.com/sonata-project/GoogleAuthenticator/issues",
"source": "https://github.com/sonata-project/GoogleAuthenticator/tree/2.3.1"
},
"funding": [
{
"url": "https://github.com/OskarStark",
"type": "github"
},
{
"url": "https://github.com/VincentLanglet",
"type": "github"
},
{
"url": "https://github.com/core23",
"type": "github"
},
{
"url": "https://github.com/wbloszyk",
"type": "github"
}
],
"abandoned": true,
"install-path": "../sonata-project/google-authenticator"
},
{
"name": "spomky-labs/base64url",
@ -1206,7 +1257,8 @@
"rfc4648",
"safe",
"url"
]
],
"install-path": "../spomky-labs/base64url"
},
{
"name": "stripe/stripe-php",
@ -1268,7 +1320,8 @@
"support": {
"issues": "https://github.com/stripe/stripe-php/issues",
"source": "https://github.com/stripe/stripe-php/tree/v7.128.0"
}
},
"install-path": "../stripe/stripe-php"
},
{
"name": "symfony/polyfill-intl-idn",
@ -1357,7 +1410,8 @@
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
],
"install-path": "../symfony/polyfill-intl-idn"
},
{
"name": "symfony/polyfill-intl-normalizer",
@ -1443,7 +1497,8 @@
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
],
"install-path": "../symfony/polyfill-intl-normalizer"
},
{
"name": "symfony/polyfill-php72",
@ -1521,7 +1576,8 @@
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
],
"install-path": "../symfony/polyfill-php72"
},
{
"name": "web-token/jwt-core",
@ -1598,7 +1654,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-core"
},
{
"name": "web-token/jwt-key-mgmt",
@ -1677,7 +1734,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-key-mgmt"
},
{
"name": "web-token/jwt-signature",
@ -1759,7 +1817,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-signature"
},
{
"name": "web-token/jwt-signature-algorithm-ecdsa",
@ -1828,7 +1887,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-signature-algorithm-ecdsa"
},
{
"name": "web-token/jwt-signature-algorithm-eddsa",
@ -1897,7 +1957,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-signature-algorithm-eddsa"
},
{
"name": "web-token/jwt-signature-algorithm-hmac",
@ -1966,7 +2027,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-signature-algorithm-hmac"
},
{
"name": "web-token/jwt-signature-algorithm-none",
@ -2035,7 +2097,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-signature-algorithm-none"
},
{
"name": "web-token/jwt-signature-algorithm-rsa",
@ -2104,7 +2167,8 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-signature-algorithm-rsa"
},
{
"name": "web-token/jwt-util-ecc",
@ -2175,6 +2239,10 @@
"jwa",
"jwt",
"symfony"
]
],
"install-path": "../web-token/jwt-util-ecc"
}
],
"dev": false,
"dev-package-names": []
}
]

View File

@ -5,7 +5,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'bdcf00e53774eee9bdd53d6213a3df5e7fd37b05',
'reference' => 'c45926b660b68b00d59b86ec692d308c0bb247d1',
'name' => '1ka/1ka.si',
'dev' => false,
),
@ -16,16 +16,16 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'bdcf00e53774eee9bdd53d6213a3df5e7fd37b05',
'reference' => 'c45926b660b68b00d59b86ec692d308c0bb247d1',
'dev_requirement' => false,
),
'composer/ca-bundle' => array(
'pretty_version' => '1.3.2',
'version' => '1.3.2.0',
'pretty_version' => '1.3.3',
'version' => '1.3.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/./ca-bundle',
'aliases' => array(),
'reference' => 'fd5dd441932a7e10ca6e5b490e272d34c8430640',
'reference' => '30897edbfb15e784fe55587b4f73ceefd3c4d98c',
'dev_requirement' => false,
),
'fgrosse/phpasn1' => array(
@ -38,12 +38,12 @@
'dev_requirement' => false,
),
'geoip2/geoip2' => array(
'pretty_version' => 'v2.12.2',
'version' => '2.12.2.0',
'pretty_version' => 'v2.13.0',
'version' => '2.13.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../geoip2/geoip2',
'aliases' => array(),
'reference' => '83adb44ac4b9553d36b579a14673ed124583082f',
'reference' => '6a41d8fbd6b90052bc34dff3b4252d0f88067b23',
'dev_requirement' => false,
),
'guzzlehttp/guzzle' => array(
@ -56,12 +56,12 @@
'dev_requirement' => false,
),
'guzzlehttp/promises' => array(
'pretty_version' => '1.5.1',
'version' => '1.5.1.0',
'pretty_version' => '1.5.2',
'version' => '1.5.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/promises',
'aliases' => array(),
'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da',
'reference' => 'b94b2807d85443f9719887892882d0329d1e2598',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
@ -110,12 +110,12 @@
'dev_requirement' => false,
),
'paragonie/sodium_compat' => array(
'pretty_version' => 'v1.17.1',
'version' => '1.17.1.0',
'pretty_version' => 'v1.18.0',
'version' => '1.18.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/sodium_compat',
'aliases' => array(),
'reference' => 'ac994053faac18d386328c91c7900f930acadf1e',
'reference' => '906e0b925895d3a5941eda25f371fbafb3cbc22f',
'dev_requirement' => false,
),
'paypal/paypal-checkout-sdk' => array(
@ -137,12 +137,12 @@
'dev_requirement' => false,
),
'phpmailer/phpmailer' => array(
'pretty_version' => 'v6.6.3',
'version' => '6.6.3.0',
'pretty_version' => 'v6.6.4',
'version' => '6.6.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
'aliases' => array(),
'reference' => '9400f305a898f194caff5521f64e5dfa926626f3',
'reference' => 'a94fdebaea6bd17f51be0c2373ab80d3d681269b',
'dev_requirement' => false,
),
'psr/http-message' => array(

View File

@ -1,6 +1,47 @@
CHANGELOG
=========
2.13.0 (2022-08-05)
-------------------
* The model class names are no longer constructed by concatenating strings.
This change was made to improve support for tools like PHP-Scoper.
Reported by Andrew Mead. GitHub #194.
* Box 4.0.1 is now used to generate the `geoip2.phar` file.
2.12.2 (2021-11-30)
-------------------
* The `geoip2.phar` now works when included from another directory.
Reported by Eduardo Ruiz. GitHub #179.
2.12.1 (2021-11-23)
-------------------
* The `geoip2.phar` included in 2.12.0 would only work in CLI applications.
This was due to a change in Box 3.x. The Phar should now work in all
applications. This release only affects users of the Phar file.
2.12.0 (2021-11-18)
-------------------
* Support for mobile country code (MCC) and mobile network codes (MNC) was
added for the GeoIP2 ISP and Enterprise databases as well as the GeoIP2
City and Insights web services. `$mobileCountryCode` and
`$mobileNetworkCode` properties were added to `GeoIp2\Model\Isp`
for the GeoIP2 ISP database and `GeoIp2\Record\Traits` for the Enterprise
database and the GeoIP2 City and Insights web services. We expect this data
to be available by late January, 2022.
* `geoip2.phar` is now generated with Box 3.x.
2.11.0 (2020-10-01)
-------------------
* IMPORTANT: PHP 7.2 or greater is now required.
* Added the `isResidentialProxy` property to `GeoIp2\Model\AnonymousIP` and
`GeoIp2\Record\Traits`.
* Additional type hints have been added.
2.10.0 (2019-12-12)
-------------------

View File

@ -2,11 +2,9 @@
## Description ##
This package provides an API for the GeoIP2
[web services](https://dev.maxmind.com/geoip/geoip2/web-services) and
[databases](https://dev.maxmind.com/geoip/geoip2/downloadable). The API also
works with the free
[GeoLite2 databases](https://dev.maxmind.com/geoip/geoip2/geolite2/).
This package provides an API for the GeoIP2 and GeoLite2
[web services](https://dev.maxmind.com/geoip/docs/web-services?lang=en) and
[databases](https://dev.maxmind.com/geoip/docs/databases?lang=en).
## Install via Composer ##
@ -26,7 +24,7 @@ You should now have the file `composer.phar` in your project directory.
Run in your project root:
```
```sh
php composer.phar require geoip2/geoip2:~2.0
```
@ -260,18 +258,51 @@ print($record->network . "\n"); // '128.101.101.101/32'
```
## Database Updates ##
You can keep your databases up to date with our
[GeoIP Update program](https://github.com/maxmind/geoipupdate/releases).
[Learn more about GeoIP Update on our developer
portal.](https://dev.maxmind.com/geoip/updating-databases?lang=en)
There is also a third-party tool for updating databases using PHP and
Composer. MaxMind does not offer support for this tool or maintain it.
[Learn more about the Geoip2 Update tool for PHP and Composer on its
GitHub page.](https://github.com/tronovav/geoip2-update)
## Web Service Client ##
### Usage ###
To use this API, you must create a new `\GeoIp2\WebService\Client`
object with your `$accountId` and `$licenseKey`, then you call the method
corresponding to a specific end point, passing it the IP address you want to
look up.
object with your `$accountId` and `$licenseKey`:
If the request succeeds, the method call will return a model class for the end
point you called. This model in turn contains multiple record classes, each of
which represents part of the data returned by the web service.
```php
$client = new Client(42, 'abcdef123456');
```
You may also call the constructor with additional arguments. The third argument
specifies the language preferences when using the `->name` method on the model
classes that this client creates. The fourth argument is additional options
such as `host` and `timeout`.
For instance, to call the GeoLite2 web service instead of the GeoIP2 web
service:
```php
$client = new Client(42, 'abcdef123456', ['en'], ['host' => 'geolite.info']);
```
After creating the client, you may now call the method corresponding to a
specific endpoint with the IP address to look up, e.g.:
```php
$record = $client->city('128.101.101.101');
```
If the request succeeds, the method call will return a model class for the
endpoint you called. This model in turn contains multiple record classes, each
of which represents part of the data returned by the web service.
If there is an error, a structured exception is thrown.
@ -286,7 +317,9 @@ use GeoIp2\WebService\Client;
// This creates a Client object that can be reused across requests.
// Replace "42" with your account ID and "license_key" with your license
// key.
// key. Set the "host" to "geolite.info" in the fourth argument options
// array to use the GeoLite2 web service instead of the GeoIP2 web
// service.
$client = new Client(42, 'abcdef123456');
// Replace "city" with the method corresponding to the web service that
@ -336,7 +369,7 @@ Because of these factors, it is possible for any end point to return a record
where some or all of the attributes are unpopulated.
See the
[GeoIP2 Precision web service docs](https://dev.maxmind.com/geoip/geoip2/web-services)
[GeoIP2 web service docs](https://dev.maxmind.com/geoip/docs/web-services?lang=en)
for details on what data each end point may return.
The only piece of data which is always returned is the `ipAddress`
@ -386,7 +419,7 @@ to the client API, please see
## Requirements ##
This library requires PHP 5.6 or greater.
This library requires PHP 7.2 or greater.
This library also relies on the [MaxMind DB Reader](https://github.com/maxmind/MaxMind-DB-Reader-php).
@ -404,6 +437,6 @@ The GeoIP2 PHP API uses [Semantic Versioning](https://semver.org/).
## Copyright and License ##
This software is Copyright (c) 2013-2019 by MaxMind, Inc.
This software is Copyright (c) 2013-2020 by MaxMind, Inc.
This is free software, licensed under the Apache License, Version 2.0.

View File

@ -13,15 +13,16 @@
}
],
"require": {
"maxmind-db/reader": "~1.5",
"maxmind/web-service-common": "~0.6",
"php": ">=5.6",
"maxmind-db/reader": "~1.8",
"maxmind/web-service-common": "~0.8",
"php": ">=7.2",
"ext-json": "*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "5.*",
"squizlabs/php_codesniffer": "3.*"
"friendsofphp/php-cs-fixer": "3.*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*",
"phpstan/phpstan": "*"
},
"autoload": {
"psr-4": {

View File

@ -10,7 +10,8 @@ $reader = new Reader('GeoIP2-City.mmdb');
$count = 500000;
$startTime = microtime(true);
for ($i = 0; $i < $count; ++$i) {
$ip = long2ip(rand(0, pow(2, 32) - 1));
$ip = long2ip(rand(0, 2 ** 32 - 1));
try {
$t = $reader->city($ip);
} catch (\GeoIp2\Exception\AddressNotFoundException $e) {

View File

@ -1,8 +1,19 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Database;
use GeoIp2\Exception\AddressNotFoundException;
use GeoIp2\Model\AbstractModel;
use GeoIp2\Model\AnonymousIp;
use GeoIp2\Model\Asn;
use GeoIp2\Model\City;
use GeoIp2\Model\ConnectionType;
use GeoIp2\Model\Country;
use GeoIp2\Model\Domain;
use GeoIp2\Model\Enterprise;
use GeoIp2\Model\Isp;
use GeoIp2\ProviderInterface;
use MaxMind\Db\Reader as DbReader;
use MaxMind\Db\Reader\InvalidDatabaseException;
@ -33,8 +44,19 @@ use MaxMind\Db\Reader\InvalidDatabaseException;
*/
class Reader implements ProviderInterface
{
/**
* @var DbReader
*/
private $dbReader;
/**
* @var string
*/
private $dbType;
/**
* @var array<string>
*/
private $locales;
/**
@ -48,8 +70,8 @@ class Reader implements ProviderInterface
* is corrupt or invalid
*/
public function __construct(
$filename,
$locales = ['en']
string $filename,
array $locales = ['en']
) {
$this->dbReader = new DbReader($filename);
$this->dbType = $this->dbReader->metadata()->databaseType;
@ -65,12 +87,11 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\City
*/
public function city($ipAddress)
public function city(string $ipAddress): City
{
return $this->modelFor('City', 'City', $ipAddress);
// @phpstan-ignore-next-line
return $this->modelFor(City::class, 'City', $ipAddress);
}
/**
@ -82,12 +103,11 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\Country
*/
public function country($ipAddress)
public function country(string $ipAddress): Country
{
return $this->modelFor('Country', 'Country', $ipAddress);
// @phpstan-ignore-next-line
return $this->modelFor(Country::class, 'Country', $ipAddress);
}
/**
@ -99,13 +119,12 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\AnonymousIp
*/
public function anonymousIp($ipAddress)
public function anonymousIp(string $ipAddress): AnonymousIp
{
// @phpstan-ignore-next-line
return $this->flatModelFor(
'AnonymousIp',
AnonymousIp::class,
'GeoIP2-Anonymous-IP',
$ipAddress
);
@ -120,13 +139,12 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\Asn
*/
public function asn($ipAddress)
public function asn(string $ipAddress): Asn
{
// @phpstan-ignore-next-line
return $this->flatModelFor(
'Asn',
Asn::class,
'GeoLite2-ASN',
$ipAddress
);
@ -141,13 +159,12 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\ConnectionType
*/
public function connectionType($ipAddress)
public function connectionType(string $ipAddress): ConnectionType
{
// @phpstan-ignore-next-line
return $this->flatModelFor(
'ConnectionType',
ConnectionType::class,
'GeoIP2-Connection-Type',
$ipAddress
);
@ -162,13 +179,12 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\Domain
*/
public function domain($ipAddress)
public function domain(string $ipAddress): Domain
{
// @phpstan-ignore-next-line
return $this->flatModelFor(
'Domain',
Domain::class,
'GeoIP2-Domain',
$ipAddress
);
@ -183,12 +199,11 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\Enterprise
*/
public function enterprise($ipAddress)
public function enterprise(string $ipAddress): Enterprise
{
return $this->modelFor('Enterprise', 'Enterprise', $ipAddress);
// @phpstan-ignore-next-line
return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress);
}
/**
@ -200,50 +215,47 @@ class Reader implements ProviderInterface
* not in the database
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
* is corrupt or invalid
*
* @return \GeoIp2\Model\Isp
*/
public function isp($ipAddress)
public function isp(string $ipAddress): Isp
{
// @phpstan-ignore-next-line
return $this->flatModelFor(
'Isp',
Isp::class,
'GeoIP2-ISP',
$ipAddress
);
}
private function modelFor($class, $type, $ipAddress)
private function modelFor(string $class, string $type, string $ipAddress): AbstractModel
{
list($record, $prefixLen) = $this->getRecord($class, $type, $ipAddress);
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
$record['traits']['ip_address'] = $ipAddress;
$record['traits']['prefix_len'] = $prefixLen;
$class = 'GeoIp2\\Model\\' . $class;
return new $class($record, $this->locales);
}
private function flatModelFor($class, $type, $ipAddress)
private function flatModelFor(string $class, string $type, string $ipAddress): AbstractModel
{
list($record, $prefixLen) = $this->getRecord($class, $type, $ipAddress);
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
$record['ip_address'] = $ipAddress;
$record['prefix_len'] = $prefixLen;
$class = 'GeoIp2\\Model\\' . $class;
return new $class($record);
}
private function getRecord($class, $type, $ipAddress)
private function getRecord(string $class, string $type, string $ipAddress): array
{
if (strpos($this->dbType, $type) === false) {
$method = lcfirst($class);
$method = lcfirst((new \ReflectionClass($class))->getShortName());
throw new \BadMethodCallException(
"The $method method cannot be used to open a {$this->dbType} database"
);
}
list($record, $prefixLen) = $this->dbReader->getWithPrefixLen($ipAddress);
[$record, $prefixLen] = $this->dbReader->getWithPrefixLen($ipAddress);
if ($record === null) {
throw new AddressNotFoundException(
"The address $ipAddress is not in the database."
@ -272,7 +284,7 @@ class Reader implements ProviderInterface
*
* @return \MaxMind\Db\Reader\Metadata object for the database
*/
public function metadata()
public function metadata(): DbReader\Metadata
{
return $this->dbReader->metadata();
}
@ -280,7 +292,7 @@ class Reader implements ProviderInterface
/**
* Closes the GeoIP2 database and returns the resources to the system.
*/
public function close()
public function close(): void
{
$this->dbReader->close();
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
@ -9,13 +11,15 @@ class HttpException extends GeoIp2Exception
{
/**
* The URI queried.
*
* @var string
*/
public $uri;
public function __construct(
$message,
$httpStatus,
$uri,
string $message,
int $httpStatus,
string $uri,
\Exception $previous = null
) {
$this->uri = $uri;

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
@ -10,14 +12,16 @@ class InvalidRequestException extends HttpException
{
/**
* The code returned by the MaxMind web service.
*
* @var string
*/
public $error;
public function __construct(
$message,
$error,
$httpStatus,
$uri,
string $message,
string $error,
int $httpStatus,
string $uri,
\Exception $previous = null
) {
$this->error = $error;

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
@ -7,14 +9,15 @@ namespace GeoIp2\Model;
*/
abstract class AbstractModel implements \JsonSerializable
{
/**
* @var array<string, mixed>
*/
protected $raw;
/**
* @ignore
*
* @param mixed $raw
*/
public function __construct($raw)
public function __construct(array $raw)
{
$this->raw = $raw;
}
@ -22,9 +25,9 @@ abstract class AbstractModel implements \JsonSerializable
/**
* @ignore
*
* @param mixed $field
* @return mixed
*/
protected function get($field)
protected function get(string $field)
{
if (isset($this->raw[$field])) {
return $this->raw[$field];
@ -39,12 +42,12 @@ abstract class AbstractModel implements \JsonSerializable
/**
* @ignore
*
* @param mixed $attr
* @return mixed
*/
public function __get($attr)
public function __get(string $attr)
{
if ($attr !== 'instance' && property_exists($this, $attr)) {
return $this->$attr;
return $this->{$attr};
}
throw new \RuntimeException("Unknown attribute: $attr");
@ -52,15 +55,13 @@ abstract class AbstractModel implements \JsonSerializable
/**
* @ignore
*
* @param mixed $attr
*/
public function __isset($attr)
public function __isset(string $attr): bool
{
return $attr !== 'instance' && isset($this->$attr);
return $attr !== 'instance' && isset($this->{$attr});
}
public function jsonSerialize()
public function jsonSerialize(): array
{
return $this->raw;
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
@ -17,6 +19,8 @@ use GeoIp2\Util;
* to a hosting or VPN provider (see description of isAnonymousVpn property).
* @property-read bool $isPublicProxy This is true if the IP address belongs to
* a public proxy.
* @property-read bool $isResidentialProxy This is true if the IP address is
* on a suspected anonymizing network and belongs to a residential ISP.
* @property-read bool $isTorExitNode This is true if the IP address is a Tor
* exit node.
* @property-read string $ipAddress The IP address that the data in the model is
@ -27,20 +31,50 @@ use GeoIp2\Util;
*/
class AnonymousIp extends AbstractModel
{
/**
* @var bool
*/
protected $isAnonymous;
/**
* @var bool
*/
protected $isAnonymousVpn;
/**
* @var bool
*/
protected $isHostingProvider;
/**
* @var bool
*/
protected $isPublicProxy;
/**
* @var bool
*/
protected $isResidentialProxy;
/**
* @var bool
*/
protected $isTorExitNode;
/**
* @var string
*/
protected $ipAddress;
/**
* @var string
*/
protected $network;
/**
* @ignore
*
* @param mixed $raw
*/
public function __construct($raw)
public function __construct(array $raw)
{
parent::__construct($raw);
@ -48,6 +82,7 @@ class AnonymousIp extends AbstractModel
$this->isAnonymousVpn = $this->get('is_anonymous_vpn');
$this->isHostingProvider = $this->get('is_hosting_provider');
$this->isPublicProxy = $this->get('is_public_proxy');
$this->isResidentialProxy = $this->get('is_residential_proxy');
$this->isTorExitNode = $this->get('is_tor_exit_node');
$ipAddress = $this->get('ip_address');
$this->ipAddress = $ipAddress;

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
@ -20,17 +22,30 @@ use GeoIp2\Util;
*/
class Asn extends AbstractModel
{
/**
* @var int|null
*/
protected $autonomousSystemNumber;
/**
* @var string|null
*/
protected $autonomousSystemOrganization;
/**
* @var string
*/
protected $ipAddress;
/**
* @var string
*/
protected $network;
/**
* @ignore
*
* @param mixed $raw
*/
public function __construct($raw)
public function __construct(array $raw)
{
parent::__construct($raw);
$this->autonomousSystemNumber = $this->get('autonomous_system_number');

View File

@ -1,13 +1,15 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
* Model class for the data returned by GeoIP2 City web service and database.
* Model class for the data returned by City Plus web service and City
* database.
*
* The only difference between the City and Insights model classes is which
* fields in each record may be populated. See
* https://dev.maxmind.com/geoip/geoip2/web-services for more details.
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more
* details.
*
* @property-read \GeoIp2\Record\City $city City data for the requested IP
* address.
@ -31,28 +33,36 @@ class City extends Country
{
/**
* @ignore
*
* @var \GeoIp2\Record\City
*/
protected $city;
/**
* @ignore
*
* @var \GeoIp2\Record\Location
*/
protected $location;
/**
* @ignore
*
* @var \GeoIp2\Record\Postal
*/
protected $postal;
/**
* @ignore
*
* @var array<\GeoIp2\Record\Subdivision>
*/
protected $subdivisions = [];
/**
* @ignore
*
* @param mixed $raw
* @param mixed $locales
*/
public function __construct($raw, $locales = ['en'])
public function __construct(array $raw, array $locales = ['en'])
{
parent::__construct($raw, $locales);
@ -63,29 +73,28 @@ class City extends Country
$this->createSubdivisions($raw, $locales);
}
private function createSubdivisions($raw, $locales)
private function createSubdivisions(array $raw, array $locales): void
{
if (!isset($raw['subdivisions'])) {
return;
}
foreach ($raw['subdivisions'] as $sub) {
array_push(
$this->subdivisions,
$this->subdivisions[] =
new \GeoIp2\Record\Subdivision($sub, $locales)
);
;
}
}
/**
* @ignore
*
* @param mixed $attr
* @return mixed
*/
public function __get($attr)
public function __get(string $attr)
{
if ($attr === 'mostSpecificSubdivision') {
return $this->$attr();
return $this->{$attr}();
}
return parent::__get($attr);
@ -93,10 +102,8 @@ class City extends Country
/**
* @ignore
*
* @param mixed $attr
*/
public function __isset($attr)
public function __isset(string $attr): bool
{
if ($attr === 'mostSpecificSubdivision') {
// We always return a mostSpecificSubdivision, even if it is the
@ -107,7 +114,7 @@ class City extends Country
return parent::__isset($attr);
}
private function mostSpecificSubdivision()
private function mostSpecificSubdivision(): \GeoIp2\Record\Subdivision
{
return empty($this->subdivisions) ?
new \GeoIp2\Record\Subdivision([], $this->locales) :

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
@ -18,16 +20,25 @@ use GeoIp2\Util;
*/
class ConnectionType extends AbstractModel
{
/**
* @var string|null
*/
protected $connectionType;
/**
* @var string
*/
protected $ipAddress;
/**
* @var string
*/
protected $network;
/**
* @ignore
*
* @param mixed $raw
*/
public function __construct($raw)
public function __construct(array $raw)
{
parent::__construct($raw);

View File

@ -1,13 +1,13 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
* Model class for the data returned by GeoIP2 Country web service and database.
*
* The only difference between the City and Insights model classes is which
* fields in each record may be populated. See
* https://dev.maxmind.com/geoip/geoip2/web-services for more details.
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more details.
*
* @property-read \GeoIp2\Record\Continent $continent Continent data for the
* requested IP address.
@ -26,24 +26,49 @@ namespace GeoIp2\Model;
* the represented country differs from the country.
* @property-read \GeoIp2\Record\Traits $traits Data for the traits of the
* requested IP address.
* @property-read array $raw The raw data from the web service.
*/
class Country extends AbstractModel
{
/**
* @var \GeoIp2\Record\Continent
*/
protected $continent;
/**
* @var \GeoIp2\Record\Country
*/
protected $country;
/**
* @var array<string>
*/
protected $locales;
/**
* @var \GeoIp2\Record\MaxMind
*/
protected $maxmind;
/**
* @var \GeoIp2\Record\Country
*/
protected $registeredCountry;
/**
* @var \GeoIp2\Record\RepresentedCountry
*/
protected $representedCountry;
/**
* @var \GeoIp2\Record\Traits
*/
protected $traits;
/**
* @ignore
*
* @param mixed $raw
* @param mixed $locales
*/
public function __construct($raw, $locales = ['en'])
public function __construct(array $raw, array $locales = ['en'])
{
parent::__construct($raw);

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
@ -18,16 +20,25 @@ use GeoIp2\Util;
*/
class Domain extends AbstractModel
{
/**
* @var string|null
*/
protected $domain;
/**
* @var string
*/
protected $ipAddress;
/**
* @var string
*/
protected $network;
/**
* @ignore
*
* @param mixed $raw
*/
public function __construct($raw)
public function __construct(array $raw)
{
parent::__construct($raw);

View File

@ -1,13 +1,14 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
* Model class for the data returned by GeoIP2 Enterprise database lookups.
*
* The only difference between the City and Enterprise model classes is which
* fields in each record may be populated. See
* https://dev.maxmind.com/geoip/geoip2/web-services for more details.
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more
* details.
*/
class Enterprise extends City
{

View File

@ -1,13 +1,14 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
* Model class for the data returned by GeoIP2 Precision: Insights web service.
* Model class for the data returned by GeoIP2 Insights web service.
*
* The only difference between the City and Insights model classes is which
* fields in each record may be populated. See
* https://dev.maxmind.com/geoip/geoip2/web-services for more details.
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for
* more details.
*/
class Insights extends City
{

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
@ -14,6 +16,12 @@ use GeoIp2\Util;
* address.
* @property-read string|null $isp The name of the ISP associated with the IP
* address.
* @property-read string|null $mobileCountryCode The [mobile country code
* (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
* the IP address and ISP.
* @property-read string|null $mobileNetworkCode The [mobile network code
* (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
* the IP address and ISP.
* @property-read string|null $organization The name of the organization associated
* with the IP address.
* @property-read string $ipAddress The IP address that the data in the model is
@ -24,25 +32,58 @@ use GeoIp2\Util;
*/
class Isp extends AbstractModel
{
/**
* @var int|null
*/
protected $autonomousSystemNumber;
/**
* @var string|null
*/
protected $autonomousSystemOrganization;
/**
* @var string|null
*/
protected $isp;
/**
* @var string|null
*/
protected $mobileCountryCode;
/**
* @var string|null
*/
protected $mobileNetworkCode;
/**
* @var string|null
*/
protected $organization;
/**
* @var string
*/
protected $ipAddress;
/**
* @var string
*/
protected $network;
/**
* @ignore
*
* @param mixed $raw
*/
public function __construct($raw)
public function __construct(array $raw)
{
parent::__construct($raw);
$this->autonomousSystemNumber = $this->get('autonomous_system_number');
$this->autonomousSystemOrganization =
$this->get('autonomous_system_organization');
$this->isp = $this->get('isp');
$this->mobileCountryCode = $this->get('mobile_country_code');
$this->mobileNetworkCode = $this->get('mobile_network_code');
$this->organization = $this->get('organization');
$ipAddress = $this->get('ip_address');

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2;
interface ProviderInterface
@ -9,12 +11,12 @@ interface ProviderInterface
*
* @return \GeoIp2\Model\Country a Country model for the requested IP address
*/
public function country($ipAddress);
public function country(string $ipAddress): Model\Country;
/**
* @param string $ipAddress an IPv4 or IPv6 address to lookup
*
* @return \GeoIp2\Model\City a City model for the requested IP address
*/
public function city($ipAddress);
public function city(string $ipAddress): Model\City;
}

View File

@ -1,18 +1,20 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
abstract class AbstractPlaceRecord extends AbstractRecord
{
/**
* @var array<string>
*/
private $locales;
/**
* @ignore
*
* @param mixed $record
* @param mixed $locales
*/
public function __construct($record, $locales = ['en'])
public function __construct(?array $record, array $locales = ['en'])
{
$this->locales = $locales;
parent::__construct($record);
@ -21,9 +23,9 @@ abstract class AbstractPlaceRecord extends AbstractRecord
/**
* @ignore
*
* @param mixed $attr
* @return mixed
*/
public function __get($attr)
public function __get(string $attr)
{
if ($attr === 'name') {
return $this->name();
@ -34,26 +36,25 @@ abstract class AbstractPlaceRecord extends AbstractRecord
/**
* @ignore
*
* @param mixed $attr
*/
public function __isset($attr)
public function __isset(string $attr): bool
{
if ($attr === 'name') {
return $this->firstSetNameLocale() === null ? false : true;
return $this->firstSetNameLocale() !== null;
}
return parent::__isset($attr);
}
private function name()
private function name(): ?string
{
$locale = $this->firstSetNameLocale();
// @phpstan-ignore-next-line
return $locale === null ? null : $this->names[$locale];
}
private function firstSetNameLocale()
private function firstSetNameLocale(): ?string
{
foreach ($this->locales as $locale) {
if (isset($this->names[$locale])) {

View File

@ -1,17 +1,20 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
abstract class AbstractRecord implements \JsonSerializable
{
/**
* @var array<string, mixed>
*/
private $record;
/**
* @ignore
*
* @param mixed $record
*/
public function __construct($record)
public function __construct(?array $record)
{
$this->record = isset($record) ? $record : [];
}
@ -19,42 +22,45 @@ abstract class AbstractRecord implements \JsonSerializable
/**
* @ignore
*
* @param mixed $attr
* @return mixed
*/
public function __get($attr)
public function __get(string $attr)
{
// XXX - kind of ugly but greatly reduces boilerplate code
$key = $this->attributeToKey($attr);
if ($this->__isset($attr)) {
return $this->record[$key];
} elseif ($this->validAttribute($attr)) {
}
if ($this->validAttribute($attr)) {
if (preg_match('/^is_/', $key)) {
return false;
}
return null;
}
throw new \RuntimeException("Unknown attribute: $attr");
}
public function __isset($attr)
public function __isset(string $attr): bool
{
return $this->validAttribute($attr) &&
isset($this->record[$this->attributeToKey($attr)]);
return $this->validAttribute($attr)
&& isset($this->record[$this->attributeToKey($attr)]);
}
private function attributeToKey($attr)
private function attributeToKey(string $attr): string
{
return strtolower(preg_replace('/([A-Z])/', '_\1', $attr));
}
private function validAttribute($attr)
private function validAttribute(string $attr): bool
{
// @phpstan-ignore-next-line
return \in_array($attr, $this->validAttributes, true);
}
public function jsonSerialize()
public function jsonSerialize(): ?array
{
return $this->record;
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -16,7 +18,7 @@ namespace GeoIp2\Record;
* @property-read string|null $name The name of the city based on the locales list
* passed to the constructor. This attribute is returned by all location
* services and databases.
* @property-read array|null $names A array map where the keys are locale codes
* @property-read array|null $names An array map where the keys are locale codes
* and the values are names. This attribute is returned by all location
* services and databases.
*/
@ -24,6 +26,8 @@ class City extends AbstractPlaceRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = ['confidence', 'geonameId', 'names'];
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -23,6 +25,8 @@ class Continent extends AbstractPlaceRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = [
'code',

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -29,6 +31,8 @@ class Country extends AbstractPlaceRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = [
'confidence',

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -37,6 +39,8 @@ class Location extends AbstractRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = [
'averageIncome',

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -14,6 +16,8 @@ class MaxMind extends AbstractRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = ['queriesRemaining'];
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -21,6 +23,8 @@ class Postal extends AbstractRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = ['code', 'confidence'];
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -15,6 +17,11 @@ namespace GeoIp2\Record;
*/
class RepresentedCountry extends Country
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = [
'confidence',
'geonameId',

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
@ -30,6 +32,8 @@ class Subdivision extends AbstractPlaceRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = [
'confidence',

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
use GeoIp2\Util;
@ -12,13 +14,13 @@ use GeoIp2\Util;
* @property-read int|null $autonomousSystemNumber The autonomous system number
* associated with the IP address. See
* https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This attribute
* is only available from the City and Insights web service and the GeoIP2
* Enterprise database.
* is only available from the City Plus and Insights web services and the
* GeoIP2 Enterprise database.
* @property-read string|null $autonomousSystemOrganization The organization
* associated with the registered autonomous system number for the IP address.
* See https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This
* attribute is only available from the City and Insights web service and the
* GeoIP2 Enterprise database.
* attribute is only available from the City Plus and Insights web services and
* the GeoIP2 Enterprise database.
* @property-read string|null $connectionType The connection type may take the
* following values: "Dialup", "Cable/DSL", "Corporate", "Cellular".
* Additional values may be added in the future. This attribute is only
@ -26,7 +28,7 @@ use GeoIp2\Util;
* @property-read string|null $domain The second level domain associated with the
* IP address. This will be something like "example.com" or "example.co.uk",
* not "foo.example.com". This attribute is only available from the
* City and Insights web service and the GeoIP2 Enterprise
* City Plus and Insights web services and the GeoIP2 Enterprise
* database.
* @property-read string $ipAddress The IP address that the data in the model
* is for. If you performed a "me" lookup against the web service, this
@ -36,7 +38,7 @@ use GeoIp2\Util;
* points.
* @property-read bool $isAnonymous This is true if the IP address belongs to
* any sort of anonymous network. This property is only available from GeoIP2
* Precision Insights.
* Insights.
* @property-read bool $isAnonymousProxy *Deprecated.* Please see our GeoIP2
* Anonymous IP database
* (https://www.maxmind.com/en/geoip2-anonymous-ip-database) to determine
@ -45,39 +47,49 @@ use GeoIp2\Util;
* registered to an anonymous VPN provider. If a VPN provider does not register
* subnets under names associated with them, we will likely only flag their IP
* ranges using the isHostingProvider property. This property is only available
* from GeoIP2 Precision Insights.
* from GeoIP2 Insights.
* @property-read bool $isHostingProvider This is true if the IP address belongs
* to a hosting or VPN provider (see description of isAnonymousVpn property).
* This property is only available from GeoIP2 Precision Insights.
* This property is only available from GeoIP2 Insights.
* @property-read bool $isLegitimateProxy This attribute is true if MaxMind
* believes this IP address to be a legitimate proxy, such as an internal
* VPN used by a corporation. This attribute is only available in the GeoIP2
* Enterprise database.
* @property-read bool $isPublicProxy This is true if the IP address belongs to
* a public proxy. This property is only available from GeoIP2 Precision
* Insights.
* a public proxy. This property is only available from GeoIP2 Insights.
* @property-read bool $isResidentialProxy This is true if the IP address is
* on a suspected anonymizing network and belongs to a residential ISP. This
* property is only available from GeoIP2 Insights.
* @property-read bool $isSatelliteProvider *Deprecated.* Due to the
* increased coverage by mobile carriers, very few satellite providers now
* serve multiple countries. As a result, the output does not provide
* sufficiently relevant data for us to maintain it.
* @property-read bool $isTorExitNode This is true if the IP address is a Tor
* exit node. This property is only available from GeoIP2 Precision Insights.
* exit node. This property is only available from GeoIP2 Insights.
* @property-read string|null $isp The name of the ISP associated with the IP
* address. This attribute is only available from the City and Insights web
* services and the GeoIP2 Enterprise database.
* address. This attribute is only available from the City Plus and Insights
* web services and the GeoIP2 Enterprise database.
* @property-read string $network The network in CIDR notation associated with
* the record. In particular, this is the largest network where all of the
* fields besides $ipAddress have the same value.
* @property-read string|null $organization The name of the organization associated
* with the IP address. This attribute is only available from the City and
* @property-read string|null $organization The name of the organization
* associated with the IP address. This attribute is only available from the
* City Plus and Insights web services and the GeoIP2 Enterprise database.
* @property-read string|null $mobileCountryCode The [mobile country code
* (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
* the IP address and ISP. This property is available from the City Plus and
* Insights web services and the GeoIP2 Enterprise database.
* @property-read float|null $staticIPScore An indicator of how static or
* @property-read string|null $mobileNetworkCode The [mobile network code
* (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
* the IP address and ISP. This property is available from the City Plus and
* Insights web services and the GeoIP2 Enterprise database.
* @property-read float|null $staticIpScore An indicator of how static or
* dynamic an IP address is. This property is only available from GeoIP2
* Precision Insights.
* Insights.
* @property-read int|null $userCount The estimated number of users sharing
* the IP/network during the past 24 hours. For IPv4, the count is for the
* individual IP. For IPv6, the count is for the /64 network. This property is
* only available from GeoIP2 Precision Insights.
* only available from GeoIP2 Insights.
* @property-read string|null $userType <p>The user type associated with the IP
* address. This can be one of the following values:</p>
* <ul>
@ -85,6 +97,7 @@ use GeoIp2\Util;
* <li>cafe
* <li>cellular
* <li>college
* <li>consumer_privacy_network
* <li>content_delivery_network
* <li>dialup
* <li>government
@ -106,6 +119,8 @@ class Traits extends AbstractRecord
{
/**
* @ignore
*
* @var array<string>
*/
protected $validAttributes = [
'autonomousSystemNumber',
@ -120,8 +135,11 @@ class Traits extends AbstractRecord
'isLegitimateProxy',
'isp',
'isPublicProxy',
'isResidentialProxy',
'isSatelliteProvider',
'isTorExitNode',
'mobileCountryCode',
'mobileNetworkCode',
'network',
'organization',
'staticIpScore',
@ -129,9 +147,9 @@ class Traits extends AbstractRecord
'userType',
];
public function __construct($record)
public function __construct(?array $record)
{
if (!isset($record['network']) && isset($record['ip_address']) && isset($record['prefix_len'])) {
if (!isset($record['network']) && isset($record['ip_address'], $record['prefix_len'])) {
$record['network'] = Util::cidr($record['ip_address'], $record['prefix_len']);
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2;
class Util
@ -10,11 +12,8 @@ class Util
*
* @internal
* @ignore
*
* @param mixed $ipAddress
* @param mixed $prefixLen
*/
public static function cidr($ipAddress, $prefixLen)
public static function cidr(string $ipAddress, int $prefixLen): string
{
$ipBytes = inet_pton($ipAddress);
$networkBytes = str_repeat("\0", \strlen($ipBytes));

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace GeoIp2\WebService;
use GeoIp2\Exception\AddressNotFoundException;
@ -8,13 +10,16 @@ use GeoIp2\Exception\GeoIp2Exception;
use GeoIp2\Exception\HttpException;
use GeoIp2\Exception\InvalidRequestException;
use GeoIp2\Exception\OutOfQueriesException;
use GeoIp2\Model\City;
use GeoIp2\Model\Country;
use GeoIp2\Model\Insights;
use GeoIp2\ProviderInterface;
use MaxMind\WebService\Client as WsClient;
/**
* This class provides a client API for all the GeoIP2 Precision web services.
* The services are Country, City, and Insights. Each service returns a
* different set of data about an IP address, with Country returning the
* This class provides a client API for all the GeoIP2 web services.
* The services are Country, City Plus, and Insights. Each service returns
* a different set of data about an IP address, with Country returning the
* least data and Insights the most.
*
* Each web service is represented by a different model class, and these model
@ -43,11 +48,22 @@ use MaxMind\WebService\Client as WsClient;
*/
class Client implements ProviderInterface
{
/**
* @var array<string>
*/
private $locales;
/**
* @var WsClient
*/
private $client;
/**
* @var string
*/
private static $basePath = '/geoip/v2.1';
const VERSION = 'v2.10.0';
public const VERSION = 'v2.13.0';
/**
* Constructor.
@ -57,7 +73,10 @@ class Client implements ProviderInterface
* @param array $locales list of locale codes to use in name property
* from most preferred to least preferred
* @param array $options array of options. Valid options include:
* * `host` - The host to use when querying the web service.
* * `host` - The host to use when querying the web
* service. To query the GeoLite2 web service
* instead of the GeoIP2 web service, set the
* host to `geolite.info`.
* * `timeout` - Timeout in seconds.
* * `connectTimeout` - Initial connection timeout in seconds.
* * `proxy` - The HTTP proxy to use. May include a schema, port,
@ -65,15 +84,16 @@ class Client implements ProviderInterface
* `http://username:password@127.0.0.1:10`.
*/
public function __construct(
$accountId,
$licenseKey,
$locales = ['en'],
$options = []
int $accountId,
string $licenseKey,
array $locales = ['en'],
array $options = []
) {
$this->locales = $locales;
// This is for backwards compatibility. Do not remove except for a
// major version bump.
// @phpstan-ignore-next-line
if (\is_string($options)) {
$options = ['host' => $options];
}
@ -87,13 +107,13 @@ class Client implements ProviderInterface
$this->client = new WsClient($accountId, $licenseKey, $options);
}
private function userAgent()
private function userAgent(): string
{
return 'GeoIP2-API/' . self::VERSION;
}
/**
* This method calls the GeoIP2 Precision: City service.
* This method calls the City Plus service.
*
* @param string $ipAddress IPv4 or IPv6 address as a string. If no
* address is provided, the address that the web service is called
@ -115,16 +135,15 @@ class Client implements ProviderInterface
* @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
* class to the above exceptions. It will be thrown directly
* if a 200 status code is returned but the body is invalid.
*
* @return \GeoIp2\Model\City
*/
public function city($ipAddress = 'me')
public function city(string $ipAddress = 'me'): City
{
return $this->responseFor('city', 'City', $ipAddress);
// @phpstan-ignore-next-line
return $this->responseFor('city', City::class, $ipAddress);
}
/**
* This method calls the GeoIP2 Precision: Country service.
* This method calls the Country service.
*
* @param string $ipAddress IPv4 or IPv6 address as a string. If no
* address is provided, the address that the web service is called
@ -146,16 +165,15 @@ class Client implements ProviderInterface
* @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent class to the above exceptions. It
* will be thrown directly if a 200 status code is returned but
* the body is invalid.
*
* @return \GeoIp2\Model\Country
*/
public function country($ipAddress = 'me')
public function country(string $ipAddress = 'me'): Country
{
return $this->responseFor('country', 'Country', $ipAddress);
return $this->responseFor('country', Country::class, $ipAddress);
}
/**
* This method calls the GeoIP2 Precision: Insights service.
* This method calls the Insights service. Insights is only supported by
* the GeoIP2 web service. The GeoLite2 web service does not support it.
*
* @param string $ipAddress IPv4 or IPv6 address as a string. If no
* address is provided, the address that the web service is called
@ -177,20 +195,20 @@ class Client implements ProviderInterface
* @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
* class to the above exceptions. It will be thrown directly
* if a 200 status code is returned but the body is invalid.
*
* @return \GeoIp2\Model\Insights
*/
public function insights($ipAddress = 'me')
public function insights(string $ipAddress = 'me'): Insights
{
return $this->responseFor('insights', 'Insights', $ipAddress);
// @phpstan-ignore-next-line
return $this->responseFor('insights', Insights::class, $ipAddress);
}
private function responseFor($endpoint, $class, $ipAddress)
private function responseFor(string $endpoint, string $class, string $ipAddress): Country
{
$path = implode('/', [self::$basePath, $endpoint, $ipAddress]);
try {
$body = $this->client->get('GeoIP2 ' . $class, $path);
$service = (new \ReflectionClass($class))->getShortName();
$body = $this->client->get('GeoIP2 ' . $service, $path);
} catch (\MaxMind\Exception\IpAddressNotFoundException $ex) {
throw new AddressNotFoundException(
$ex->getMessage(),
@ -232,8 +250,6 @@ class Client implements ProviderInterface
);
}
$class = 'GeoIp2\\Model\\' . $class;
return new $class($body, $this->locales);
}
}

View File

@ -1,5 +1,11 @@
# CHANGELOG
## 1.5.2 - 2022-08-07
### Changed
- Officially support PHP 8.2
## 1.5.1 - 2021-10-22
### Fixed
@ -12,10 +18,11 @@
### Changed
- Call handler when waiting on fulfilled/rejected Promise
- Officially support PHP 8.1
### Fixed
- Fix manually settle promises generated with Utils::task
- Fix manually settle promises generated with `Utils::task`
## 1.4.1 - 2021-02-18

View File

@ -1,13 +0,0 @@
all: clean test
test:
vendor/bin/phpunit
coverage:
vendor/bin/phpunit --coverage-html=artifacts/coverage
view-coverage:
open artifacts/coverage/index.html
clean:
rm -rf artifacts/*

View File

@ -17,7 +17,7 @@ for a general introduction to promises.
- [Implementation notes](#implementation-notes)
# Features
## Features
- [Promises/A+](https://promisesaplus.com/) implementation.
- Promise resolution and chaining is handled iteratively, allowing for
@ -29,15 +29,14 @@ for a general introduction to promises.
`GuzzleHttp\Promise\Coroutine::of()`.
# Quick start
## Quick Start
A *promise* represents the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promise's eventual value or the reason
why the promise cannot be fulfilled.
## Callbacks
### Callbacks
Callbacks are registered with the `then` method by providing an optional
`$onFulfilled` followed by an optional `$onRejected` function.
@ -60,12 +59,11 @@ $promise->then(
```
*Resolving* a promise means that you either fulfill a promise with a *value* or
reject a promise with a *reason*. Resolving a promises triggers callbacks
registered with the promises's `then` method. These callbacks are triggered
reject a promise with a *reason*. Resolving a promise triggers callbacks
registered with the promise's `then` method. These callbacks are triggered
only once and in the order in which they were added.
## Resolving a promise
### Resolving a Promise
Promises are fulfilled using the `resolve($value)` method. Resolving a promise
with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
@ -92,8 +90,7 @@ $promise
$promise->resolve('reader.');
```
## Promise forwarding
### Promise Forwarding
Promises can be chained one after the other. Each then in the chain is a new
promise. The return value of a promise is what's forwarded to the next
@ -123,7 +120,7 @@ $promise->resolve('A');
$nextPromise->resolve('B');
```
## Promise rejection
### Promise Rejection
When a promise is rejected, the `$onRejected` callbacks are invoked with the
rejection reason.
@ -140,7 +137,7 @@ $promise->reject('Error!');
// Outputs "Error!"
```
## Rejection forwarding
### Rejection Forwarding
If an exception is thrown in an `$onRejected` callback, subsequent
`$onRejected` callbacks are invoked with the thrown exception as the reason.
@ -195,7 +192,8 @@ $promise
$promise->reject('Error!');
```
# Synchronous wait
## Synchronous Wait
You can synchronously force promises to complete using a promise's `wait`
method. When creating a promise, you can provide a wait function that is used
@ -247,8 +245,7 @@ $promise->wait();
> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
## Unwrapping a promise
### Unwrapping a Promise
When synchronously waiting on a promise, you are joining the state of the
promise into the current state of execution (i.e., return the value of the
@ -275,7 +272,7 @@ wait function will be the value delivered to promise B.
**Note**: when you do not unwrap the promise, no value is returned.
# Cancellation
## Cancellation
You can cancel a promise that has not yet been fulfilled using the `cancel()`
method of a promise. When creating a promise you can provide an optional
@ -283,10 +280,9 @@ cancel function that when invoked cancels the action of computing a resolution
of the promise.
# API
## API
## Promise
### Promise
When creating a promise object, you can provide an optional `$waitFn` and
`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
@ -349,7 +345,7 @@ A promise has the following methods:
Rejects the promise with the given `$reason`.
## FulfilledPromise
### FulfilledPromise
A fulfilled promise can be created to represent a promise that has been
fulfilled.
@ -366,7 +362,7 @@ $promise->then(function ($value) {
```
## RejectedPromise
### RejectedPromise
A rejected promise can be created to represent a promise that has been
rejected.
@ -383,7 +379,7 @@ $promise->then(null, function ($reason) {
```
# Promise interop
## Promise Interoperability
This library works with foreign promises that have a `then` method. This means
you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
@ -409,7 +405,7 @@ a foreign promise. You will need to wrap a third-party promise with a Guzzle
promise in order to utilize wait and cancel functions with foreign promises.
## Event Loop Integration
### Event Loop Integration
In order to keep the stack size constant, Guzzle promises are resolved
asynchronously using a task queue. When waiting on promises synchronously, the
@ -437,10 +433,9 @@ $loop->addPeriodicTimer(0, [$queue, 'run']);
*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
# Implementation notes
## Implementation Notes
## Promise resolution and chaining is handled iteratively
### Promise Resolution and Chaining is Handled Iteratively
By shuffling pending handlers from one owner to another, promises are
resolved iteratively, allowing for "infinite" then chaining.
@ -476,8 +471,7 @@ all of its pending handlers to the new promise. When the new promise is
eventually resolved, all of the pending handlers are delivered the forwarded
value.
## A promise is the deferred.
### A Promise is the Deferred
Some promise libraries implement promises using a deferred object to represent
a computation and a promise object to represent the delivery of the result of
@ -505,7 +499,10 @@ $promise->resolve('foo');
## Upgrading from Function API
A static API was first introduced in 1.4.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:
A static API was first introduced in 1.4.0, in order to mitigate problems with
functions conflicting between global and local copies of the package. The
function API will be removed in 2.0.0. A migration table has been provided here
for your convenience:
| Original Function | Replacement Method |
|----------------|----------------|
@ -536,10 +533,12 @@ A static API was first introduced in 1.4.0, in order to mitigate problems with f
If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information.
## License
Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
## For Enterprise
Available as part of the Tidelift Subscription

View File

@ -81,16 +81,8 @@ class EachPromise implements PromisorInterface
$this->iterable->rewind();
$this->refillPending();
} catch (\Throwable $e) {
/**
* @psalm-suppress NullReference
* @phpstan-ignore-next-line
*/
$this->aggregate->reject($e);
} catch (\Exception $e) {
/**
* @psalm-suppress NullReference
* @phpstan-ignore-next-line
*/
$this->aggregate->reject($e);
}

View File

@ -1,6 +1,56 @@
CHANGELOG
=========
1.11.0
-------------------
* Replace runtime define of a constant to facilitate opcache preloading.
Reported by vedadkajtaz. GitHub #134.
* Resolve minor issue found by the Clang static analyzer in the C
extension.
1.10.1 (2021-04-14)
-------------------
* Fix a `TypeError` exception in the pure PHP reader when using large
databases on 32-bit PHP builds with the `bcmath` extension. Reported
by dodo1708. GitHub #124.
1.10.0 (2021-02-09)
-------------------
* When using the pure PHP reader, unsigned integers up to PHP_MAX_INT
will now be integers in PHP rather than strings. Previously integers
greater than 2^24 on 32-bit platforms and 2^56 on 64-bit platforms
would be strings due to the use of `gmp` or `bcmath` to decode them.
Reported by Alejandro Celaya. GitHub #119.
1.9.0 (2021-01-07)
------------------
* The `maxminddb` extension is now buildable on Windows. Pull request
by Jan Ehrhardt. GitHub #115.
1.8.0 (2020-10-01)
------------------
* Fixes for PHP 8.0. Pull Request by Remi Collet. GitHub #108.
1.7.0 (2020-08-07)
------------------
* IMPORTANT: PHP 7.2 or greater is now required.
* The extension no longer depends on the pure PHP classes in
`maxmind-db/reader`. You can use it independently.
* Type hints have been added to both the pure PHP implementation
and the extension.
* The `metadata` method on the reader now returns a new copy of the
metadata object rather than the actual object used by the reader.
* Work around PHP `is_readable()` bug. Reported by Ben Roberts. GitHub
#92.
* This is the first release of the extension as a PECL package.
GitHub #34.
1.6.0 (2019-12-19)
------------------

View File

@ -112,9 +112,16 @@ you are using an autoloader, no changes to your code should be necessary.
First install [libmaxminddb](https://github.com/maxmind/libmaxminddb) as
described in its [README.md
file](https://github.com/maxmind/libmaxminddb/blob/master/README.md#installing-from-a-tarball).
After successfully installing libmaxmindb, run the following commands from the
top-level directory of this distribution:
file](https://github.com/maxmind/libmaxminddb/blob/main/README.md#installing-from-a-tarball).
After successfully installing libmaxmindb, you may install the extension
from [pecl](https://pecl.php.net/package/maxminddb):
```
pecl install maxminddb
```
Alternatively, you may install it from the source. To do so, run the following
commands from the top-level directory of this distribution:
```
cd ext
@ -157,7 +164,7 @@ client API, please see [our support page](https://www.maxmind.com/en/support).
## Requirements ##
This library requires PHP 5.6 or greater.
This library requires PHP 7.2 or greater.
The GMP or BCMath extension may be required to read some databases
using the pure PHP API.
@ -173,6 +180,6 @@ The MaxMind DB Reader PHP API uses [Semantic Versioning](https://semver.org/).
## Copyright and License ##
This software is Copyright (c) 2014-2019 by MaxMind, Inc.
This software is Copyright (c) 2014-2020 by MaxMind, Inc.
This is free software, licensed under the Apache License, Version 2.0.

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* PSR-4 autoloader implementation for the MaxMind\DB namespace.
* First we define the 'mmdb_autoload' function, and then we register
@ -14,7 +16,7 @@
* @param string $class
* the name of the class to load
*/
function mmdb_autoload($class)
function mmdb_autoload($class): void
{
/*
* A project-specific mapping between the namespaces and where
@ -26,16 +28,16 @@ function mmdb_autoload($class)
$namespace_map = ['MaxMind\\Db\\' => __DIR__ . '/src/MaxMind/Db/'];
foreach ($namespace_map as $prefix => $dir) {
/* First swap out the namespace prefix with a directory... */
// First swap out the namespace prefix with a directory...
$path = str_replace($prefix, $dir, $class);
/* replace the namespace separator with a directory separator... */
// replace the namespace separator with a directory separator...
$path = str_replace('\\', '/', $path);
/* and finally, add the PHP file extension to the result. */
// and finally, add the PHP file extension to the result.
$path = $path . '.php';
/* $path should now contain the path to a PHP file defining $class */
// $path should now contain the path to a PHP file defining $class
if (file_exists($path)) {
include $path;
}

View File

@ -13,7 +13,7 @@
}
],
"require": {
"php": ">=5.6"
"php": ">=7.2"
},
"suggest": {
"ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
@ -21,14 +21,15 @@
"ext-maxminddb": "A C-based database decoder that provides significantly faster lookups"
},
"conflict": {
"ext-maxminddb": "<1.6.0,>=2.0.0"
"ext-maxminddb": "<1.10.1,>=2.0.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "5.*",
"friendsofphp/php-cs-fixer": "3.*",
"phpunit/phpunit": ">=8.0.0,<10.0.0",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpcov": "^3.0",
"squizlabs/php_codesniffer": "3.*"
"phpunit/phpcov": ">=6.0.0",
"squizlabs/php_codesniffer": "3.*",
"phpstan/phpstan": "*"
},
"autoload": {
"psr-4": {

10
vendor/maxmind-db/reader/ext/config.w32 vendored Normal file
View File

@ -0,0 +1,10 @@
ARG_WITH("maxminddb", "Enable MaxMind DB Reader extension support", "no");
if (PHP_MAXMINDDB == "yes") {
if (CHECK_HEADER_ADD_INCLUDE("maxminddb.h", "CFLAGS_MAXMINDDB", PHP_MAXMINDDB + ";" + PHP_PHP_BUILD + "\\include\\maxminddb") &&
CHECK_LIB("libmaxminddb.lib", "maxminddb", PHP_MAXMINDDB)) {
EXTENSION("maxminddb", "maxminddb.c");
} else {
WARNING('Could not find maxminddb.h or libmaxminddb.lib; skipping');
}
}

View File

@ -21,6 +21,8 @@
#include <zend.h>
#include "Zend/zend_exceptions.h"
#include "Zend/zend_types.h"
#include "ext/spl/spl_exceptions.h"
#include "ext/standard/info.h"
#include <maxminddb.h>
@ -33,50 +35,50 @@
#define PHP_MAXMINDDB_NS ZEND_NS_NAME("MaxMind", "Db")
#define PHP_MAXMINDDB_READER_NS ZEND_NS_NAME(PHP_MAXMINDDB_NS, "Reader")
#define PHP_MAXMINDDB_METADATA_NS \
ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata")
#define PHP_MAXMINDDB_READER_EX_NS \
ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "InvalidDatabaseException")
#ifdef ZEND_ENGINE_3
#define Z_MAXMINDDB_P(zv) php_maxminddb_fetch_object(Z_OBJ_P(zv))
#define _ZVAL_STRING ZVAL_STRING
#define _ZVAL_STRINGL ZVAL_STRINGL
typedef size_t strsize_t;
typedef zend_object free_obj_t;
#else
#define Z_MAXMINDDB_P(zv) \
(maxminddb_obj *)zend_object_store_get_object(zv TSRMLS_CC)
#define _ZVAL_STRING(a, b) ZVAL_STRING(a, b, 1)
#define _ZVAL_STRINGL(a, b, c) ZVAL_STRINGL(a, b, c, 1)
typedef int strsize_t;
typedef void free_obj_t;
#endif
/* For PHP 8 compatibility */
#ifndef TSRMLS_C
#if PHP_VERSION_ID < 80000
#define PROP_OBJ(zv) (zv)
#else
#define PROP_OBJ(zv) Z_OBJ_P(zv)
#define TSRMLS_C
#endif
#ifndef TSRMLS_CC
#define TSRMLS_CC
#endif
#ifndef TSRMLS_DC
#define TSRMLS_DC
/* End PHP 8 compatibility */
#endif
#ifndef ZEND_ACC_CTOR
#define ZEND_ACC_CTOR 0
#endif
#ifdef ZEND_ENGINE_3
typedef struct _maxminddb_obj {
MMDB_s *mmdb;
zend_object std;
} maxminddb_obj;
#else
typedef struct _maxminddb_obj {
zend_object std;
MMDB_s *mmdb;
} maxminddb_obj;
/* IS_MIXED was added in 2020 */
#ifndef IS_MIXED
#define IS_MIXED IS_UNDEF
#endif
/* ZEND_THIS was added in 7.4 */
#ifndef ZEND_THIS
#define ZEND_THIS (&EX(This))
#endif
typedef struct _maxminddb_obj {
MMDB_s *mmdb;
zend_object std;
} maxminddb_obj;
PHP_FUNCTION(maxminddb);
static int
@ -96,7 +98,6 @@ static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list,
zval *z_value TSRMLS_DC);
static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list,
zval *z_value TSRMLS_DC);
static zend_class_entry *lookup_class(const char *name TSRMLS_DC);
#define CHECK_ALLOCATED(val) \
if (!val) { \
@ -104,38 +105,16 @@ static zend_class_entry *lookup_class(const char *name TSRMLS_DC);
return; \
}
#define THROW_EXCEPTION(name, ...) \
{ \
zend_class_entry *exception_ce = lookup_class(name TSRMLS_CC); \
zend_throw_exception_ex(exception_ce, 0 TSRMLS_CC, __VA_ARGS__); \
}
#if PHP_VERSION_ID < 50399
#define object_properties_init(zo, class_type) \
{ \
zval *tmp; \
zend_hash_copy((*zo).properties, \
&class_type->default_properties, \
(copy_ctor_func_t)zval_add_ref, \
(void *)&tmp, \
sizeof(zval *)); \
}
#endif
static zend_object_handlers maxminddb_obj_handlers;
static zend_class_entry *maxminddb_ce;
static zend_class_entry *maxminddb_ce, *maxminddb_exception_ce, *metadata_ce;
static inline maxminddb_obj *
php_maxminddb_fetch_object(zend_object *obj TSRMLS_DC) {
#ifdef ZEND_ENGINE_3
return (maxminddb_obj *)((char *)(obj)-XtOffsetOf(maxminddb_obj, std));
#else
return (maxminddb_obj *)obj;
#endif
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxmindbreader_construct, 0, 0, 1)
ZEND_ARG_INFO(0, db_file)
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, db_file, IS_STRING, 0)
ZEND_END_ARG_INFO()
PHP_METHOD(MaxMind_Db_Reader, __construct) {
@ -150,14 +129,14 @@ PHP_METHOD(MaxMind_Db_Reader, __construct) {
maxminddb_ce,
&db_file,
&name_len) == FAILURE) {
THROW_EXCEPTION("InvalidArgumentException",
"The constructor takes exactly one argument.");
return;
}
if (0 != php_check_open_basedir(db_file TSRMLS_CC) ||
0 != access(db_file, R_OK)) {
THROW_EXCEPTION("InvalidArgumentException",
zend_throw_exception_ex(
spl_ce_InvalidArgumentException,
0 TSRMLS_CC,
"The file \"%s\" does not exist or is not readable.",
db_file);
return;
@ -167,7 +146,9 @@ PHP_METHOD(MaxMind_Db_Reader, __construct) {
uint16_t status = MMDB_open(db_file, MMDB_MODE_MMAP, mmdb);
if (MMDB_SUCCESS != status) {
THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS,
zend_throw_exception_ex(
maxminddb_exception_ce,
0 TSRMLS_CC,
"Error opening database file (%s). Is this a valid "
"MaxMind DB file?",
db_file);
@ -175,12 +156,13 @@ PHP_METHOD(MaxMind_Db_Reader, __construct) {
return;
}
maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(getThis());
maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(ZEND_THIS);
mmdb_obj->mmdb = mmdb;
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxmindbreader_get, 0, 0, 1)
ZEND_ARG_INFO(0, ip_address)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
arginfo_maxminddbreader_get, 0, 1, IS_MIXED, 1)
ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0)
ZEND_END_ARG_INFO()
PHP_METHOD(MaxMind_Db_Reader, get) {
@ -188,76 +170,75 @@ PHP_METHOD(MaxMind_Db_Reader, get) {
get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value, &prefix_len);
}
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
arginfo_maxminddbreader_getWithPrefixLen, 0, 1, IS_ARRAY, 1)
ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0)
ZEND_END_ARG_INFO()
PHP_METHOD(MaxMind_Db_Reader, getWithPrefixLen) {
zval *record, *z_prefix_len;
#ifdef ZEND_ENGINE_3
zval _record, _z_prefix_len;
record = &_record;
z_prefix_len = &_z_prefix_len;
#else
ALLOC_INIT_ZVAL(record);
ALLOC_INIT_ZVAL(z_prefix_len);
#endif
zval record, z_prefix_len;
int prefix_len = 0;
if (get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, record, &prefix_len)) {
if (get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, &record, &prefix_len) ==
FAILURE) {
return;
}
array_init(return_value);
add_next_index_zval(return_value, record);
add_next_index_zval(return_value, &record);
ZVAL_LONG(z_prefix_len, prefix_len);
add_next_index_zval(return_value, z_prefix_len);
ZVAL_LONG(&z_prefix_len, prefix_len);
add_next_index_zval(return_value, &z_prefix_len);
}
static int
get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) {
char *ip_address = NULL;
strsize_t name_len;
zval *_this_zval = NULL;
zval *this_zval = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
getThis(),
"Os",
&_this_zval,
&this_zval,
maxminddb_ce,
&ip_address,
&name_len) == FAILURE) {
THROW_EXCEPTION("InvalidArgumentException",
"Method takes exactly one argument.");
return 1;
return FAILURE;
}
const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(getThis());
const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(ZEND_THIS);
MMDB_s *mmdb = mmdb_obj->mmdb;
if (NULL == mmdb) {
THROW_EXCEPTION("BadMethodCallException",
zend_throw_exception_ex(spl_ce_BadMethodCallException,
0 TSRMLS_CC,
"Attempt to read from a closed MaxMind DB.");
return 1;
return FAILURE;
}
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_flags = AI_NUMERICHOST,
// We set ai_socktype so that we only get one result back
/* We set ai_socktype so that we only get one result back */
.ai_socktype = SOCK_STREAM};
struct addrinfo *addresses = NULL;
int gai_status = getaddrinfo(ip_address, NULL, &hints, &addresses);
if (gai_status) {
THROW_EXCEPTION("InvalidArgumentException",
zend_throw_exception_ex(spl_ce_InvalidArgumentException,
0 TSRMLS_CC,
"The value \"%s\" is not a valid IP address.",
ip_address);
return 1;
return FAILURE;
}
if (!addresses || !addresses->ai_addr) {
THROW_EXCEPTION(
"InvalidArgumentException",
zend_throw_exception_ex(
spl_ce_InvalidArgumentException,
0 TSRMLS_CC,
"getaddrinfo was successful but failed to set the addrinfo");
return 1;
return FAILURE;
}
int sa_family = addresses->ai_addr->sa_family;
@ -269,131 +250,126 @@ get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) {
freeaddrinfo(addresses);
if (MMDB_SUCCESS != mmdb_error) {
char *exception_name;
zend_class_entry *ex;
if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) {
exception_name = "InvalidArgumentException";
ex = spl_ce_InvalidArgumentException;
} else {
exception_name = PHP_MAXMINDDB_READER_EX_NS;
ex = maxminddb_exception_ce;
}
THROW_EXCEPTION(exception_name,
zend_throw_exception_ex(ex,
0 TSRMLS_CC,
"Error looking up %s. %s",
ip_address,
MMDB_strerror(mmdb_error));
return 1;
return FAILURE;
}
*prefix_len = result.netmask;
if (sa_family == AF_INET && mmdb->metadata.ip_version == 6) {
// We return the prefix length given the IPv4 address. If there is
// no IPv4 subtree, we return a prefix length of 0.
/* We return the prefix length given the IPv4 address. If there is
no IPv4 subtree, we return a prefix length of 0. */
*prefix_len = *prefix_len >= 96 ? *prefix_len - 96 : 0;
}
if (!result.found_entry) {
ZVAL_NULL(record);
return 0;
return SUCCESS;
}
MMDB_entry_data_list_s *entry_data_list = NULL;
int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (MMDB_SUCCESS != status) {
THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS,
zend_throw_exception_ex(maxminddb_exception_ce,
0 TSRMLS_CC,
"Error while looking up data for %s. %s",
ip_address,
MMDB_strerror(status));
MMDB_free_entry_data_list(entry_data_list);
return 1;
return FAILURE;
} else if (NULL == entry_data_list) {
THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS,
zend_throw_exception_ex(
maxminddb_exception_ce,
0 TSRMLS_CC,
"Error while looking up data for %s. Your database may "
"be corrupt or you have found a bug in libmaxminddb.",
ip_address);
return 1;
return FAILURE;
}
const MMDB_entry_data_list_s *rv =
handle_entry_data_list(entry_data_list, record TSRMLS_CC);
if (rv == NULL) {
/* We should have already thrown the exception in handle_entry_data_list
*/
return FAILURE;
}
MMDB_free_entry_data_list(entry_data_list);
return 0;
return SUCCESS;
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxmindbreader_void, 0, 0, 0)
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_void, 0, 0, 0)
ZEND_END_ARG_INFO()
PHP_METHOD(MaxMind_Db_Reader, metadata) {
if (ZEND_NUM_ARGS() != 0) {
THROW_EXCEPTION("InvalidArgumentException",
"Method takes no arguments.");
zval *this_zval = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
getThis(),
"O",
&this_zval,
maxminddb_ce) == FAILURE) {
return;
}
const maxminddb_obj *const mmdb_obj =
(maxminddb_obj *)Z_MAXMINDDB_P(getThis());
(maxminddb_obj *)Z_MAXMINDDB_P(this_zval);
if (NULL == mmdb_obj->mmdb) {
THROW_EXCEPTION("BadMethodCallException",
zend_throw_exception_ex(spl_ce_BadMethodCallException,
0 TSRMLS_CC,
"Attempt to read from a closed MaxMind DB.");
return;
}
const char *const name = ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata");
zend_class_entry *metadata_ce = lookup_class(name TSRMLS_CC);
object_init_ex(return_value, metadata_ce);
#ifdef ZEND_ENGINE_3
zval _metadata_array;
zval *metadata_array = &_metadata_array;
ZVAL_NULL(metadata_array);
#else
zval *metadata_array;
ALLOC_INIT_ZVAL(metadata_array);
#endif
MMDB_entry_data_list_s *entry_data_list;
MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list);
handle_entry_data_list(entry_data_list, metadata_array TSRMLS_CC);
zval metadata_array;
const MMDB_entry_data_list_s *rv =
handle_entry_data_list(entry_data_list, &metadata_array TSRMLS_CC);
if (rv == NULL) {
return;
}
MMDB_free_entry_data_list(entry_data_list);
#if PHP_VERSION_ID >= 80000
zend_call_method_with_1_params(Z_OBJ_P(return_value),
zend_call_method_with_1_params(PROP_OBJ(return_value),
metadata_ce,
&metadata_ce->constructor,
ZEND_CONSTRUCTOR_FUNC_NAME,
NULL,
metadata_array);
zval_ptr_dtor(metadata_array);
#elif defined(ZEND_ENGINE_3)
zend_call_method_with_1_params(return_value,
metadata_ce,
&metadata_ce->constructor,
ZEND_CONSTRUCTOR_FUNC_NAME,
NULL,
metadata_array);
zval_ptr_dtor(metadata_array);
#else
zend_call_method_with_1_params(&return_value,
metadata_ce,
&metadata_ce->constructor,
ZEND_CONSTRUCTOR_FUNC_NAME,
NULL,
metadata_array);
&metadata_array);
zval_ptr_dtor(&metadata_array);
#endif
}
PHP_METHOD(MaxMind_Db_Reader, close) {
if (ZEND_NUM_ARGS() != 0) {
THROW_EXCEPTION("InvalidArgumentException",
"Method takes no arguments.");
zval *this_zval = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
getThis(),
"O",
&this_zval,
maxminddb_ce) == FAILURE) {
return;
}
maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(getThis());
maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(this_zval);
if (NULL == mmdb_obj->mmdb) {
THROW_EXCEPTION("BadMethodCallException",
zend_throw_exception_ex(spl_ce_BadMethodCallException,
0 TSRMLS_CC,
"Attempt to close a closed MaxMind DB.");
return;
}
@ -411,12 +387,12 @@ handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list,
case MMDB_DATA_TYPE_ARRAY:
return handle_array(entry_data_list, z_value TSRMLS_CC);
case MMDB_DATA_TYPE_UTF8_STRING:
_ZVAL_STRINGL(z_value,
ZVAL_STRINGL(z_value,
(char *)entry_data_list->entry_data.utf8_string,
entry_data_list->entry_data.data_size);
break;
case MMDB_DATA_TYPE_BYTES:
_ZVAL_STRINGL(z_value,
ZVAL_STRINGL(z_value,
(char *)entry_data_list->entry_data.bytes,
entry_data_list->entry_data.data_size);
break;
@ -445,7 +421,8 @@ handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list,
ZVAL_LONG(z_value, entry_data_list->entry_data.int32);
break;
default:
THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS,
zend_throw_exception_ex(maxminddb_exception_ce,
0 TSRMLS_CC,
"Invalid data type arguments: %d",
entry_data_list->entry_data.type);
return NULL;
@ -459,30 +436,26 @@ handle_map(const MMDB_entry_data_list_s *entry_data_list,
array_init(z_value);
const uint32_t map_size = entry_data_list->entry_data.data_size;
uint i;
uint32_t i;
for (i = 0; i < map_size && entry_data_list; i++) {
entry_data_list = entry_data_list->next;
char *key = estrndup((char *)entry_data_list->entry_data.utf8_string,
entry_data_list->entry_data.data_size);
if (NULL == key) {
THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS,
zend_throw_exception_ex(maxminddb_exception_ce,
0 TSRMLS_CC,
"Invalid data type arguments");
return NULL;
}
entry_data_list = entry_data_list->next;
#ifdef ZEND_ENGINE_3
zval _new_value;
zval *new_value = &_new_value;
ZVAL_NULL(new_value);
#else
zval *new_value;
ALLOC_INIT_ZVAL(new_value);
#endif
zval new_value;
entry_data_list =
handle_entry_data_list(entry_data_list, new_value TSRMLS_CC);
add_assoc_zval(z_value, key, new_value);
handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC);
if (entry_data_list != NULL) {
add_assoc_zval(z_value, key, &new_value);
}
efree(key);
}
return entry_data_list;
@ -495,20 +468,15 @@ handle_array(const MMDB_entry_data_list_s *entry_data_list,
array_init(z_value);
uint i;
uint32_t i;
for (i = 0; i < size && entry_data_list; i++) {
entry_data_list = entry_data_list->next;
#ifdef ZEND_ENGINE_3
zval _new_value;
zval *new_value = &_new_value;
ZVAL_NULL(new_value);
#else
zval *new_value;
ALLOC_INIT_ZVAL(new_value);
#endif
zval new_value;
entry_data_list =
handle_entry_data_list(entry_data_list, new_value TSRMLS_CC);
add_next_index_zval(z_value, new_value);
handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC);
if (entry_data_list != NULL) {
add_next_index_zval(z_value, &new_value);
}
}
return entry_data_list;
}
@ -535,7 +503,7 @@ static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list,
spprintf(&num_str, 0, "0x%016" PRIX64 "%016" PRIX64, high, low);
CHECK_ALLOCATED(num_str);
_ZVAL_STRING(z_value, num_str);
ZVAL_STRING(z_value, num_str);
efree(num_str);
}
@ -556,7 +524,7 @@ static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list,
spprintf(&int_str, 0, "%" PRIu32, val);
CHECK_ALLOCATED(int_str);
_ZVAL_STRING(z_value, int_str);
ZVAL_STRING(z_value, int_str);
efree(int_str);
#endif
}
@ -578,29 +546,11 @@ static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list,
spprintf(&int_str, 0, "%" PRIu64, val);
CHECK_ALLOCATED(int_str);
_ZVAL_STRING(z_value, int_str);
ZVAL_STRING(z_value, int_str);
efree(int_str);
#endif
}
static zend_class_entry *lookup_class(const char *name TSRMLS_DC) {
#ifdef ZEND_ENGINE_3
zend_string *n = zend_string_init(name, strlen(name), 0);
zend_class_entry *ce = zend_lookup_class(n);
zend_string_release(n);
if (NULL == ce) {
zend_error(E_ERROR, "Class %s not found", name);
}
return ce;
#else
zend_class_entry **ce;
if (FAILURE == zend_lookup_class(name, strlen(name), &ce TSRMLS_CC)) {
zend_error(E_ERROR, "Class %s not found", name);
}
return *ce;
#endif
}
static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) {
maxminddb_obj *obj =
php_maxminddb_fetch_object((zend_object *)object TSRMLS_CC);
@ -610,12 +560,8 @@ static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) {
}
zend_object_std_dtor(&obj->std TSRMLS_CC);
#ifndef ZEND_ENGINE_3
efree(object);
#endif
}
#ifdef ZEND_ENGINE_3
static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) {
maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj));
zend_object_std_init(&obj->std, type TSRMLS_CC);
@ -625,31 +571,154 @@ static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) {
return &obj->std;
}
#else
static zend_object_value
maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) {
zend_object_value retval;
maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj));
zend_object_std_init(&obj->std, type TSRMLS_CC);
object_properties_init(&(obj->std), type);
/* clang-format off */
static zend_function_entry maxminddb_methods[] = {
PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxminddbreader_construct,
ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
PHP_ME(MaxMind_Db_Reader, close, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC)
PHP_ME(MaxMind_Db_Reader, get, arginfo_maxminddbreader_get, ZEND_ACC_PUBLIC)
PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxminddbreader_getWithPrefixLen, ZEND_ACC_PUBLIC)
PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC)
{ NULL, NULL, NULL }
};
/* clang-format on */
retval.handle = zend_objects_store_put(
obj, NULL, maxminddb_free_storage, NULL TSRMLS_CC);
retval.handlers = &maxminddb_obj_handlers;
ZEND_BEGIN_ARG_INFO_EX(arginfo_metadata_construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, metadata, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
return retval;
PHP_METHOD(MaxMind_Db_Reader_Metadata, __construct) {
zval *object = NULL;
zval *metadata_array = NULL;
zend_long node_count = 0;
zend_long record_size = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
getThis(),
"Oa",
&object,
metadata_ce,
&metadata_array) == FAILURE) {
return;
}
zval *tmp = NULL;
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"binary_format_major_version",
sizeof("binary_format_major_version") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"binaryFormatMajorVersion",
sizeof("binaryFormatMajorVersion") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"binary_format_minor_version",
sizeof("binary_format_minor_version") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"binaryFormatMinorVersion",
sizeof("binaryFormatMinorVersion") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"build_epoch",
sizeof("build_epoch") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"buildEpoch",
sizeof("buildEpoch") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"database_type",
sizeof("database_type") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"databaseType",
sizeof("databaseType") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"description",
sizeof("description") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"description",
sizeof("description") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"ip_version",
sizeof("ip_version") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"ipVersion",
sizeof("ipVersion") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(
HASH_OF(metadata_array), "languages", sizeof("languages") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"languages",
sizeof("languages") - 1,
tmp);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"record_size",
sizeof("record_size") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"recordSize",
sizeof("recordSize") - 1,
tmp);
if (Z_TYPE_P(tmp) == IS_LONG) {
record_size = Z_LVAL_P(tmp);
}
}
if (record_size != 0) {
zend_update_property_long(metadata_ce,
PROP_OBJ(object),
"nodeByteSize",
sizeof("nodeByteSize") - 1,
record_size / 4);
}
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
"node_count",
sizeof("node_count") - 1))) {
zend_update_property(metadata_ce,
PROP_OBJ(object),
"nodeCount",
sizeof("nodeCount") - 1,
tmp);
if (Z_TYPE_P(tmp) == IS_LONG) {
node_count = Z_LVAL_P(tmp);
}
}
if (record_size != 0) {
zend_update_property_long(metadata_ce,
PROP_OBJ(object),
"searchTreeSize",
sizeof("searchTreeSize") - 1,
record_size * node_count / 4);
}
}
#endif
// clang-format off
static zend_function_entry maxminddb_methods[] = {
PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxmindbreader_construct,
ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
PHP_ME(MaxMind_Db_Reader, close, arginfo_maxmindbreader_void, ZEND_ACC_PUBLIC)
PHP_ME(MaxMind_Db_Reader, get, arginfo_maxmindbreader_get, ZEND_ACC_PUBLIC)
PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxmindbreader_get, ZEND_ACC_PUBLIC)
PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxmindbreader_void, ZEND_ACC_PUBLIC)
static zend_function_entry metadata_methods[] = {
PHP_ME(MaxMind_Db_Reader_Metadata, __construct, arginfo_metadata_construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
{NULL, NULL, NULL}
};
// clang-format on
@ -657,17 +726,55 @@ static zend_function_entry maxminddb_methods[] = {
PHP_MINIT_FUNCTION(maxminddb) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_EX_NS, NULL);
maxminddb_exception_ce =
zend_register_internal_class_ex(&ce, zend_ce_exception);
INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_NS, maxminddb_methods);
maxminddb_ce = zend_register_internal_class(&ce TSRMLS_CC);
maxminddb_ce->create_object = maxminddb_create_handler;
INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_METADATA_NS, metadata_methods);
metadata_ce = zend_register_internal_class(&ce TSRMLS_CC);
zend_declare_property_null(metadata_ce,
"binaryFormatMajorVersion",
sizeof("binaryFormatMajorVersion") - 1,
ZEND_ACC_PUBLIC);
zend_declare_property_null(metadata_ce,
"binaryFormatMinorVersion",
sizeof("binaryFormatMinorVersion") - 1,
ZEND_ACC_PUBLIC);
zend_declare_property_null(
metadata_ce, "buildEpoch", sizeof("buildEpoch") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(metadata_ce,
"databaseType",
sizeof("databaseType") - 1,
ZEND_ACC_PUBLIC);
zend_declare_property_null(
metadata_ce, "description", sizeof("description") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(
metadata_ce, "ipVersion", sizeof("ipVersion") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(
metadata_ce, "languages", sizeof("languages") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(metadata_ce,
"nodeByteSize",
sizeof("nodeByteSize") - 1,
ZEND_ACC_PUBLIC);
zend_declare_property_null(
metadata_ce, "nodeCount", sizeof("nodeCount") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(
metadata_ce, "recordSize", sizeof("recordSize") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(metadata_ce,
"searchTreeSize",
sizeof("searchTreeSize") - 1,
ZEND_ACC_PUBLIC);
memcpy(&maxminddb_obj_handlers,
zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
maxminddb_obj_handlers.clone_obj = NULL;
#ifdef ZEND_ENGINE_3
maxminddb_obj_handlers.offset = XtOffsetOf(maxminddb_obj, std);
maxminddb_obj_handlers.free_obj = maxminddb_free_storage;
#endif
zend_declare_class_constant_string(maxminddb_ce,
"MMDB_LIB_VERSION",
sizeof("MMDB_LIB_VERSION") - 1,

View File

@ -15,7 +15,7 @@
#ifndef PHP_MAXMINDDB_H
#define PHP_MAXMINDDB_H 1
#define PHP_MAXMINDDB_VERSION "1.6.0"
#define PHP_MAXMINDDB_VERSION "1.10.1"
#define PHP_MAXMINDDB_EXTNAME "maxminddb"
extern zend_module_entry maxminddb_module_entry;

63
vendor/maxmind-db/reader/package.xml vendored Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0"?>
<package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0"
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>maxminddb</name>
<channel>pecl.php.net</channel>
<summary>Reader for the MaxMind DB file format</summary>
<description>This is the PHP extension for reading MaxMind DB files. MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6).</description>
<lead>
<name>Greg Oschwald</name>
<user>oschwald</user>
<email>goschwald@maxmind.com</email>
<active>yes</active>
</lead>
<date>2021-04-14</date>
<version>
<release>1.10.1</release>
<api>1.10.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="https://github.com/maxmind/MaxMind-DB-Reader-php/blob/main/LICENSE">Apache License 2.0</license>
<notes>* Fix a `TypeError` exception in the pure PHP reader when using large
databases on 32-bit PHP builds with the `bcmath` extension. Reported
by dodo1708. GitHub #124.</notes>
<contents>
<dir name="/">
<file role="doc" name="LICENSE"/>
<file role="doc" name="CHANGELOG.md"/>
<file role="doc" name="README.md"/>
<dir name="ext">
<file role="src" name="config.m4"/>
<file role="src" name="config.w32"/>
<file role="src" name="maxminddb.c"/>
<file role="src" name="php_maxminddb.h"/>
<dir name="tests">
<file role="test" name="001-load.phpt"/>
<file role="test" name="002-final.phpt"/>
<file role="test" name="003-open-basedir.phpt"/>
</dir>
</dir>
</dir>
</contents>
<dependencies>
<required>
<php>
<min>7.2.0</min>
</php>
<pearinstaller>
<min>1.10.0</min>
</pearinstaller>
</required>
</dependencies>
<providesextension>maxminddb</providesextension>
<extsrcrelease />
</package>

View File

@ -1,7 +1,10 @@
<?php
declare(strict_types=1);
namespace MaxMind\Db;
use ArgumentCountError;
use BadMethodCallException;
use Exception;
use InvalidArgumentException;
@ -17,15 +20,42 @@ use UnexpectedValueException;
*/
class Reader
{
/**
* @var int
*/
private static $DATA_SECTION_SEPARATOR_SIZE = 16;
/**
* @var string
*/
private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
/**
* @var int
*/
private static $METADATA_START_MARKER_LENGTH = 14;
private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KB
/**
* @var int
*/
private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KiB
/**
* @var Decoder
*/
private $decoder;
/**
* @var resource
*/
private $fileHandle;
/**
* @var int
*/
private $fileSize;
/**
* @var int
*/
private $ipV4Start;
/**
* @var Metadata
*/
private $metadata;
/**
@ -36,39 +66,37 @@ class Reader
* the MaxMind DB file to use
*
* @throws InvalidArgumentException for invalid database path or unknown arguments
* @throws \MaxMind\Db\Reader\InvalidDatabaseException
* @throws InvalidDatabaseException
* if the database is invalid or there is an error reading
* from it
*/
public function __construct($database)
public function __construct(string $database)
{
if (\func_num_args() !== 1) {
throw new InvalidArgumentException(
'The constructor takes exactly one argument.'
throw new ArgumentCountError(
sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args())
);
}
if (!is_readable($database)) {
$fileHandle = @fopen($database, 'rb');
if ($fileHandle === false) {
throw new InvalidArgumentException(
"The file \"$database\" does not exist or is not readable."
);
}
$this->fileHandle = @fopen($database, 'rb');
if ($this->fileHandle === false) {
throw new InvalidArgumentException(
"Error opening \"$database\"."
);
}
$this->fileSize = @filesize($database);
if ($this->fileSize === false) {
$this->fileHandle = $fileHandle;
$fileSize = @filesize($database);
if ($fileSize === false) {
throw new UnexpectedValueException(
"Error determining the size of \"$database\"."
);
}
$this->fileSize = $fileSize;
$start = $this->findMetadataStart($database);
$metadataDecoder = new Decoder($this->fileHandle, $start);
list($metadataArray) = $metadataDecoder->decode($start);
[$metadataArray] = $metadataDecoder->decode($start);
$this->metadata = new Metadata($metadataArray);
$this->decoder = new Decoder(
$this->fileHandle,
@ -91,14 +119,14 @@ class Reader
*
* @return mixed the record for the IP address
*/
public function get($ipAddress)
public function get(string $ipAddress)
{
if (\func_num_args() !== 1) {
throw new InvalidArgumentException(
'Method takes exactly one argument.'
throw new ArgumentCountError(
sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args())
);
}
list($record) = $this->getWithPrefixLen($ipAddress);
[$record] = $this->getWithPrefixLen($ipAddress);
return $record;
}
@ -118,11 +146,11 @@ class Reader
* @return array an array where the first element is the record and the
* second the network prefix length for the record
*/
public function getWithPrefixLen($ipAddress)
public function getWithPrefixLen(string $ipAddress): array
{
if (\func_num_args() !== 1) {
throw new InvalidArgumentException(
'Method takes exactly one argument.'
throw new ArgumentCountError(
sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args())
);
}
@ -132,13 +160,7 @@ class Reader
);
}
if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
throw new InvalidArgumentException(
"The value \"$ipAddress\" is not a valid IP address."
);
}
list($pointer, $prefixLen) = $this->findAddressInTree($ipAddress);
[$pointer, $prefixLen] = $this->findAddressInTree($ipAddress);
if ($pointer === 0) {
return [null, $prefixLen];
}
@ -146,9 +168,16 @@ class Reader
return [$this->resolveDataPointer($pointer), $prefixLen];
}
private function findAddressInTree($ipAddress)
private function findAddressInTree(string $ipAddress): array
{
$rawAddress = unpack('C*', inet_pton($ipAddress));
$packedAddr = @inet_pton($ipAddress);
if ($packedAddr === false) {
throw new InvalidArgumentException(
"The value \"$ipAddress\" is not a valid IP address."
);
}
$rawAddress = unpack('C*', $packedAddr);
$bitCount = \count($rawAddress) * 8;
@ -182,14 +211,18 @@ class Reader
if ($node === $nodeCount) {
// Record is empty
return [0, $i];
} elseif ($node > $nodeCount) {
}
if ($node > $nodeCount) {
// Record is a data pointer
return [$node, $i];
}
throw new InvalidDatabaseException('Something bad happened');
throw new InvalidDatabaseException(
'Invalid or corrupt database. Maximum search depth reached without finding a leaf node'
);
}
private function ipV4StartNode()
private function ipV4StartNode(): int
{
// If we have an IPv4 database, the start node is the first node
if ($this->metadata->ipVersion === 4) {
@ -205,16 +238,17 @@ class Reader
return $node;
}
private function readNode($nodeNumber, $index)
private function readNode(int $nodeNumber, int $index): int
{
$baseOffset = $nodeNumber * $this->metadata->nodeByteSize;
switch ($this->metadata->recordSize) {
case 24:
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3);
list(, $node) = unpack('N', "\x00" . $bytes);
[, $node] = unpack('N', "\x00" . $bytes);
return $node;
case 28:
$bytes = Util::read($this->fileHandle, $baseOffset + 3 * $index, 4);
if ($index === 0) {
@ -222,14 +256,16 @@ class Reader
} else {
$middle = 0x0F & \ord($bytes[0]);
}
list(, $node) = unpack('N', \chr($middle) . substr($bytes, $index, 3));
[, $node] = unpack('N', \chr($middle) . substr($bytes, $index, 3));
return $node;
case 32:
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4);
list(, $node) = unpack('N', $bytes);
[, $node] = unpack('N', $bytes);
return $node;
default:
throw new InvalidDatabaseException(
'Unknown record size: '
@ -238,7 +274,10 @@ class Reader
}
}
private function resolveDataPointer($pointer)
/**
* @return mixed
*/
private function resolveDataPointer(int $pointer)
{
$resolved = $pointer - $this->metadata->nodeCount
+ $this->metadata->searchTreeSize;
@ -248,7 +287,7 @@ class Reader
);
}
list($data) = $this->decoder->decode($resolved);
[$data] = $this->decoder->decode($resolved);
return $data;
}
@ -258,7 +297,7 @@ class Reader
* are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
* an issue, but I suspect it won't be.
*/
private function findMetadataStart($filename)
private function findMetadataStart(string $filename): int
{
$handle = $this->fileHandle;
$fstat = fstat($handle);
@ -278,6 +317,7 @@ class Reader
return $offset + $markerLength;
}
}
throw new InvalidDatabaseException(
"Error opening database file ($filename). " .
'Is this a valid MaxMind DB file?'
@ -290,11 +330,11 @@ class Reader
*
* @return Metadata object for the database
*/
public function metadata()
public function metadata(): Metadata
{
if (\func_num_args()) {
throw new InvalidArgumentException(
'Method takes no arguments.'
throw new ArgumentCountError(
sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args())
);
}
@ -306,7 +346,7 @@ class Reader
);
}
return $this->metadata;
return clone $this->metadata;
}
/**
@ -315,8 +355,14 @@ class Reader
* @throws Exception
* if an I/O error occurs
*/
public function close()
public function close(): void
{
if (\func_num_args()) {
throw new ArgumentCountError(
sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args())
);
}
if (!\is_resource($this->fileHandle)) {
throw new BadMethodCallException(
'Attempt to close a closed MaxMind DB.'

View File

@ -1,63 +1,61 @@
<?php
declare(strict_types=1);
namespace MaxMind\Db\Reader;
// @codingStandardsIgnoreLine
use RuntimeException;
/**
* @ignore
*
* We subtract 1 from the log to protect against precision loss.
*/
\define(__NAMESPACE__ . '\_MM_MAX_INT_BYTES', (log(PHP_INT_MAX, 2) - 1) / 8);
class Decoder
{
/**
* @var resource
*/
private $fileStream;
/**
* @var int
*/
private $pointerBase;
/**
* @var float
*/
private $pointerBaseByteSize;
// This is only used for unit testing
/**
* This is only used for unit testing.
*
* @var bool
*/
private $pointerTestHack;
/**
* @var bool
*/
private $switchByteOrder;
/** @ignore */
const _EXTENDED = 0;
/** @ignore */
const _POINTER = 1;
/** @ignore */
const _UTF8_STRING = 2;
/** @ignore */
const _DOUBLE = 3;
/** @ignore */
const _BYTES = 4;
/** @ignore */
const _UINT16 = 5;
/** @ignore */
const _UINT32 = 6;
/** @ignore */
const _MAP = 7;
/** @ignore */
const _INT32 = 8;
/** @ignore */
const _UINT64 = 9;
/** @ignore */
const _UINT128 = 10;
/** @ignore */
const _ARRAY = 11;
/** @ignore */
const _CONTAINER = 12;
/** @ignore */
const _END_MARKER = 13;
/** @ignore */
const _BOOLEAN = 14;
/** @ignore */
const _FLOAT = 15;
private const _EXTENDED = 0;
private const _POINTER = 1;
private const _UTF8_STRING = 2;
private const _DOUBLE = 3;
private const _BYTES = 4;
private const _UINT16 = 5;
private const _UINT32 = 6;
private const _MAP = 7;
private const _INT32 = 8;
private const _UINT64 = 9;
private const _UINT128 = 10;
private const _ARRAY = 11;
private const _CONTAINER = 12;
private const _END_MARKER = 13;
private const _BOOLEAN = 14;
private const _FLOAT = 15;
/**
* @param resource $fileStream
*/
public function __construct(
$fileStream,
$pointerBase = 0,
$pointerTestHack = false
int $pointerBase = 0,
bool $pointerTestHack = false
) {
$this->fileStream = $fileStream;
$this->pointerBase = $pointerBase;
@ -68,7 +66,7 @@ class Decoder
$this->switchByteOrder = $this->isPlatformLittleEndian();
}
public function decode($offset)
public function decode(int $offset): array
{
$ctrlByte = \ord(Util::read($this->fileStream, $offset, 1));
++$offset;
@ -79,14 +77,14 @@ class Decoder
// use the size to determine the length of the pointer and then follow
// it.
if ($type === self::_POINTER) {
list($pointer, $offset) = $this->decodePointer($ctrlByte, $offset);
[$pointer, $offset] = $this->decodePointer($ctrlByte, $offset);
// for unit testing
if ($this->pointerTestHack) {
return [$pointer];
}
list($result) = $this->decode($pointer);
[$result] = $this->decode($pointer);
return [$result, $offset];
}
@ -108,43 +106,51 @@ class Decoder
++$offset;
}
list($size, $offset) = $this->sizeFromCtrlByte($ctrlByte, $offset);
[$size, $offset] = $this->sizeFromCtrlByte($ctrlByte, $offset);
return $this->decodeByType($type, $offset, $size);
}
private function decodeByType($type, $offset, $size)
private function decodeByType(int $type, int $offset, int $size): array
{
switch ($type) {
case self::_MAP:
return $this->decodeMap($size, $offset);
case self::_ARRAY:
return $this->decodeArray($size, $offset);
case self::_BOOLEAN:
return [$this->decodeBoolean($size), $offset];
}
$newOffset = $offset + $size;
$bytes = Util::read($this->fileStream, $offset, $size);
switch ($type) {
case self::_BYTES:
case self::_UTF8_STRING:
return [$bytes, $newOffset];
case self::_DOUBLE:
$this->verifySize(8, $size);
return [$this->decodeDouble($bytes), $newOffset];
case self::_FLOAT:
$this->verifySize(4, $size);
return [$this->decodeFloat($bytes), $newOffset];
case self::_INT32:
return [$this->decodeInt32($bytes, $size), $newOffset];
case self::_UINT16:
case self::_UINT32:
case self::_UINT64:
case self::_UINT128:
return [$this->decodeUint($bytes, $size), $newOffset];
default:
throw new InvalidDatabaseException(
'Unknown or unexpected type: ' . $type
@ -152,7 +158,7 @@ class Decoder
}
}
private function verifySize($expected, $actual)
private function verifySize(int $expected, int $actual): void
{
if ($expected !== $actual) {
throw new InvalidDatabaseException(
@ -161,86 +167,82 @@ class Decoder
}
}
private function decodeArray($size, $offset)
private function decodeArray(int $size, int $offset): array
{
$array = [];
for ($i = 0; $i < $size; ++$i) {
list($value, $offset) = $this->decode($offset);
array_push($array, $value);
[$value, $offset] = $this->decode($offset);
$array[] = $value;
}
return [$array, $offset];
}
private function decodeBoolean($size)
private function decodeBoolean(int $size): bool
{
return $size === 0 ? false : true;
return $size !== 0;
}
private function decodeDouble($bits)
private function decodeDouble(string $bytes): float
{
// This assumes IEEE 754 doubles, but most (all?) modern platforms
// use them.
//
// We are not using the "E" format as that was only added in
// 7.0.15 and 7.1.1. As such, we must switch byte order on
// little endian machines.
list(, $double) = unpack('d', $this->maybeSwitchByteOrder($bits));
[, $double] = unpack('E', $bytes);
return $double;
}
private function decodeFloat($bits)
private function decodeFloat(string $bytes): float
{
// This assumes IEEE 754 floats, but most (all?) modern platforms
// use them.
//
// We are not using the "G" format as that was only added in
// 7.0.15 and 7.1.1. As such, we must switch byte order on
// little endian machines.
list(, $float) = unpack('f', $this->maybeSwitchByteOrder($bits));
[, $float] = unpack('G', $bytes);
return $float;
}
private function decodeInt32($bytes, $size)
private function decodeInt32(string $bytes, int $size): int
{
switch ($size) {
case 0:
return 0;
case 1:
case 2:
case 3:
$bytes = str_pad($bytes, 4, "\x00", STR_PAD_LEFT);
$bytes = str_pad($bytes, 4, "\x00", \STR_PAD_LEFT);
break;
case 4:
break;
default:
throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"
);
}
list(, $int) = unpack('l', $this->maybeSwitchByteOrder($bytes));
[, $int] = unpack('l', $this->maybeSwitchByteOrder($bytes));
return $int;
}
private function decodeMap($size, $offset)
private function decodeMap(int $size, int $offset): array
{
$map = [];
for ($i = 0; $i < $size; ++$i) {
list($key, $offset) = $this->decode($offset);
list($value, $offset) = $this->decode($offset);
[$key, $offset] = $this->decode($offset);
[$value, $offset] = $this->decode($offset);
$map[$key] = $value;
}
return [$map, $offset];
}
private function decodePointer($ctrlByte, $offset)
private function decodePointer(int $ctrlByte, int $offset): array
{
$pointerSize = (($ctrlByte >> 3) & 0x3) + 1;
@ -250,46 +252,56 @@ class Decoder
switch ($pointerSize) {
case 1:
$packed = \chr($ctrlByte & 0x7) . $buffer;
list(, $pointer) = unpack('n', $packed);
[, $pointer] = unpack('n', $packed);
$pointer += $this->pointerBase;
break;
case 2:
$packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer;
list(, $pointer) = unpack('N', $packed);
[, $pointer] = unpack('N', $packed);
$pointer += $this->pointerBase + 2048;
break;
case 3:
$packed = \chr($ctrlByte & 0x7) . $buffer;
// It is safe to use 'N' here, even on 32 bit machines as the
// first bit is 0.
list(, $pointer) = unpack('N', $packed);
[, $pointer] = unpack('N', $packed);
$pointer += $this->pointerBase + 526336;
break;
case 4:
// We cannot use unpack here as we might overflow on 32 bit
// machines
$pointerOffset = $this->decodeUint($buffer, $pointerSize);
$byteLength = $pointerSize + $this->pointerBaseByteSize;
$pointerBase = $this->pointerBase;
if ($byteLength <= _MM_MAX_INT_BYTES) {
$pointer = $pointerOffset + $this->pointerBase;
} elseif (\extension_loaded('gmp')) {
$pointer = gmp_strval(gmp_add($pointerOffset, $this->pointerBase));
} elseif (\extension_loaded('bcmath')) {
$pointer = bcadd($pointerOffset, $this->pointerBase);
if (\PHP_INT_MAX - $pointerBase >= $pointerOffset) {
$pointer = $pointerOffset + $pointerBase;
} else {
throw new RuntimeException(
'The gmp or bcmath extension must be installed to read this database.'
'The database offset is too large to be represented on your platform.'
);
}
break;
default:
throw new InvalidDatabaseException(
'Unexpected pointer size ' . $pointerSize
);
}
return [$pointer, $offset];
}
private function decodeUint($bytes, $byteLength)
// @phpstan-ignore-next-line
private function decodeUint(string $bytes, int $byteLength)
{
if ($byteLength === 0) {
return 0;
@ -297,16 +309,22 @@ class Decoder
$integer = 0;
// PHP integers are signed. PHP_INT_SIZE - 1 is the number of
// complete bytes that can be converted to an integer. However,
// we can convert another byte if the leading bit is zero.
$useRealInts = $byteLength <= \PHP_INT_SIZE - 1
|| ($byteLength === \PHP_INT_SIZE && (\ord($bytes[0]) & 0x80) === 0);
for ($i = 0; $i < $byteLength; ++$i) {
$part = \ord($bytes[$i]);
// We only use gmp or bcmath if the final value is too big
if ($byteLength <= _MM_MAX_INT_BYTES) {
if ($useRealInts) {
$integer = ($integer << 8) + $part;
} elseif (\extension_loaded('gmp')) {
$integer = gmp_strval(gmp_add(gmp_mul($integer, 256), $part));
$integer = gmp_strval(gmp_add(gmp_mul((string) $integer, '256'), $part));
} elseif (\extension_loaded('bcmath')) {
$integer = bcadd(bcmul($integer, 256), $part);
$integer = bcadd(bcmul((string) $integer, '256'), (string) $part);
} else {
throw new RuntimeException(
'The gmp or bcmath extension must be installed to read this database.'
@ -317,9 +335,9 @@ class Decoder
return $integer;
}
private function sizeFromCtrlByte($ctrlByte, $offset)
private function sizeFromCtrlByte(int $ctrlByte, int $offset): array
{
$size = $ctrlByte & 0x1f;
$size = $ctrlByte & 0x1F;
if ($size < 29) {
return [$size, $offset];
@ -331,22 +349,22 @@ class Decoder
if ($size === 29) {
$size = 29 + \ord($bytes);
} elseif ($size === 30) {
list(, $adjust) = unpack('n', $bytes);
[, $adjust] = unpack('n', $bytes);
$size = 285 + $adjust;
} elseif ($size > 30) {
list(, $adjust) = unpack('N', "\x00" . $bytes);
} else {
[, $adjust] = unpack('N', "\x00" . $bytes);
$size = $adjust + 65821;
}
return [$size, $offset + $bytesToRead];
}
private function maybeSwitchByteOrder($bytes)
private function maybeSwitchByteOrder(string $bytes): string
{
return $this->switchByteOrder ? strrev($bytes) : $bytes;
}
private function isPlatformLittleEndian()
private function isPlatformLittleEndian(): bool
{
$testint = 0x00FF;
$packed = pack('S', $testint);

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Db\Reader;
use Exception;

View File

@ -1,71 +1,100 @@
<?php
declare(strict_types=1);
namespace MaxMind\Db\Reader;
use ArgumentCountError;
/**
* This class provides the metadata for the MaxMind DB file.
*
* @property int $nodeCount This is an unsigned 32-bit
* integer indicating the number of
* nodes in the search tree.
* @property int $recordSize This is an unsigned 16-bit
* integer. It indicates the number
* of bits in a record in the search
* tree. Note that each node
* consists of two records.
* @property int $ipVersion This is an unsigned 16-bit
* integer which is always 4 or 6.
* It indicates whether the database
* contains IPv4 or IPv6 address
* data.
* @property string $databaseType This is a string that indicates
* the structure of each data record
* associated with an IP address.
* The actual definition of these
* structures is left up to the
* database creator.
* @property array $languages An array of strings, each of
* which is a language code. A given
* record may contain data items
* that have been localized to some
* or all of these languages. This
* may be undefined.
* @property int $binaryFormatMajorVersion This is an unsigned 16-bit
* integer indicating the major
* version number for the database's
* binary format.
* @property int $binaryFormatMinorVersion This is an unsigned 16-bit
* integer indicating the minor
* version number for the database's
* binary format.
* @property int $buildEpoch This is an unsigned 64-bit
* integer that contains the
* database build timestamp as a
* Unix epoch value.
* @property array $description This key will always point to a
* map (associative array). The keys
* of that map will be language
* codes, and the values will be a
* description in that language as a
* UTF-8 string. May be undefined
* for some databases.
*/
class Metadata
{
private $binaryFormatMajorVersion;
private $binaryFormatMinorVersion;
private $buildEpoch;
private $databaseType;
private $description;
private $ipVersion;
private $languages;
private $nodeByteSize;
private $nodeCount;
private $recordSize;
private $searchTreeSize;
/**
* This is an unsigned 16-bit integer indicating the major version number
* for the database's binary format.
*
* @var int
*/
public $binaryFormatMajorVersion;
/**
* This is an unsigned 16-bit integer indicating the minor version number
* for the database's binary format.
*
* @var int
*/
public $binaryFormatMinorVersion;
/**
* This is an unsigned 64-bit integer that contains the database build
* timestamp as a Unix epoch value.
*
* @var int
*/
public $buildEpoch;
/**
* This is a string that indicates the structure of each data record
* associated with an IP address. The actual definition of these
* structures is left up to the database creator.
*
* @var string
*/
public $databaseType;
/**
* This key will always point to a map (associative array). The keys of
* that map will be language codes, and the values will be a description
* in that language as a UTF-8 string. May be undefined for some
* databases.
*
* @var array
*/
public $description;
/**
* This is an unsigned 16-bit integer which is always 4 or 6. It indicates
* whether the database contains IPv4 or IPv6 address data.
*
* @var int
*/
public $ipVersion;
/**
* An array of strings, each of which is a language code. A given record
* may contain data items that have been localized to some or all of
* these languages. This may be undefined.
*
* @var array
*/
public $languages;
/**
* @var int
*/
public $nodeByteSize;
/**
* This is an unsigned 32-bit integer indicating the number of nodes in
* the search tree.
*
* @var int
*/
public $nodeCount;
/**
* This is an unsigned 16-bit integer. It indicates the number of bits in a
* record in the search tree. Note that each node consists of two records.
*
* @var int
*/
public $recordSize;
/**
* @var int
*/
public $searchTreeSize;
public function __construct($metadata)
public function __construct(array $metadata)
{
if (\func_num_args() !== 1) {
throw new ArgumentCountError(
sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args())
);
}
$this->binaryFormatMajorVersion =
$metadata['binary_format_major_version'];
$this->binaryFormatMinorVersion =
@ -80,9 +109,4 @@ class Metadata
$this->nodeByteSize = $this->recordSize / 4;
$this->searchTreeSize = $this->nodeCount * $this->nodeByteSize;
}
public function __get($var)
{
return $this->$var;
}
}

View File

@ -1,10 +1,15 @@
<?php
declare(strict_types=1);
namespace MaxMind\Db\Reader;
class Util
{
public static function read($stream, $offset, $numberOfBytes)
/**
* @param resource $stream
*/
public static function read($stream, int $offset, int $numberOfBytes): string
{
if ($numberOfBytes === 0) {
return '';
@ -15,10 +20,11 @@ class Util
// We check that the number of bytes read is equal to the number
// asked for. We use ftell as getting the length of $value is
// much slower.
if (ftell($stream) - $offset === $numberOfBytes) {
if ($value !== false && ftell($stream) - $offset === $numberOfBytes) {
return $value;
}
}
throw new InvalidDatabaseException(
'The MaxMind DB file contains bad data'
);

View File

@ -1,6 +1,26 @@
CHANGELOG
=========
0.9.0 (2022-03-28)
------------------
* Improved internal type hint usage.
0.8.1 (2020-11-02)
------------------
* We now correctly handle responses without a `Content-Type` header. In 0.8.0,
such responses could lead to a type error. In particular, this affected the
minFraud Report Transaction endpoint, which returns a response with no
content. Reported by Dmitry Malashko. GitHub #99 on
`maxmind/minfraud-api-php`.
0.8.0 (2020-10-01)
------------------
* PHP 7.2 or greater is now required.
* Added additional type hints.
0.7.0 (2020-05-06)
------------------

View File

@ -5,7 +5,7 @@ shared code between MaxMind's various web service client APIs.
## Requirements ##
The library requires PHP 5.6 or greater.
The library requires PHP 7.2 or greater.
There are several other dependencies as defined in the `composer.json` file.

View File

@ -12,15 +12,16 @@
}
],
"require": {
"php": ">=5.6",
"php": ">=7.2",
"composer/ca-bundle": "^1.0.3",
"ext-curl": "*",
"ext-json": "*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0",
"squizlabs/php_codesniffer": "3.*"
"friendsofphp/php-cs-fixer": "3.*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*",
"phpstan/phpstan": "*"
},
"autoload": {
"psr-4": {

View File

@ -51,10 +51,6 @@ fi
git push
message="$version
$notes"
hub release create -m "$message" "$tag"
gh release create --target "$(git branch --show-current)" -t "$version" -n "$notes" "$tag"
git push --tags

View File

@ -0,0 +1,7 @@
parameters:
level: 6
paths:
- src
- tests
checkMissingIterableValueType: false

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**
@ -9,6 +11,8 @@ class HttpException extends WebServiceException
{
/**
* The URI queried.
*
* @var string
*/
private $uri;
@ -19,21 +23,21 @@ class HttpException extends WebServiceException
* @param \Exception $previous the previous exception, if any
*/
public function __construct(
$message,
$httpStatus,
$uri,
string $message,
int $httpStatus,
string $uri,
\Exception $previous = null
) {
$this->uri = $uri;
parent::__construct($message, $httpStatus, $previous);
}
public function getUri()
public function getUri(): string
{
return $this->uri;
}
public function getStatusCode()
public function getStatusCode(): int
{
return $this->getCode();
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**
@ -9,28 +11,30 @@ class InvalidRequestException extends HttpException
{
/**
* The code returned by the MaxMind web service.
*
* @var string
*/
private $error;
/**
* @param string $message the exception message
* @param int $error the error code returned by the MaxMind web service
* @param string $error the error code returned by the MaxMind web service
* @param int $httpStatus the HTTP status code of the response
* @param string $uri the URI queries
* @param \Exception $previous the previous exception, if any
*/
public function __construct(
$message,
$error,
$httpStatus,
$uri,
string $message,
string $error,
int $httpStatus,
string $uri,
\Exception $previous = null
) {
$this->error = $error;
parent::__construct($message, $httpStatus, $uri, $previous);
}
public function getErrorCode()
public function getErrorCode(): string
{
return $this->error;
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
class IpAddressNotFoundException extends InvalidRequestException

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\Exception;
/**

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\WebService;
use Composer\CaBundle\CaBundle;
@ -22,16 +24,56 @@ use MaxMind\WebService\Http\RequestFactory;
*/
class Client
{
const VERSION = '0.2.0';
public const VERSION = '0.2.0';
/**
* @var string|null
*/
private $caBundle;
/**
* @var float|null
*/
private $connectTimeout;
/**
* @var string
*/
private $host = 'api.maxmind.com';
/**
* @var bool
*/
private $useHttps = true;
/**
* @var RequestFactory
*/
private $httpRequestFactory;
/**
* @var string
*/
private $licenseKey;
/**
* @var string|null
*/
private $proxy;
/**
* @var float|null
*/
private $timeout;
/**
* @var string
*/
private $userAgentPrefix;
/**
* @var int
*/
private $accountId;
/**
@ -39,6 +81,7 @@ class Client
* @param string $licenseKey your MaxMind license key
* @param array $options an array of options. Possible keys:
* * `host` - The host to use when connecting to the web service.
* * `useHttps` - A boolean flag for sending the request via https.(True by default)
* * `userAgent` - The prefix of the User-Agent to use in the request.
* * `caBundle` - The bundle of CA root certificates to use in the request.
* * `connectTimeout` - The connect timeout to use for the request.
@ -47,9 +90,9 @@ class Client
* username, and password, e.g., `http://username:password@127.0.0.1:10`.
*/
public function __construct(
$accountId,
$licenseKey,
$options = []
int $accountId,
string $licenseKey,
array $options = []
) {
$this->accountId = $accountId;
$this->licenseKey = $licenseKey;
@ -61,6 +104,9 @@ class Client
if (isset($options['host'])) {
$this->host = $options['host'];
}
if (isset($options['useHttps'])) {
$this->useHttps = $options['useHttps'];
}
if (isset($options['userAgent'])) {
$this->userAgentPrefix = $options['userAgent'] . ' ';
}
@ -96,9 +142,9 @@ class Client
* @throws WebServiceException when some other error occurs. This also
* serves as the base class for the above exceptions.
*
* @return array The decoded content of a successful response
* @return array|null The decoded content of a successful response
*/
public function post($service, $path, $input)
public function post(string $service, string $path, array $input): ?array
{
$requestBody = json_encode($input);
if ($requestBody === false) {
@ -113,7 +159,7 @@ class Client
['Content-Type: application/json']
);
list($statusCode, $contentType, $responseBody) = $request->post($requestBody);
[$statusCode, $contentType, $responseBody] = $request->post($requestBody);
return $this->handleResponse(
$statusCode,
@ -124,11 +170,13 @@ class Client
);
}
public function get($service, $path)
public function get(string $service, string $path): ?array
{
$request = $this->createRequest($path);
$request = $this->createRequest(
$path
);
list($statusCode, $contentType, $responseBody) = $request->get();
[$statusCode, $contentType, $responseBody] = $request->get();
return $this->handleResponse(
$statusCode,
@ -139,15 +187,15 @@ class Client
);
}
private function userAgent()
private function userAgent(): string
{
$curlVersion = curl_version();
return $this->userAgentPrefix . 'MaxMind-WS-API/' . self::VERSION . ' PHP/' . PHP_VERSION .
return $this->userAgentPrefix . 'MaxMind-WS-API/' . self::VERSION . ' PHP/' . \PHP_VERSION .
' curl/' . $curlVersion['version'];
}
private function createRequest($path, $headers = [])
private function createRequest(string $path, array $headers = []): Http\Request
{
array_push(
$headers,
@ -171,8 +219,8 @@ class Client
/**
* @param int $statusCode the HTTP status code of the response
* @param string $contentType the Content-Type of the response
* @param string $responseBody the response body
* @param string|null $contentType the Content-Type of the response
* @param string|null $responseBody the response body
* @param string $service the name of the service
* @param string $path the path used in the request
*
@ -185,15 +233,15 @@ class Client
* @throws WebServiceException when some other error occurs. This also
* serves as the base class for the above exceptions
*
* @return array The decoded content of a successful response
* @return array|null The decoded content of a successful response
*/
private function handleResponse(
$statusCode,
$contentType,
$responseBody,
$service,
$path
) {
int $statusCode,
?string $contentType,
?string $responseBody,
string $service,
string $path
): ?array {
if ($statusCode >= 400 && $statusCode <= 499) {
$this->handle4xx($statusCode, $contentType, $responseBody, $service, $path);
} elseif ($statusCode >= 500) {
@ -208,20 +256,26 @@ class Client
/**
* @return string describing the JSON error
*/
private function jsonErrorDescription()
private function jsonErrorDescription(): string
{
$errno = json_last_error();
switch ($errno) {
case JSON_ERROR_DEPTH:
case \JSON_ERROR_DEPTH:
return 'The maximum stack depth has been exceeded.';
case JSON_ERROR_STATE_MISMATCH:
case \JSON_ERROR_STATE_MISMATCH:
return 'Invalid or malformed JSON.';
case JSON_ERROR_CTRL_CHAR:
case \JSON_ERROR_CTRL_CHAR:
return 'Control character error.';
case JSON_ERROR_SYNTAX:
case \JSON_ERROR_SYNTAX:
return 'Syntax error.';
case JSON_ERROR_UTF8:
case \JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters.';
default:
return "Other JSON error ($errno).";
}
@ -232,15 +286,15 @@ class Client
*
* @return string the constructed URL
*/
private function urlFor($path)
private function urlFor(string $path): string
{
return 'https://' . $this->host . $path;
return ($this->useHttps ? 'https://' : 'http://') . $this->host . $path;
}
/**
* @param int $statusCode the HTTP status code
* @param string $contentType the response content-type
* @param string $body the response body
* @param string|null $contentType the response content-type
* @param string|null $body the response body
* @param string $service the service name
* @param string $path the path used in the request
*
@ -250,20 +304,20 @@ class Client
* @throws InvalidRequestException
*/
private function handle4xx(
$statusCode,
$contentType,
$body,
$service,
$path
) {
if (\strlen($body) === 0) {
int $statusCode,
?string $contentType,
?string $body,
string $service,
string $path
): void {
if ($body === null || $body === '') {
throw new HttpException(
"Received a $statusCode error for $service with no body",
$statusCode,
$this->urlFor($path)
);
}
if (!strstr($contentType, 'json')) {
if ($contentType === null || !strstr($contentType, 'json')) {
throw new HttpException(
"Received a $statusCode error for $service with " .
'the following body: ' . $body,
@ -311,11 +365,11 @@ class Client
* @throws InsufficientFundsException
*/
private function handleWebServiceError(
$message,
$code,
$statusCode,
$path
) {
string $message,
string $code,
int $statusCode,
string $path
): void {
switch ($code) {
case 'IP_ADDRESS_NOT_FOUND':
case 'IP_ADDRESS_RESERVED':
@ -325,6 +379,7 @@ class Client
$statusCode,
$this->urlFor($path)
);
case 'ACCOUNT_ID_REQUIRED':
case 'ACCOUNT_ID_UNKNOWN':
case 'AUTHORIZATION_INVALID':
@ -337,6 +392,7 @@ class Client
$statusCode,
$this->urlFor($path)
);
case 'OUT_OF_QUERIES':
case 'INSUFFICIENT_FUNDS':
throw new InsufficientFundsException(
@ -345,6 +401,7 @@ class Client
$statusCode,
$this->urlFor($path)
);
case 'PERMISSION_REQUIRED':
throw new PermissionRequiredException(
$message,
@ -352,6 +409,7 @@ class Client
$statusCode,
$this->urlFor($path)
);
default:
throw new InvalidRequestException(
$message,
@ -369,7 +427,7 @@ class Client
*
* @throws HttpException
*/
private function handle5xx($statusCode, $service, $path)
private function handle5xx(int $statusCode, string $service, string $path): void
{
throw new HttpException(
"Received a server error ($statusCode) for $service",
@ -385,7 +443,7 @@ class Client
*
* @throws HttpException
*/
private function handleUnexpectedStatus($statusCode, $service, $path)
private function handleUnexpectedStatus(int $statusCode, string $service, string $path): void
{
throw new HttpException(
'Received an unexpected HTTP status ' .
@ -397,7 +455,7 @@ class Client
/**
* @param int $statusCode the HTTP status code
* @param string $body the successful request body
* @param string|null $body the successful request body
* @param string $service the service name
*
* @throws WebServiceException if a response body is included but not
@ -405,13 +463,13 @@ class Client
* included, or is expected and included
* but cannot be decoded as JSON
*
* @return array the decoded request body
* @return array|null the decoded request body
*/
private function handleSuccess($statusCode, $body, $service)
private function handleSuccess(int $statusCode, ?string $body, string $service): ?array
{
// A 204 should have no response body
if ($statusCode === 204) {
if (\strlen($body) !== 0) {
if ($body !== null && $body !== '') {
throw new WebServiceException(
"Received a 204 response for $service along with an " .
"unexpected HTTP body: $body"
@ -422,7 +480,7 @@ class Client
}
// A 200 should have a valid JSON body
if (\strlen($body) === 0) {
if ($body === null || $body === '') {
throw new WebServiceException(
"Received a 200 response for $service but did not " .
'receive a HTTP body.'
@ -441,7 +499,7 @@ class Client
return $decodedContent;
}
private function getCaBundle()
private function getCaBundle(): ?string
{
$curlVersion = curl_version();

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\WebService\Http;
use MaxMind\Exception\HttpException;
@ -12,7 +14,7 @@ use MaxMind\Exception\HttpException;
class CurlRequest implements Request
{
/**
* @var resource
* @var \CurlHandle
*/
private $ch;
@ -26,11 +28,7 @@ class CurlRequest implements Request
*/
private $options;
/**
* @param string $url
* @param array $options
*/
public function __construct($url, $options)
public function __construct(string $url, array $options)
{
$this->url = $url;
$this->options = $options;
@ -38,69 +36,65 @@ class CurlRequest implements Request
}
/**
* @param string $body
*
* @throws HttpException
*
* @return array
*/
public function post($body)
public function post(string $body): array
{
$curl = $this->createCurl();
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
curl_setopt($curl, \CURLOPT_POST, true);
curl_setopt($curl, \CURLOPT_POSTFIELDS, $body);
return $this->execute($curl);
}
public function get()
public function get(): array
{
$curl = $this->createCurl();
curl_setopt($curl, CURLOPT_HTTPGET, true);
curl_setopt($curl, \CURLOPT_HTTPGET, true);
return $this->execute($curl);
}
/**
* @return resource
* @return \CurlHandle
*/
private function createCurl()
{
curl_reset($this->ch);
$opts = [];
$opts[CURLOPT_URL] = $this->url;
$opts[\CURLOPT_URL] = $this->url;
if (!empty($this->options['caBundle'])) {
$opts[CURLOPT_CAINFO] = $this->options['caBundle'];
$opts[\CURLOPT_CAINFO] = $this->options['caBundle'];
}
$opts[CURLOPT_ENCODING] = '';
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
$opts[CURLOPT_FOLLOWLOCATION] = false;
$opts[CURLOPT_SSL_VERIFYPEER] = true;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[\CURLOPT_ENCODING] = '';
$opts[\CURLOPT_SSL_VERIFYHOST] = 2;
$opts[\CURLOPT_FOLLOWLOCATION] = false;
$opts[\CURLOPT_SSL_VERIFYPEER] = true;
$opts[\CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_HTTPHEADER] = $this->options['headers'];
$opts[CURLOPT_USERAGENT] = $this->options['userAgent'];
$opts[CURLOPT_PROXY] = $this->options['proxy'];
$opts[\CURLOPT_HTTPHEADER] = $this->options['headers'];
$opts[\CURLOPT_USERAGENT] = $this->options['userAgent'];
$opts[\CURLOPT_PROXY] = $this->options['proxy'];
// The defined()s are here as the *_MS opts are not available on older
// cURL versions
$connectTimeout = $this->options['connectTimeout'];
if (\defined('CURLOPT_CONNECTTIMEOUT_MS')) {
$opts[CURLOPT_CONNECTTIMEOUT_MS] = ceil($connectTimeout * 1000);
$opts[\CURLOPT_CONNECTTIMEOUT_MS] = ceil($connectTimeout * 1000);
} else {
$opts[CURLOPT_CONNECTTIMEOUT] = ceil($connectTimeout);
$opts[\CURLOPT_CONNECTTIMEOUT] = ceil($connectTimeout);
}
$timeout = $this->options['timeout'];
if (\defined('CURLOPT_TIMEOUT_MS')) {
$opts[CURLOPT_TIMEOUT_MS] = ceil($timeout * 1000);
$opts[\CURLOPT_TIMEOUT_MS] = ceil($timeout * 1000);
} else {
$opts[CURLOPT_TIMEOUT] = ceil($timeout);
$opts[\CURLOPT_TIMEOUT] = ceil($timeout);
}
curl_setopt_array($this->ch, $opts);
@ -109,13 +103,11 @@ class CurlRequest implements Request
}
/**
* @param resource $curl
* @param \CurlHandle $curl
*
* @throws HttpException
*
* @return array
*/
private function execute($curl)
private function execute($curl): array
{
$body = curl_exec($curl);
if ($errno = curl_errno($curl)) {
@ -128,9 +120,17 @@ class CurlRequest implements Request
);
}
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
$statusCode = curl_getinfo($curl, \CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($curl, \CURLINFO_CONTENT_TYPE);
return [$statusCode, $contentType, $body];
return [
$statusCode,
// The PHP docs say "Content-Type: of the requested document. NULL
// indicates server did not send valid Content-Type: header" for
// CURLINFO_CONTENT_TYPE. However, it will return FALSE if no header
// is set. To keep our types simple, we return null in this case.
($contentType === false ? null : $contentType),
$body,
];
}
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\WebService\Http;
/**
@ -9,21 +11,9 @@ namespace MaxMind\WebService\Http;
*/
interface Request
{
/**
* @param string $url
* @param array $options
*/
public function __construct($url, $options);
public function __construct(string $url, array $options);
/**
* @param string $body
*
* @return mixed
*/
public function post($body);
public function post(string $body): array;
/**
* @return mixed
*/
public function get();
public function get(): array;
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace MaxMind\WebService\Http;
/**
@ -14,7 +16,7 @@ class RequestFactory
* done the connection is kept alive, SSL resumption can be used
* etcetera.
*
* @var resource
* @var \CurlHandle|null
*/
private $ch;
@ -25,6 +27,9 @@ class RequestFactory
}
}
/**
* @return \CurlHandle
*/
private function getCurlHandle()
{
if (empty($this->ch)) {
@ -34,13 +39,7 @@ class RequestFactory
return $this->ch;
}
/**
* @param string $url
* @param array $options
*
* @return Request
*/
public function request($url, $options)
public function request(string $url, array $options): Request
{
$options['curlHandle'] = $this->getCurlHandle();

View File

@ -1,68 +0,0 @@
name: CI
on: [push]
jobs:
old:
name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: ['ubuntu-18.04']
php-versions: ['5.3', '5.4', '5.5', '5.6', '7.0']
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl
ini-values: max_execution_time=600, memory_limit=256M, error_reporting=-1, display_errors=On
coverage: none
- name: Use Composer 1.x
run: composer self-update --1
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"
- name: PHPUnit tests
run: vendor/bin/phpunit
moderate-modern:
name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: ['ubuntu-latest']
php-versions: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
continue-on-error: ${{ matrix.php-versions == '8.1' }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, sodium
ini-values: error_reporting=-1, display_errors=On
coverage: none
- name: Install Composer dependencies (PHP < 8.1)
if: ${{ matrix.php-versions != '8.1' }}
uses: "ramsey/composer-install@v2"
- name: Install Composer dependencies - ignore-platform-reqs (PHP 8.1)
if: ${{ matrix.php-versions == '8.1' }}
uses: "ramsey/composer-install@v2"
with:
composer-options: --ignore-platform-reqs
- name: PHPUnit tests
run: vendor/bin/phpunit

View File

@ -1,33 +0,0 @@
name: Psalm
on: [push]
jobs:
psalm:
name: Psalm on PHP ${{ matrix.php-versions }}
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: ['ubuntu-latest']
php-versions: ['7.4']
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: psalm:4
coverage: none
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"
with:
composer-options: --no-dev
- name: Put Psalm config in place
run: cp psalm-above-3.xml psalm.xml
- name: Static Analysis
run: psalm

View File

@ -1,9 +0,0 @@
/.idea/
/.php_cs.cache
/.phpunit.result.cache
/box/
/dist/sodium-compat.phar
/dist/sodium-compat.phar.sig
/composer.lock
/vendor/
/psalm.xml

View File

@ -1,27 +0,0 @@
build: false
shallow_clone: false
platform:
- x86
- x64
clone_folder: C:\projects\sodium_compat
image: Visual Studio 2017
install:
- cinst -y OpenSSL.Light
- SET PATH=C:\Program Files\OpenSSL;%PATH%
- sc config wuauserv start= auto
- net start wuauserv
- cinst -y php --version 5.6.30
- cd c:\tools\php56
- copy php.ini-production php.ini
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- cd C:\projects\sodium_compat
- SET PATH=C:\tools\php56;%PATH%
- php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe
- php.exe composer.phar install --prefer-source --no-interaction
test_script:
- cd C:\projects\sodium_compat
- vendor\bin\phpunit.bat tests/Windows32Test.php

View File

@ -1,4 +0,0 @@
<?php
require_once 'autoload.php';
ParagonIE_Sodium_Compat::$fastMult = true;

View File

@ -1,6 +0,0 @@
<?php
require_once 'autoload.php';
define('DO_PEDANTIC_TEST', true);
ParagonIE_Sodium_Compat::$fastMult = true;

View File

@ -1,8 +0,0 @@
<?php
require_once (dirname(__FILE__) . '/vendor/autoload.php');
if (PHP_VERSION_ID >= 50300) {
require_once (dirname(__FILE__) . '/tests/phpunit-shim.php');
}
require_once (dirname(__FILE__) . '/autoload.php');

View File

@ -42,7 +42,9 @@ if (PHP_VERSION_ID < 70000) {
}
/* Explicitly, always load the Compat class: */
if (!class_exists('ParagonIE_Sodium_Compat', false)) {
require_once dirname(__FILE__) . '/src/Compat.php';
}
if (!class_exists('SodiumException', false)) {
require_once dirname(__FILE__) . '/src/SodiumException.php';
@ -69,4 +71,5 @@ if (PHP_VERSION_ID < 70200 || !extension_loaded('sodium')) {
// Older versions of {PHP, ext/sodium} will not define these
require_once(dirname(__FILE__) . '/lib/php72compat.php');
}
require_once(dirname(__FILE__) . '/lib/stream-xchacha20.php');
require_once(dirname(__FILE__) . '/lib/ristretto255.php');

View File

@ -1,8 +0,0 @@
#!/usr/bin/env bash
curl -LSs https://box-project.github.io/box2/installer.php | php
mkdir ~/box
mv box.phar ~/box/box
PATH=$PATH:~/box/ make -C dist/ build-phar
# PATH=$PATH:~/box/ make -C dist/ sign-phar

View File

@ -1,39 +0,0 @@
# This builds sodium-compat.phar. To run this Makefile, `box` and `composer`
# must be installed and in your $PATH. Run it from inside the dist/ directory.
box := $(shell which box)
composer := "composer"
gitcommit := $(shell git rev-parse HEAD)
.PHONY: all
all: build-phar
.PHONY: sign-phar
sign-phar:
gpg -u 7F52D5C61D1255C731362E826B97A1C2826404DA --armor --output sodium-compat.phar.sig --detach-sig sodium-compat.phar
# ensure we run in clean tree. export git tree and run there.
.PHONY: build-phar
build-phar:
@echo "Creating .phar from revision $(shell git rev-parse HEAD)."
rm -rf worktree
install -d worktree
(cd $(CURDIR)/..; git archive HEAD) | tar -x -C worktree
$(MAKE) -f $(CURDIR)/Makefile -C worktree sodium-compat.phar
mv worktree/*.phar .
rm -rf worktree
.PHONY: clean
clean:
rm -vf sodium-compat.phar sodium-compat.phar.sig
# Inside workdir/:
sodium-compat.phar: dist/box.json composer.lock
cp dist/box.json .
php -d phar.readonly=0 $(box) build -c box.json -v
composer.lock:
$(composer) config autoloader-suffix $(gitcommit)
$(composer) install --no-dev

View File

@ -1,32 +0,0 @@
{
"chmod": "0755",
"finder": [
{
"in": "",
"name": "autoload.php"
},
{
"in": "src",
"name": "*.php"
},
{
"in": "lib",
"name": "*.php"
},
{
"in": "vendor/composer",
"name": "*.php"
},
{
"in": "vendor/paragonie",
"name": "*.php"
}
],
"compactors": [
"Herrera\\Box\\Compactor\\Php"
],
"main": "vendor/autoload.php",
"output": "sodium-compat.phar",
"shebang": false,
"stub": true
}

View File

@ -41,3 +41,19 @@ if (!is_callable('sodium_crypto_stream_xchacha20_xor')) {
return ParagonIE_Sodium_Compat::crypto_stream_xchacha20_xor($message, $nonce, $key, true);
}
}
if (!is_callable('sodium_crypto_stream_xchacha20_xor_ic')) {
/**
* @see ParagonIE_Sodium_Compat::crypto_stream_xchacha20_xor_ic()
* @param string $message
* @param string $nonce
* @param int $counter
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
function sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key)
{
return ParagonIE_Sodium_Compat::crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key, true);
}
}

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="true" backupStaticAttributes="false" bootstrap="autoload-phpunit.php" colors="true" convertDeprecationsToExceptions="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnError="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Unit Tests">
<directory suffix="Test.php">./tests/unit</directory>
</testsuite>
<testsuite name="Libsodium Compatibility Tests">
<directory suffix="Test.php">./tests/compat</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -1,56 +0,0 @@
<?xml version="1.0"?>
<psalm
useDocblockTypes="true"
>
<projectFiles>
<directory name="src" />
</projectFiles>
<issueHandlers>
<InvalidFunctionCall errorLevel="suppress" />
<!--
Previous issue type is suppressed because we have to use a string
as a function name for PHP 5.2 compatibility. When testing, be sure
to change this to "info".
-->
<UndefinedFunction errorLevel="info" />
<!--
Inconsistently erroneous.
-->
<DuplicateClass errorLevel="info" />
<!--
Psalm isn't correctly identifying the guard conditions that return
early if a class already exists.
-->
<RedundantConditionGivenDocblockType errorLevel="suppress" />
<TooFewArguments errorLevel="info" />
<DocblockTypeContradiction errorLevel="suppress" />
<RedundantCondition errorLevel="info" />
<!--
Redundancy is good for PHP <7
-->
<TypeDoesNotContainType errorLevel="info" />
<!--
This mostly fails on `PHP_INT_SIZE === 4`
-->
<InternalMethod errorLevel="suppress" />
<!--
We mark methods as internal.
-->
<PossiblyNullArgument errorLevel="suppress" />
<!--
Not interesting
-->
<RedundantCast errorLevel="info" />
<UnnecessaryVarAnnotation errorLevel="suppress" />
<UnusedVariable errorLevel="info" />
<UnusedFunctionCall errorLevel="info" />
</issueHandlers>
</psalm>

View File

@ -1,42 +0,0 @@
<?xml version="1.0"?>
<psalm
useDocblockTypes="true"
>
<projectFiles>
<directory name="src" />
</projectFiles>
<issueHandlers>
<InvalidFunctionCall errorLevel="suppress" />
<!--
Previous issue type is suppressed because we have to use a string
as a function name for PHP 5.2 compatibility. When testing, be sure
to change this to "info".
-->
<UndefinedFunction errorLevel="info" />
<!--
Inconsistently erroneous.
-->
<DuplicateClass errorLevel="info" />
<!--
Psalm isn't correctly identifying the guard conditions that return
early if a class already exists.
-->
<RedundantConditionGivenDocblockType errorLevel="suppress" />
<TooFewArguments errorLevel="info" />
<DocblockTypeContradiction errorLevel="info" />
<RedundantCondition errorLevel="info" />
<!--
Redundancy is good for PHP <7
-->
<TypeDoesNotContainType errorLevel="info" />
<!--
This mostly fails on `PHP_INT_SIZE === 4`
-->
</issueHandlers>
</psalm>

View File

@ -3154,6 +3154,55 @@ class ParagonIE_Sodium_Compat
return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key);
}
/**
* DANGER! UNAUTHENTICATED ENCRYPTION!
*
* Unless you are following expert advice, do not use this feature.
*
* Algorithm: XChaCha20
*
* This DOES NOT provide ciphertext integrity.
*
* @param string $message Plaintext message
* @param string $nonce Number to be used Once; must be 24 bytes
* @param int $counter
* @param string $key Encryption key
* @return string Encrypted text which is vulnerable to chosen-
* ciphertext attacks unless you implement some
* other mitigation to the ciphertext (i.e.
* Encrypt then MAC)
* @param bool $dontFallback
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($counter, 'int', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.');
}
if (is_callable('sodium_crypto_stream_xchacha20_xor_ic') && !$dontFallback) {
return sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key);
}
$ic = ParagonIE_Sodium_Core_Util::store64_le($counter);
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
}
return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
}
/**
* Return a secure random key for use with crypto_stream_xchacha20
*

View File

@ -1,213 +0,0 @@
<?php
/**
* Class ParagonIE_Sodium_Core_Base64
*
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* We have to copy/paste the contents into the variant files because PHP 5.2
* doesn't support late static binding, and we have no better workaround
* available that won't break PHP 7+. Therefore, we're forced to duplicate code.
*/
abstract class ParagonIE_Sodium_Core_Base64_Common
{
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encode($src)
{
return self::doEncode($src, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUnpadded($src)
{
return self::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
* @throws TypeError
*/
protected static function doEncode($src, $pad = true)
{
$dest = '';
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
self::encode6Bits( $b0 >> 2 ) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
self::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
self::encode6Bits($b0 >> 2) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
self::encode6Bits( $b0 >> 2) .
self::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $src
* @param bool $strictPadding
* @return string
* @throws RangeException
* @throws TypeError
* @psalm-suppress RedundantCondition
*/
public static function decode($src, $strictPadding = false)
{
// Remove padding
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($src[$srcLen - 1] === '=') {
$srcLen--;
if ($src[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
if ($src[$srcLen - 1] === '=') {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$src = rtrim($src, '=');
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4));
$c0 = self::decode6Bits($chunk[1]);
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$c3 = self::decode6Bits($chunk[4]);
$dest .= pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3 ) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$c0 = self::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$dest .= pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
} elseif ($i + 1 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$dest .= pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
} elseif ($i < $srcLen && $strictPadding) {
$err |= 1;
}
}
/** @var bool $check */
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
abstract protected static function decode6Bits($src);
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
abstract protected static function encode6Bits($src);
}

View File

@ -3,6 +3,9 @@
if (class_exists('ParagonIE_Sodium_Core_Ed25519', false)) {
return;
}
if (!class_exists('ParagonIE_Sodium_Core_Curve25519', false)) {
require_once dirname(__FILE__) . '/Curve25519.php';
}
/**
* Class ParagonIE_Sodium_Core_Ed25519

Some files were not shown because too many files have changed in this diff Show More