Libs changes for php 8.x

This commit is contained in:
Sumit Panwar
2025-05-23 10:46:00 +05:30
parent 178e8fa28a
commit fa0a498733
1185 changed files with 92175 additions and 58962 deletions

View File

@@ -12,8 +12,10 @@ sudo: required
dist: trusty
php:
- 5.6
- 7.2
- 8.1
- 8.2
- 8.3
- 8.4
before_script:
- sudo cp -f tests/travis-ci-apache-vhost /etc/apache2/sites-available/default

View File

@@ -42,7 +42,7 @@ class Adapter_ProductPriceCalculator
&$specific_price_output = null,
$with_ecotax = true,
$use_group_reduction = true,
Context $context = null,
?Context $context = null,
$use_customer_price = true,
$id_roomtype = false
) {

View File

@@ -1056,7 +1056,7 @@ class CartCore extends ObjectModel
/* If we have a product combination, the minimal quantity is set with the one of this combination */
if (!empty($id_product_attribute)) {
$minimal_quantity = (int)Attribute::getAttributeMinimalQty($id_product_attribute);
$minimal_quantity = (int)ProductAttribute::getAttributeMinimalQty($id_product_attribute);
} else {
$minimal_quantity = (int)$product->minimal_quantity;
}

View File

@@ -124,8 +124,8 @@ class ContextCore
public function getMobileDetect()
{
if ($this->mobile_detect === null) {
require_once(_PS_TOOL_DIR_.'mobile_Detect/Mobile_Detect.php');
$this->mobile_detect = new Mobile_Detect();
require_once(_PS_TOOL_DIR_.'mobile_detect/autoload.php');
$this->mobile_detect = new Detection\MobileDetect();
}
return $this->mobile_detect;
}

View File

@@ -24,7 +24,7 @@
* International Registered Trademark & Property of PrestaShop SA
*/
class AttributeCore extends ObjectModel
class ProductAttributeCore extends ObjectModel
{
/** @var int Group id which attribute belongs */
public $id_attribute_group;
@@ -125,7 +125,7 @@ class AttributeCore extends ObjectModel
public function add($autodate = true, $null_values = false)
{
if ($this->position <= 0) {
$this->position = Attribute::getHigherPosition($this->id_attribute_group) + 1;
$this->position = ProductAttribute::getHigherPosition($this->id_attribute_group) + 1;
}
$return = parent::add($autodate, $null_values);
@@ -198,7 +198,7 @@ class AttributeCore extends ObjectModel
* @param int $qty Quantity needed
* @return bool Quantity is available or not
*/
public static function checkAttributeQty($id_product_attribute, $qty, Shop $shop = null)
public static function checkAttributeQty($id_product_attribute, $qty, ?Shop $shop = null)
{
if (!$shop) {
$shop = Context::getContext()->shop;
@@ -231,7 +231,7 @@ class AttributeCore extends ObjectModel
Tools::displayAsDeprecated();
$id_product = (int)$arr['id_product'];
$qty = Attribute::getAttributeQty($id_product);
$qty = ProductAttribute::getAttributeQty($id_product);
if ($qty !== false) {
$arr['quantity'] = (int)$qty;

View File

@@ -26,6 +26,8 @@
class SmartyCustomCore extends Smarty
{
public $template_class = null;
public function __construct()
{
parent::__construct();

View File

@@ -597,7 +597,12 @@ abstract class ControllerCore
*/
public static function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (error_reporting() === 0) {
/**
* Before PHP 8.0.0, the $errno was always if expression prepended by the @ error-control operator.
* @see https://www.php.net/manual/fr/function.set-error-handler.php
* @see https://www.php.net/manual/en/language.operators.errorcontrol.php
*/
if (!(error_reporting() & $errno)) {
return false;
}

View File

@@ -790,7 +790,11 @@ class FrontControllerCore extends Controller
Context::getContext()->cookie->write();
if ($this->maintenance == true || !(int)Configuration::get('PS_SHOP_ENABLE')) {
$this->maintenance = true;
if (!in_array(Tools::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP')))) {
$allowedIps = array();
if (Configuration::get('PS_MAINTENANCE_IP')) {
$allowedIps = explode(',', Configuration::get('PS_MAINTENANCE_IP'));
}
if (!$allowedIps || !in_array(Tools::getRemoteAddr(), $allowedIps)) {
header('HTTP/1.1 503 temporarily overloaded');
$this->context->smarty->assign($this->initLogoAndFavicon());
@@ -1032,48 +1036,63 @@ class FrontControllerCore extends Controller
if (!in_array($_SERVER['SERVER_NAME'], array('localhost', '127.0.0.1'))) {
/* Check if Maxmind Database exists */
if (@filemtime(_PS_GEOIP_DIR_._PS_GEOIP_CITY_FILE_)) {
if (!isset($this->context->cookie->iso_code_country) || (isset($this->context->cookie->iso_code_country) && !in_array(strtoupper($this->context->cookie->iso_code_country), explode(';', Configuration::get('PS_ALLOWED_COUNTRIES'))))) {
include_once(_PS_GEOIP_DIR_.'geoipcity.inc');
if (!isset($this->context->cookie->iso_code_country) || (isset($this->context->cookie->iso_code_country)
&& !in_array(strtoupper($this->context->cookie->iso_code_country), explode(';', Configuration::get('PS_ALLOWED_COUNTRIES'))))
) {
include_once(_PS_GEOIP_DIR_.'autoload.php');
$gi = geoip_open(realpath(_PS_GEOIP_DIR_._PS_GEOIP_CITY_FILE_), GEOIP_STANDARD);
$record = geoip_record_by_addr($gi, Tools::getRemoteAddr());
$reader = new GeoIp2\Database\Reader(_PS_GEOIP_DIR_ . _PS_GEOIP_CITY_FILE_);
try {
$record = $reader->city(Tools::getRemoteAddr());
} catch (GeoIp2\Exception\AddressNotFoundException $e) {
$record = null;
}
if (is_object($record)) {
if (!in_array(strtoupper($record->country_code), explode(';', Configuration::get('PS_ALLOWED_COUNTRIES'))) && !FrontController::isInWhitelistForGeolocation()) {
if (!in_array(strtoupper($record->country->isoCode), explode(';', Configuration::get('PS_ALLOWED_COUNTRIES')))
&& !FrontController::isInWhitelistForGeolocation()
) {
if (Configuration::get('PS_GEOLOCATION_BEHAVIOR') == _PS_GEOLOCATION_NO_CATALOG_) {
$this->restrictedCountry = true;
} elseif (Configuration::get('PS_GEOLOCATION_BEHAVIOR') == _PS_GEOLOCATION_NO_ORDER_) {
$this->context->smarty->assign(array(
'restricted_country_mode' => true,
'geolocation_country' => $record->country_name
'geolocation_country' => $record->country->name
));
}
} else {
$has_been_set = !isset($this->context->cookie->iso_code_country);
$this->context->cookie->iso_code_country = strtoupper($record->country_code);
$this->context->cookie->iso_code_country = strtoupper($record->country->isoCode);
}
}
}
if (isset($this->context->cookie->iso_code_country) && $this->context->cookie->iso_code_country && !Validate::isLanguageIsoCode($this->context->cookie->iso_code_country)) {
if (isset($this->context->cookie->iso_code_country)
&& $this->context->cookie->iso_code_country
&& !Validate::isLanguageIsoCode($this->context->cookie->iso_code_country)
) {
$this->context->cookie->iso_code_country = Country::getIsoById(Configuration::get('PS_COUNTRY_DEFAULT'));
}
if (isset($this->context->cookie->iso_code_country) && ($id_country = (int)Country::getByIso(strtoupper($this->context->cookie->iso_code_country)))) {
if (isset($this->context->cookie->iso_code_country)
&& ($id_country = (int)Country::getByIso(strtoupper($this->context->cookie->iso_code_country)))
) {
/* Update defaultCountry */
if ($default_country->iso_code != $this->context->cookie->iso_code_country) {
$default_country = new Country($id_country);
}
if (isset($has_been_set) && $has_been_set) {
$this->context->cookie->id_currency = (int)($default_country->id_currency ? (int)$default_country->id_currency : (int)Configuration::get('PS_CURRENCY_DEFAULT'));
}
return $default_country;
} elseif (Configuration::get('PS_GEOLOCATION_NA_BEHAVIOR') == _PS_GEOLOCATION_NO_CATALOG_ && !FrontController::isInWhitelistForGeolocation()) {
$this->restrictedCountry = true;
} elseif (Configuration::get('PS_GEOLOCATION_NA_BEHAVIOR') == _PS_GEOLOCATION_NO_ORDER_ && !FrontController::isInWhitelistForGeolocation()) {
$this->context->smarty->assign(array(
'restricted_country_mode' => true,
'geolocation_country' => isset($record->country_name) && $record->country_name ? $record->country_name : 'Undefined'
'geolocation_country' => isset($record->country->name) && $record->country->name ? $record->country->name : 'Undefined'
));
}
}

View File

@@ -62,7 +62,7 @@ if (defined('_PS_ADMIN_DIR_')) {
} else {
require_once(dirname(__FILE__).'/smartyfront.config.inc.php');
}
require_once SMARTY_PLUGINS_DIR.'modifier.truncate.php';
require_once SMARTY_PLUGINS_DIR . 'modifier.truncate.php';
// This escape modifier is required for invoice PDF generation
function smartyEscape($string, $esc_type = 'html', $char_set = null, $double_encode = true)

View File

@@ -239,7 +239,7 @@ class AdminAttributeGeneratorControllerCore extends AdminController
$this->initPageHeaderToolbar();
$this->initGroupTable();
$attributes = Attribute::getAttributes(Context::getContext()->language->id, true);
$attributes = ProductAttribute::getAttributes(Context::getContext()->language->id, true);
$attribute_js = array();
foreach ($attributes as $k => $attribute) {

View File

@@ -102,7 +102,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
{
if (($id = Tools::getValue('id_attribute_group'))) {
$this->table = 'attribute';
$this->className = 'Attribute';
$this->className = 'ProductAttribute';
$this->identifier = 'id_attribute';
$this->position_identifier = 'id_attribute';
$this->position_group_identifier = 'id_attribute_group';
@@ -345,13 +345,13 @@ class AdminAttributesGroupsControllerCore extends AdminController
// Override var of Controller
$this->table = 'attribute';
$this->className = 'Attribute';
$this->className = 'ProductAttribute';
$this->identifier = 'id_attribute';
$this->lang = true;
$this->tpl_folder = 'attributes/';
// Create object Attribute
if (!$obj = new Attribute((int)Tools::getValue($this->identifier))) {
if (!$obj = new ProductAttribute((int)Tools::getValue($this->identifier))) {
return;
}
@@ -480,7 +480,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
}
$this->content .= $this->renderForm();
} elseif ($this->display == 'editAttributes') {
if (!$this->object = new Attribute((int)Tools::getValue('id_attribute'))) {
if (!$this->object = new ProductAttribute((int)Tools::getValue('id_attribute'))) {
return;
}
@@ -614,7 +614,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
if (Validate::isLoadedObject($obj = new AttributeGroup((int)$id))) {
$bread_extended[] = '<a href="'.Context::getContext()->link->getAdminLink('AdminAttributesGroups').'&id_attribute_group='.$id.'&viewattribute_group">'.$obj->name[$this->context->employee->id_lang].'</a>';
}
if (Validate::isLoadedObject($obj = new Attribute((int)$this->id_attribute))) {
if (Validate::isLoadedObject($obj = new ProductAttribute((int)$this->id_attribute))) {
$bread_extended[] = sprintf($this->l('Edit: %s'), $obj->name[$this->context->employee->id_lang]);
}
} else {
@@ -659,7 +659,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
{
if (Tools::isSubmit('updateattribute') || Tools::isSubmit('deleteattribute') || Tools::isSubmit('submitAddattribute') || Tools::isSubmit('submitBulkdeleteattribute')) {
$this->table = 'attribute';
$this->className = 'Attribute';
$this->className = 'ProductAttribute';
$this->identifier = 'id_attribute';
if ($this->display == 'edit') {
@@ -671,7 +671,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
public function processPosition()
{
if (Tools::getIsset('viewattribute_group')) {
$object = new Attribute((int)Tools::getValue('id_attribute'));
$object = new ProductAttribute((int)Tools::getValue('id_attribute'));
self::$currentIndex = self::$currentIndex.'&viewattribute_group';
} else {
$object = new AttributeGroup((int)Tools::getValue('id_attribute_group'));
@@ -717,7 +717,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
if (!Tools::getValue($this->identifier) && Tools::getValue('id_attribute') && !Tools::getValue('attributeOrderby')) {
// Override var of Controller
$this->table = 'attribute';
$this->className = 'Attribute';
$this->className = 'ProductAttribute';
$this->identifier = 'id_attribute';
}
@@ -725,7 +725,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
if (Tools::getValue('updateattribute') || Tools::isSubmit('deleteattribute') || Tools::isSubmit('submitAddattribute')) {
if ($this->tabAccess['edit'] !== '1') {
$this->errors[] = Tools::displayError('You do not have permission to edit this.');
} elseif (!$object = new Attribute((int)Tools::getValue($this->identifier))) {
} elseif (!$object = new ProductAttribute((int)Tools::getValue($this->identifier))) {
$this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
}
@@ -864,7 +864,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
{
// If we are deleting attributes instead of attribute_groups
if (Tools::getIsset('attributeBox')) {
$this->className = 'Attribute';
$this->className = 'ProductAttribute';
$this->table = 'attribute';
$this->boxes = Tools::getValue($this->table.'Box');
}
@@ -923,7 +923,7 @@ class AdminAttributesGroupsControllerCore extends AdminController
$pos = explode('_', $value);
if ((isset($pos[1]) && isset($pos[2])) && (int)$pos[2] === $id_attribute) {
if ($attribute = new Attribute((int)$pos[2])) {
if ($attribute = new ProductAttribute((int)$pos[2])) {
if (isset($position) && $attribute->updatePosition($way, $position)) {
echo 'ok position '.(int)$position.' for attribute '.(int)$pos[2].'\r\n';
} else {

View File

@@ -533,7 +533,7 @@ class AdminCartsControllerCore extends AdminController
// Don't try to use a product if not instanciated before due to errors
if (isset($product) && $product->id) {
if (($id_product_attribute = Tools::getValue('id_product_attribute')) != 0) {
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !Attribute::checkAttributeQty((int)$id_product_attribute, (int)$qty)) {
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !ProductAttribute::checkAttributeQty((int)$id_product_attribute, (int)$qty)) {
$errors[] = Tools::displayError('There is not enough product in stock.');
}
} elseif (!$product->checkQty((int)$qty)) {
@@ -558,7 +558,7 @@ class AdminCartsControllerCore extends AdminController
if (!($qty_upd = $this->context->cart->updateQty($qty, $id_product, (int)$id_product_attribute, (int)$id_customization, $operator))) {
$errors[] = Tools::displayError('You already have the maximum quantity available for this product.');
} elseif ($qty_upd < 0) {
$minimal_qty = $id_product_attribute ? Attribute::getAttributeMinimalQty((int)$id_product_attribute) : $product->minimal_quantity;
$minimal_qty = $id_product_attribute ? ProductAttribute::getAttributeMinimalQty((int)$id_product_attribute) : $product->minimal_quantity;
$errors[] = sprintf(Tools::displayError('You must add a minimum quantity of %d', false), $minimal_qty);
}
}

View File

@@ -139,9 +139,9 @@ class AdminGeolocationControllerCore extends AdminController
{
$this->display = 'options';
if (!$this->isGeoLiteCityAvailable()) {
$this->displayWarning($this->l('In order to use Geolocation, please download the file').'
<a href="https://github.com/mbcc2006/GeoLiteCity-data/blob/master/GeoLiteCity.dat" target="_blank">GeoLiteCity.dat</a> '.
$this->l('from GitHub and copy into the /tools/geoip/ directory.'));
$this->displayWarning($this->l('Since December 30, 2019, you need to register for a ').
'<a href="https://www.maxmind.com/en/geolite2/signup" target="_blank">'.' '.$this->l('MaxMind account').'</a>'.' '.
$this->l('to get a license key to be able to download the geolocation data. Once downloaded, extract the GeoLite2 City(.mmdb) data using Winrar or Gzip into the /tools/geoip/ directory'));
Configuration::updateValue('PS_GEOLOCATION_ENABLED', 0);
}
@@ -150,7 +150,7 @@ class AdminGeolocationControllerCore extends AdminController
protected function isGeoLiteCityAvailable()
{
if (@filemtime(_PS_GEOIP_DIR_._PS_GEOIP_CITY_FILE_)) {
if (file_exists(_PS_GEOIP_DIR_._PS_GEOIP_CITY_FILE_) && @filemtime(_PS_GEOIP_DIR_._PS_GEOIP_CITY_FILE_)) {
return true;
}

View File

@@ -4274,7 +4274,7 @@ class AdminOrdersControllerCore extends AdminController
if ($update_quantity < 0) {
// If product has attribute, minimal quantity is set with minimal quantity of attribute
$minimal_quantity = ($product_informations['product_attribute_id']) ? Attribute::getAttributeMinimalQty($product_informations['product_attribute_id']) : $product->minimal_quantity;
$minimal_quantity = ($product_informations['product_attribute_id']) ? ProductAttribute::getAttributeMinimalQty($product_informations['product_attribute_id']) : $product->minimal_quantity;
die(Tools::jsonEncode(array('error' => sprintf(Tools::displayError('You must add %d minimum quantity', false), $minimal_quantity))));
} elseif (!$update_quantity) {
die(Tools::jsonEncode(array('error' => Tools::displayError('You already have the maximum quantity available for this product.', false))));
@@ -4845,7 +4845,7 @@ class AdminOrdersControllerCore extends AdminController
if ($update_quantity < 0) {
// If product has attribute, minimal quantity is set with minimal quantity of attribute
$minimal_quantity = ($product_informations['product_attribute_id']) ? Attribute::getAttributeMinimalQty($product_informations['product_attribute_id']) : $product->minimal_quantity;
$minimal_quantity = ($product_informations['product_attribute_id']) ? ProductAttribute::getAttributeMinimalQty($product_informations['product_attribute_id']) : $product->minimal_quantity;
die(json_encode(array('error' => sprintf(Tools::displayError('You must add %d minimum quantity', false), $minimal_quantity))));
} elseif (!$update_quantity) {
die(json_encode(array('error' => Tools::displayError('You already have the maximum quantity available for this product.', false))));

View File

@@ -317,7 +317,7 @@ class AdminSpecificPriceRuleControllerCore extends AdminController
);
$attribute_groups = array();
$attributes = Attribute::getAttributes((int)$this->context->language->id);
$attributes = ProductAttribute::getAttributes((int)$this->context->language->id);
foreach ($attributes as $attribute) {
if (!isset($attribute_groups[$attribute['id_attribute_group']])) {
$attribute_groups[$attribute['id_attribute_group']] = array(

View File

@@ -130,7 +130,7 @@ class CartControllerCore extends FrontController
$product = new Product((int)$this->id_product);
if (count($customization_product)) {
if ($this->id_product_attribute > 0) {
$minimal_quantity = (int)Attribute::getAttributeMinimalQty($this->id_product_attribute);
$minimal_quantity = (int)ProductAttribute::getAttributeMinimalQty($this->id_product_attribute);
} else {
$minimal_quantity = (int)$product->minimal_quantity;
}
@@ -561,7 +561,7 @@ class CartControllerCore extends FrontController
// Check product quantity availability
if ($this->id_product_attribute) {
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !Attribute::checkAttributeQty($this->id_product_attribute, $qty_to_check)) {
if (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !ProductAttribute::checkAttributeQty($this->id_product_attribute, $qty_to_check)) {
$this->errors[] = Tools::displayError('There isn\'t enough product in stock.', !Tools::getValue('ajax'));
}
} elseif ($product->hasAttributes()) {
@@ -570,7 +570,7 @@ class CartControllerCore extends FrontController
// @todo do something better than a redirect admin !!
if (!$this->id_product_attribute) {
Tools::redirectAdmin($this->context->link->getProductLink($product));
} elseif (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !Attribute::checkAttributeQty($this->id_product_attribute, $qty_to_check)) {
} elseif (!Product::isAvailableWhenOutOfStock($product->out_of_stock) && !ProductAttribute::checkAttributeQty($this->id_product_attribute, $qty_to_check)) {
$this->errors[] = Tools::displayError('There isn\'t enough product in stock.', !Tools::getValue('ajax'));
}
} elseif (!$product->checkQty($qty_to_check)) {
@@ -680,7 +680,7 @@ class CartControllerCore extends FrontController
if ($update_quantity < 0) {
// If product has attribute, minimal quantity is set with minimal quantity of attribute
$minimal_quantity = ($this->id_product_attribute) ? Attribute::getAttributeMinimalQty($this->id_product_attribute) : $product->minimal_quantity;
$minimal_quantity = ($this->id_product_attribute) ? ProductAttribute::getAttributeMinimalQty($this->id_product_attribute) : $product->minimal_quantity;
$this->errors[] = sprintf(Tools::displayError('You must add %d minimum quantity', !Tools::getValue('ajax')), $minimal_quantity);
} elseif (!$update_quantity) {
$this->errors[] = Tools::displayError('You already have the maximum quantity available for this product.', !Tools::getValue('ajax'));

View File

@@ -1,7 +1,7 @@
The MIT License (MIT)
Copyright (c) 2016 Taylor Hornby <https://defuse.ca> and Paragon Initiative
Enterprises <https://paragonie.com>.
Copyright (c) 2016 Taylor Hornby <https://defuse.ca>
Copyright (c) 2016 Paragon Initiative Enterprises <https://paragonie.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -1,12 +1,26 @@
php-encryption
===============
[![Build Status](https://travis-ci.org/defuse/php-encryption.svg?branch=master)](https://travis-ci.org/defuse/php-encryption)
![Build Status](https://app.travis-ci.com/defuse/php-encryption.svg?branch=master)
[![codecov](https://codecov.io/gh/defuse/php-encryption/branch/master/graph/badge.svg)](https://codecov.io/gh/defuse/php-encryption)
[![Latest Stable Version](https://poser.pugx.org/defuse/php-encryption/v/stable)](https://packagist.org/packages/defuse/php-encryption)
[![License](https://poser.pugx.org/defuse/php-encryption/license)](https://packagist.org/packages/defuse/php-encryption)
[![Downloads](https://img.shields.io/packagist/dt/defuse/php-encryption.svg)](https://packagist.org/packages/defuse/php-encryption)
```terminal
composer require defuse/php-encryption
```
This is a library for encrypting data with a key or password in PHP. **It
requires PHP 5.4 or newer.** The current version is v2.0.0, which is expected to
requires PHP 5.6 or newer and OpenSSL 1.0.1 or newer.** We recommend using a
version of PHP that [still has security
support](https://www.php.net/supported-versions.php), which at the time of
writing means PHP 8.0 or later. Using this library with an unsupported
version of PHP could lead to security vulnerabilities.
The current version of `php-encryption` is v2.4.0. This library is expected to
remain stable and supported by its authors with security and bugfixes until at
least January 1st, 2019.
least January 1st, 2024.
The library is a joint effort between [Taylor Hornby](https://defuse.ca/) and
[Scott Arciszewski](https://paragonie.com/blog/author/scott-arcizewski) as well
@@ -19,14 +33,15 @@ ecosystem. Secondly, this library is "difficult to misuse." Like
[libsodium](https://github.com/jedisct1/libsodium), its API is designed to be
easy to use in a secure way and hard to use in an insecure way.
Dependencies
------------
This library requres no special dependencies except for PHP 5.4 or newer with
the OpenSSL extensions enabled (this is the default). It uses
[random\_compat](https://github.com/paragonie/random_compat), which is bundled
in with this library so that your users will not need to follow any special
installation steps.
This library requires no special dependencies except for PHP 5.6 or newer with
the OpenSSL extensions (version 1.0.1 or later) enabled (this is the default).
It uses [random\_compat](https://github.com/paragonie/random_compat), which is
bundled in with this library so that your users will not need to follow any
special installation steps.
Getting Started
----------------
@@ -53,6 +68,14 @@ documentation.
If you're interested in contributing to this library, see the [Internal
Developer Documentation](docs/InternalDeveloperDocs.md).
Other Language Support
----------------------
This library is intended for server-side PHP software that needs to encrypt data at rest.
If you are building software that needs to encrypt client-side, or building a system that
requires cross-platform encryption/decryption support, we strongly recommend using
[libsodium](https://download.libsodium.org/doc/bindings_for_other_languages) instead.
Examples
---------
@@ -60,6 +83,7 @@ If the documentation is not enough for you to understand how to use this
library, then you can look at an example project that uses this library:
- [encutil](https://github.com/defuse/encutil)
- [fileencrypt](https://github.com/tsusanka/fileencrypt)
Security Audit Status
---------------------
@@ -75,13 +99,23 @@ a formal audit, please [contact Taylor Hornby](https://defuse.ca/contact.htm).
Public Keys
------------
The GnuPG public key used to sign releases is available in
The GnuPG public key used to sign current and older releases is available in
[dist/signingkey.asc](https://github.com/defuse/php-encryption/raw/master/dist/signingkey.asc). Its fingerprint is:
```
2FA6 1D8D 99B9 2658 6BAC 3D53 385E E055 A129 1538
```
You can verify it against the Taylor Hornby's [contact
You can verify it against Taylor Hornby's [contact
page](https://defuse.ca/contact.htm) and
[twitter](https://twitter.com/DefuseSec/status/723741424253059074).
Due to the old key expiring, new releases will be signed with a new public key
available in [dist/signingkey-new.asc](https://github.com/defuse/php-encryption/raw/master/dist/signingkey-new.asc). Its fingerprint is:
```
6DD6 E677 0281 5846 FC85 25A3 DD2E 507F 7BDB 1669
```
A signature of this new key by the old key is available in
[dist/signingkey-new.asc.sig](https://github.com/defuse/php-encryption/raw/master/dist/signingkey-new.asc.sig).

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env php
<?php
use Defuse\Crypto\Key;
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
if (file_exists($file)) {
require $file;
break;
}
}
$key = Key::createNewRandomKey();
echo $key->saveToAsciiSafeString(), "\n";

View File

@@ -2,14 +2,15 @@
# must be installed and in your $PATH. Run it from inside the dist/ directory.
box := $(shell which box)
composer := "composer"
composer := $(shell which composer)
gitcommit := $(shell git rev-parse HEAD)
.PHONY: all
all: build-phar
.PHONY: sign-phar
sign-phar:
gpg -u 7B4B2D98 --armor --output defuse-crypto.phar.sig --detach-sig defuse-crypto.phar
gpg -u DD2E507F7BDB1669 --armor --output defuse-crypto.phar.sig --detach-sig defuse-crypto.phar
# ensure we run in clean tree. export git tree and run there.
.PHONY: build-phar
@@ -30,8 +31,9 @@ clean:
defuse-crypto.phar: dist/box.json composer.lock
cp dist/box.json .
php -d phar.readonly=0 $(box) build -c box.json -v
php $(box) compile -c box.json -v
composer.lock:
$(composer) config autoloader-suffix $(gitcommit)
$(composer) install --no-dev

View File

@@ -15,9 +15,6 @@
"exclude": "other"
}
],
"compactors": [
"Herrera\\Box\\Compactor\\Php"
],
"main": "vendor/autoload.php",
"output": "defuse-crypto.phar",
"shebang": false,

View File

@@ -1,29 +0,0 @@
<?php
/**
* 2010-2021 Webkul.
*
* NOTICE OF LICENSE
*
* All right is reserved,
* Please go through this link for complete license : https://store.webkul.com/license.html
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to newer
* versions in the future. If you wish to customize this module for your
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
*
* @author Webkul IN <support@webkul.com>
* @copyright 2010-2021 Webkul IN
* @license https://store.webkul.com/license.html
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,4 @@
<?php
require 'defuse-crypto.phar';
require realpath(dirname(__FILE__) . '/../vendor/yoast/phpunit-polyfills/phpunitpolyfills-autoload.php');
?>

View File

@@ -0,0 +1,53 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBF5V4TABEAC4G2BkHDaqbip3gj1oOqKh3V6LQa9QAd/f/hyhmR5hXpciPxf3
NNHAxzoGAuB51f1YPJTNO59mGKHDuCFfr0pI94HDGoW4WgxqnUyqHBj2+/JhQPqO
lgDT0QDcfxmUd0wfZl/Ur+8SsaBYvfFWNmPaXHp9m4MMRtw9uZNIW6LlZ24JqmGy
/YUELUSH7P+uJ4HQEdixaqQ0VgIomRDI+5IwdJMtq4TSNazQm3nNmH9Em37cdi6J
NDfFRy2QxJDmuqlg8mkpS5TvrrNy/UJwIeXO9PuGaBODr8GAKWvhkpfGlxN+hWMY
01bOFnuEnOcuXw8BjPAKHqwOuGvinNmQ7lX1Rj3ssd31sTUimop0oNjOTZztpJBR
m6wO2/YGMjt+eL02NgBBDIsV837PeWuJmymTJDGQuBjZ3JWUfyT3AnkA8OU5vKjs
pM8AjIiuU7C8zQhUSHDnukTKWpBmMdOXeWNb5Ye6n60wJWzWFGlm+cYalPs+q3H8
bxHxHEdFT0rUpxB05bc9zsZ3gGkc2NTNW/00a6gvTyX1UsBAeNgvVSHBHQGfow6o
ZKG+LnVxd+cl97ay6kP29eLypXffbXQ3hMXe9tUNBjAeiok9tssU70Epr9wTh/Fm
/iEbGc8VhS4TSk3c+3eS16rvlQ51FmAlmG6kAnN/ah+BiM4syPrWcJHIDQARAQAB
tG1kZWZ1c2UvcGhwLWVuY3J5cHRpb24gbWFpbnRhaW5lcnMgKGRlZnVzZS9waHAt
ZW5jcnlwdGlvbiByZWxlYXNlIHNpZ25pbmcga2V5KSA8cGhwLWVuY3J5cHRpb25A
ZGVmdXNlLmludmFsaWQ+iQJUBBMBCAA+FiEEbdbmdwKBWEb8hSWj3S5Qf3vbFmkF
Al5V4TACGwMFCQlmAYAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ3S5Qf3vb
FmnQ4Q//bHAwDI7CcTlDDktdRCP0YCRtb5zMa6vSqnZLi5aTqzmL1yQCAp1/JTwf
nlHn5xt60eKwfjIKj7Kj0n8WDFYnlOu30H5fNtFHis0xeS7GkH60tIE/pQUZILlB
Wcnx/ZPnlxccjtfSbnpelSPdvIoHVRNhh1ZYG/49kuuv8TMbMIi2FBAzGEatPoLN
f4wntoOKGvl8R2rPc2geapXTz++X+HJkddHCISR61obDRl9P0t9x+0M7gGSVMGfC
GC4wh3JB6/ha8l+DI+l88/64IKRG+M33bBwLGQ0RIhotHIy442gLJTm6TeoH8iUz
xCBwPYW+Ta1wZi17PIjHdTkNGBeEj/Hr5tTVV3oxrQVgHCymzasnz9IwcCCMwpKK
ZOMFl0+PT3TSBKLnUByvOB64YOjxU7t+sRf53Biz3yKzto5VdHGW64OG2vGFy/Xz
vI5RqU34wjtEHxWfz8y2GBnhD2TzEFCIIWPAX3TDG64NBSEBjhUraOmoVoaYJlP6
rqxIQo4yhC+f5rnr2ZA48Hnrg0jEdVvN07FegoOnQQPpYBBkOrkTDWChn8oiXMfg
9bjv19zDOBVXl9EU+P8AhwTHz/pBKmhb97N9nYp/pbmejA+I0Kw1vZo7gaMxL938
oQkdtWT70ZzcpcZfeKVXoZa/ddAmuxzNknZA8ZnjQ9Qhv7aNX2O5Ag0EXlXhMAEQ
AM8od4/85i7ZPmM6C1M4n4XcXeKsuZKHLvLLcRHFGkjVdXRSaxpbk2yDJiLnB9hX
GSJG2gUCT+yrimjQ71bJ4q9K2+mkVHVjdtCrCtoOYEIpMLzsRtqyAWotcVmdv8Zv
4IIjxfdxpTkj9gZmUfDIe6tbN2iBCAo1HArXq1qSdof3ui8SqdWeinkd7lZMesFm
dGQaAcHbmEakO5mRzljme8IBs3UY9j/zxEG1JbsHx9ua7CVwJ7lxi2SgSW6nF9k5
CX5zbrDqlqSJNtDs+KbjCbI2eK+qe4qZWHPiw4bNBn6EWf97/4Os8w7Vrrpyd2eO
1JENwjlG6WG9mbJdIWWwakZ0CeH5LnJo6dV47KZbbbB6ncavaL+VpfbTCgdOGsCc
GcYUVl90/v5pPm2owx4Dg9hSfcp8fesQuq4b79NAcjF7meu5wgNdvFlfuXony+UC
W2wNi0mi9lzLD0n0j0GDzWyd3r7yXmPTL4LhrQu/pIcWIljKI3GUAQZqIYbGAO3G
7hEFT8rDWg2vKRtMag4iy5FvZFqR+7TwWJAcWnHJBZ95F9NzeYIFhp9a3hxbKXqD
xEnyGgzAszUycq29BApT4/4rDZQuXuOBd4lJp8tSzctLjvo7D3la+MWD6AlDkYT4
bGKN9NfRCzYr2Zq3jOByAV3d5hGgyzdJlZSqXAGtbHHdABEBAAGJAjwEGAEIACYW
IQRt1uZ3AoFYRvyFJaPdLlB/e9sWaQUCXlXhMAIbDAUJCWYBgAAKCRDdLlB/e9sW
aSGfD/wPeq6lGu8ocHIkO74VPioJRKRXDVLsY02xKP64p0RHUGFTOqqB3A3UV0ue
tkizoUdfF5xkgJ18gbxXo8lotBq+Ita5hoYAfqJnTnucAPGovREJ+X1HfdK4pJqW
KNJElBz+fC4chqksiUAuH7IMImmy0/lA+LqZagzkQJU10MvmiFZ6kn+X5Mb4izRl
vAHo16eI4psApdT8Bs7mwAjgCHxS9Re46uOElB4Bx3iFPd/PEwHWnfr8x9TJZYKW
fsShG31+vfBRCfGtfKGxiAkp3EEM11lzbbfMcC3lai5iJQ/FmHgoIDeIG2Ebuk4w
/PYakSrpvkEYoMP31pVHDhzopVeURS2lpvQJ4CvTP5CVQtKrbuygow6GF8N/drCE
hdEx22pzW02ADS9fgzrlDztIOlOvC9a+epISIaEjfrc9dWhrw6chZEoWIil2MVQR
Sj0jZ8w/H7P88oHTOcFVel73ZEPg9eRUkqMnIn3DWUuqLI2SX/AtVnhdYHWTiOkq
knsGofWxUSu3RZR2ZElK9hjNKdVbGDzHGAYeJihieTKIOXpCf6Ix5B32tmFpfmBV
Q9YP3JLsRTxIMbXsJImand/r6fSjdmTpk2PovYPtE1HTJKaVHeagQdsrWw5LaJv0
ZWuwJm0y0WJXcAEjwOHhBs0nvq2CXuZi2ZTPtY+DbsSFWhaN7g==
=Ysgx
-----END PGP PUBLIC KEY BLOCK-----

Binary file not shown.

View File

@@ -0,0 +1,64 @@
Cryptography Details
=====================
Here is a high-level description of how this library works. Any discrepancy
between this documentation and the actual implementation will be considered
a security bug.
Let's start with the following definitions:
- HKDF-SHA256(*k*, *n*, *info*, *s*) is the key derivation function specified in
RFC 5869 (using the SHA256 hash function). The parameters are:
- *k*: The initial keying material.
- *n*: The number of output bytes.
- *info*: The info string.
- *s*: The salt.
- AES-256-CTR(*m*, *k*, *iv*) is AES-256 encryption in CTR mode. The parameters
are:
- *m*: An arbitrary-length (possibly zero-length) message.
- *k*: A 32-byte key.
- *iv*: A 16-byte initialization vector (nonce).
- PBKDF2-SHA256(*p*, *s*, *i*, *n*) is the password-based key derivation
function defined in RFC 2898 (using the SHA256 hash function). The parameters
are:
- *p*: The password string.
- *s*: The salt string.
- *i*: The iteration count.
- *n*: The output length in bytes.
- VERSION is the string `"\xDE\xF5\x02\x00"`.
- AUTHINFO is the string `"DefusePHP|V2|KeyForAuthentication"`.
- ENCRINFO is the string `"DefusePHP|V2|KeyForEncryption"`.
To encrypt a message *m* using a 32-byte key *k*, the following steps are taken:
1. Generate a random 32-byte string *salt*.
2. Derive the 32-byte authentication key *akey* = HKDF-SHA256(*k*, 32, AUTHINFO, *salt*).
3. Derive the 32-byte encryption key *ekey* = HKDF-SHA256(*k*, 32, ENCRINFO, *salt*).
4. Generate a random 16-byte initialization vector *iv*.
5. Compute *c* = AES-256-CTR(*m*, *ekey*, *iv*).
6. Combine *ctxt* = VERSION || *salt* || *iv* || *c*.
7. Compute *h* = HMAC-SHA256(*ctxt*, *akey*).
8. Output *ctxt* || *h*.
Decryption is roughly the reverse process (see the code for details, since the
security of the decryption routine is highly implementation-dependent).
For encryption using a password *p*, steps 1-3 above are replaced by:
1. Generate a random 32-byte string *salt*.
2. Compute *k* = PBKDF2-SHA256(SHA256(*p*), *salt*, 100000, 32).
3. Derive the 32-byte authentication key *akey* = HKDF-SHA256(*k*, 32, AUTHINFO, *salt*)
4. Derive the 32-byte encryption key *ekey* = HKDF-SHA256(*k*, 32, ENCRINFO, *salt*)
The remainder of the process is the same. Notice the reuse of the same *salt*
for PBKDF2-SHA256 and HKDF-SHA256. The prehashing of the password in step 2 is
done to prevent a [DoS attack using long
passwords](https://github.com/defuse/php-encryption/issues/230).
For `KeyProtectedByPassword`, the serialized key is encrypted according to the
password encryption defined above. However, the actual password used for
encryption is the SHA256 hash of the password the user provided. This is done in
order to provide domain separation between the message encryption in the user's
application and the internal key encryption done by this library. It fixes
a [key replacement chosen-protocol
attack](https://github.com/defuse/php-encryption/issues/240).

View File

@@ -0,0 +1,51 @@
Frequently Asked Questions
===========================
How do I use this library to encrypt passwords?
------------------------------------------------
Passwords should not be encrypted, they should be hashed with a *slow* password
hashing function that's designed to slow down password guessing attacks. See
[How to Safely Store Your Users' Passwords in
2016](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016).
How do I give it the same key every time instead of a new random key?
----------------------------------------------------------------------
A `Key` object can be saved to a string by calling its `saveToAsciiSafeString()`
method. You will have to save that string somewhere safe, and then load it back
into a `Key` object using `Key`'s `loadFromAsciiSafeString` static method.
Where you store the string depends on your application. For example if you are
using `KeyProtectedByPassword` to encrypt files with a user's login password,
then you should not store the `Key` at all. If you are protecting sensitive data
on a server that may be compromised, then you should store it in a hardware
security module. When in doubt, consult a security expert.
Why is an EnvironmentIsBrokenException getting thrown?
-------------------------------------------------------
Either you've encountered a bug in this library, or your system doesn't support
the use of this library. For example, if your system does not have a secure
random number generator, this library will refuse to run, by throwing that
exception, instead of falling back to an insecure random number generator.
Why am I getting a BadFormatException when loading a Key from a string?
------------------------------------------------------------------------
If you're getting this exception, then the string you're giving to
`loadFromAsciiSafeString()` is *not* the same as the string you got from
`saveToAsciiSafeString()`. Perhaps your database column isn't wide enough and
it's truncating the string as you insert it?
Does encrypting hide the length of the plaintext?
--------------------------------------------------
Encryption does not, and is not intended to, hide the length of the data being
encrypted. For example, it is not safe to encrypt a field in which only a small
number of different-length values are possible (e.g. "male" or "female") since
it would be possible to tell what the plaintext is by looking at the length of
the ciphertext. In order to do this safely, it is your responsibility to, before
encrypting, pad the data out to the length of the longest string that will ever
be encrypted. This way, all plaintexts are the same length, and no information
about the plaintext can be gleaned from the length of the ciphertext.

View File

@@ -0,0 +1,53 @@
Getting The Code
=================
There are two ways to use this library in your applications. You can either:
1. Use [Composer](https://getcomposer.org/), or
2. `require_once` a single `.phar` file in your application.
If you are not using either option (for example, because you're using Git submodules), you may need to write your own autoloader ([example](https://gist.github.com/paragonie-scott/949daee819bb7f19c50e5e103170b400)).
Option 1: Using Composer
-------------------------
Run this inside the directory of your composer-enabled project:
```sh
composer require defuse/php-encryption
```
Unfortunately, composer doesn't provide a way for you to verify that the code
you're getting was signed by this library's authors. If you want a more secure
option, use the `.phar` method described below.
Option 2: Including a PHAR
----------------------------
The `.phar` option lets you include this library into your project simply by
calling `require_once` on a single file. Download `defuse-crypto.phar` and
`defuse-crypto.phar.sig` from this project's
[releases](https://github.com/defuse/php-encryption/releases) page.
You should verify the integrity of the `.phar`. The `defuse-crypto.phar.sig`
contains the signature of `defuse-crypto.phar`. It is signed with Taylor
Hornby's PGP key. You can find Taylor's public key in `dist/signingkey.asc`. You
can verify the public key's fingerprint against the Taylor Hornby's [contact
page](https://defuse.ca/contact.htm) and
[twitter](https://twitter.com/DefuseSec/status/723741424253059074).
Once you have verified the signature, it is safe to use the `.phar`. Place it
somewhere in your file system, e.g. `/var/www/lib/defuse-crypto.phar`, and then
pass that path to `require_once`.
```php
<?php
require_once('/var/www/lib/defuse-crypto.phar');
// ... the Crypto, File, Key, and KeyProtectedByPassword classes are now
// available ...
// ...
```

View File

@@ -0,0 +1,166 @@
Information for the Developers of php-encryption
=================================================
Status
-------
This library is currently frozen under a long-term support release. We do not
plan to add any new features. We will maintain the library by fixing any bugs
that are reported, or security vulnerabilities that are found.
Development Environment
------------------------
Development is done on Linux. To run the tests, you will need to have the
following tools installed:
- `php` (with OpenSSL enabled, if you're compiling from source).
- `gpg`
- `composer`
Running the Tests
------------------
First do `composer install` and then you can run the tests by running
`./test.sh`. This will download a PHPUnit PHAR, verify its cryptographic
signatures, and then use it to run the tests in `test/unit`.
Getting and Using Psalm
-----------------------
[Psalm](https://github.com/vimeo/psalm) is a static analysis suite for PHP projects.
We use Psalm to ensure type safety throughout our library.
To install Psalm, you just need to run one command:
composer require --dev "vimeo/psalm:dev-master"
To verify that your code changes are still strictly type-safe, run the following
command:
vendor/bin/psalm
Reporting Bugs
---------------
Please report bugs, even critical security vulnerabilities, by opening an issue
on GitHub. We recommend disclosing security vulnerabilities found in this
library *publicly* as soon as possible.
Philosophy
-----------
This library is developed around several core values:
- Rule #1: Security is prioritized over everything else.
> Whenever there is a conflict between security and some other property,
> security will be favored. For example, the library has runtime tests,
> which make it slower, but will hopefully stop it from encrypting stuff
> if the platform it's running on is broken.
- Rule #2: It should be difficult to misuse the library.
> We assume the developers using this library have no experience with
> cryptography. We only assume that they know that the "key" is something
> you need to encrypt and decrypt the messages, and that it must be kept
> secret. Whenever possible, the library should refuse to encrypt or decrypt
> messages when it is not being used correctly.
- Rule #3: The library aims only to be compatible with itself.
> Other PHP encryption libraries try to support every possible type of
> encryption, even the insecure ones (e.g. ECB mode). Because there are so
> many options, inexperienced developers must decide whether to use "CBC
> mode" or "ECB mode" when both are meaningless terms to them. This
> inevitably leads to vulnerabilities.
> This library will only support one secure mode. A developer using this
> library will call "encrypt" and "decrypt" methods without worrying about
> how they are implemented.
- Rule #4: The library should require no special installation.
> Some PHP encryption libraries, like libsodium-php, are not straightforward
> to install and cannot packaged with "just download and extract"
> applications. This library will always be just a handful of PHP files that
> you can copy to your source tree and require().
Publishing Releases
--------------------
To make a release, you will need to install [composer](https://getcomposer.org/)
and [box](https://github.com/box-project/box2) on your system. They will need to
be available in your `$PATH` so that running the commands `composer` and `box`
in your terminal run them, respectively. You will also need the private key for
signing (ID: 7B4B2D98) available.
Once you have those tools installed and the key available follow these steps:
**Remember to set the version number in `composer.json`!**
Make a fresh clone of the repository:
```
git clone <url>
```
Check out the branch you want to release:
```
git checkout <branchname>
```
Check that the version number in composer.json is correct (or not specified so that it gets picked up from the git tag):
```
cat composer.json
```
Check that the version number and support lifetime in README.md are correct:
```
cat README.md
```
Run the tests:
```
composer install
./test.sh
```
Generate the `.phar`:
```
cd dist
make build-phar
```
Test the `.phar`:
```
cd ../
./test.sh dist/phar-testing-autoload.php
```
Sign the `.phar`:
```
cd dist
make sign-phar
```
Tag the release:
```
git -c user.signingkey=DD2E507F7BDB1669 tag -s "<TAG NAME>" -m "<TAG MESSAGE>"
```
`<TAG NAME>` should be in the format `v2.0.0` and `<TAG MESSAGE>` should look
like "Release of v2.0.0."
Push the tag to github, then use the
[releases](https://github.com/defuse/php-encryption/releases) page to draft
a new release for that tag. Upload the `.phar` and the `.phar.sig` file to be
included as part of that release.

View File

@@ -0,0 +1,314 @@
Tutorial
=========
Hello! If you're reading this file, it's because you want to add encryption to
one of your PHP projects. My job, as the person writing this documentation, is
to help you make sure you're doing the right thing and then show you how to use
this library to do it. To help me help you, please read the documentation
*carefully* and *deliberately*.
A Word of Caution
------------------
Encryption is not magic dust you can sprinkle on a system to make it more
secure. The way encryption is integrated into a system's design needs to be
carefully thought out. Sometimes, encryption is the wrong thing to use. Other
times, encryption needs to be used in a very specific way in order for it to
work as intended. Even if you are sure of what you are doing, we strongly
recommend seeking advice from an expert.
The first step is to think about your application's threat model. Ask yourself
the following questions. Who will want to attack my application, and what will
they get out of it? Are they trying to steal some information? Trying to alter
or destroy some information? Or just trying to make the system go down so people
can't access it? Then ask yourself how encryption can help combat those threats.
If you're going to add encryption to your application, you should have a very
clear idea of exactly which kinds of attacks it's helping to secure your
application against. Once you have your threat model, think about what kinds of
attacks it *does not* cover, and whether or not you should improve your threat
model to include those attacks.
**This isn't for storing user login passwords:** The most common use of
cryptography in web applications is to protect the users' login passwords. If
you're trying to use this library to "encrypt" your users' passwords, you're in
the wrong place. Passwords shouldn't be *encrypted*, they should be *hashed*
with a slow computation-heavy function that makes password guessing attacks more
expensive. See [How to Safely Store Your Users' Passwords in
2016](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016).
**This isn't for encrypting network communication:** Likewise, if you're trying
to encrypt messages sent between two parties over the Internet, you don't want
to be using this library. For that, set up a TLS connection between the two
points, or, if it's a chat app, use the [Signal
Protocol](https://whispersystems.org/blog/advanced-ratcheting/).
What this library provides is symmetric encryption for "data at rest." This
means it is not suitable for use in building protocols where "data is in motion"
(i.e. moving over a network) except in limited set of cases.
Please note that **encryption does not, and is not intended to, hide the
*length* of the data being encrypted.** For example, it is not safe to encrypt
a field in which only a small number of different-length values are possible
(e.g. "male" or "female") since it would be possible to tell what the plaintext
is by looking at the length of the ciphertext. In order to do this safely, it is
your responsibility to, before encrypting, pad the data out to the length of the
longest string that will ever be encrypted. This way, all plaintexts are the
same length, and no information about the plaintext can be gleaned from the
length of the ciphertext.
Getting the Code
-----------------
There are several different ways to obtain this library's code and to add it to
your project. Even if you've already cloned the code from GitHub, you should
take steps to verify the cryptographic signatures to make sure the code you got
was not intercepted and modified by an attacker.
Please head over to the [**Installing and
Verifying**](InstallingAndVerifying.md) documentation to get the code, and then
come back here to continue the tutorial.
Using the Library
------------------
I'm going to assume you know what symmetric encryption is, and the difference
between symmetric and asymmetric encryption. If you don't, I recommend taking
[Dan Boneh's Cryptography I course](https://www.coursera.org/learn/crypto/) on
Coursera.
To give you a quick introduction to the library, I'm going to explain how it
would be used in two sterotypical scenarios. Hopefully, one of these sterotypes
is close enough to what you want to do that you'll be able to figure out what
needs to be different on your own.
### Formal Documentation
While this tutorial should get you up and running fast, it's important to
understand how this library behaves. Please make sure to read the formal
documentation of all of the functions you're using, since there are some
important security warnings there.
The following classes are available for you to use:
- [Crypto](classes/Crypto.md): Encrypting and decrypting strings.
- [File](classes/File.md): Encrypting and decrypting files.
- [Key](classes/Key.md): Represents a secret encryption key.
- [KeyProtectedByPassword](classes/KeyProtectedByPassword.md): Represents
a secret encryption key that needs to be "unlocked" by a password before it
can be used.
### Scenario #1: Keep data secret from the database administrator
In this scenario, our threat model is as follows. Alice is a server
administrator responsible for managing a trusted web server. Eve is a database
administrator responsible for managing a database server. Dave is a web
developer working on code that will eventually run on the trusted web server.
Let's say Alice and Dave trust each other, and Alice is going to host Dave's
application on her server. But both Alice and Dave don't trust Eve. They know
Eve is a good database administrator, but she might have incentive to steal the
data from the database. They want to keep some of the web application's data
secret from Eve.
In order to do that, Alice will use the included `generate-defuse-key` script
which generates a random encryption key and prints it to standard output:
```sh
$ composer require defuse/php-encryption
$ vendor/bin/generate-defuse-key
```
Alice will run this script once and save the output to a configuration file, say
in `/etc/daveapp-secret-key.txt` and set the file permissions so that only the
user that the website PHP scripts run as can access it.
Dave will write his code to load the key from the configuration file:
```php
<?php
use Defuse\Crypto\Key;
function loadEncryptionKeyFromConfig()
{
$keyAscii = // ... load the contents of /etc/daveapp-secret-key.txt
return Key::loadFromAsciiSafeString($keyAscii);
}
```
Then, whenever Dave wants to save a secret value to the database, he will first
encrypt it:
```php
<?php
use Defuse\Crypto\Crypto;
// ...
$key = loadEncryptionKeyFromConfig();
// ...
$ciphertext = Crypto::encrypt($secret_data, $key);
// ... save $ciphertext into the database ...
```
Whenever Dave wants to get the value back from the database, he must decrypt it
using the same key:
```php
<?php
use Defuse\Crypto\Crypto;
// ...
$key = loadEncryptionKeyFromConfig();
// ...
$ciphertext = // ... load $ciphertext from the database
try {
$secret_data = Crypto::decrypt($ciphertext, $key);
} catch (\Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
// An attack! Either the wrong key was loaded, or the ciphertext has
// changed since it was created -- either corrupted in the database or
// intentionally modified by Eve trying to carry out an attack.
// ... handle this case in a way that's suitable to your application ...
}
```
Note that if anyone ever steals the key from Alice's server, they can decrypt
all of the ciphertexts that are stored in the database. As part of our threat
model, we are assuming Alice's server administration skills and Dave's secure
coding skills are good enough to stop Eve from being able to steal the key.
Under those assumptions, this solution will prevent Eve from seeing data that's
stored in the database.
However, notice that our threat model says nothing about what could happen if
Eve wants to *modify* the data. With this solution, Eve will not be able to
alter any individual ciphertext (because each ciphertext has its own
cryptographic integrity check), but Eve *will* be able to swap ciphertexts for
one another, and revert ciphertexts to what they used to be at previous times.
If we needed to defend against such attacks, we would have to re-design our
threat model and come up with a different solution.
### Scenario #2: Encrypting account data with the user's login password
This scenario is like Scenario 1, but subtly different. The threat model is as
follows. We have Alice, a server administrator, and Dave, the developer. Alice
and Dave trust each other, and Alice wants to host Dave's web application,
including its database, on her server. Alice is worried about her server getting
hacked. The application will store the users' credit card numbers, and Alice
wants to protect them in case the server gets hacked.
We can model the situation like this: after the server gets hacked, the attacker
will have read and write access to all data on it until the attack is detected
and Alice rebuilds the server. We'll call the time the attacker has access to
the server the *exposure window.* One idea to minimize loss is to encrypt the
users' credit card numbers using a key made from their login password. Then, as
long as the users all have strong passwords, and they are never logged in during
the exposure window, their credit cards will be protected from the attacker.
To implement this, Dave will use the `KeyProtectedByPassword` class. When a new
user account is created, Dave will save a new key to their account, one that's
protected by their login password:
```php
<?php
use Defuse\Crypto\KeyProtectedByPassword;
function CreateUserAccount($username, $password)
{
// ... other user account creation stuff, including password hashing
$protected_key = KeyProtectedByPassword::createRandomPasswordProtectedKey($password);
$protected_key_encoded = $protected_key->saveToAsciiSafeString();
// ... save $protected_key_encoded into the user's account record
}
```
**WARNING:** Because of the way `KeyProtectedByPassword` is implemented, knowing
`SHA256($password)` is enough to decrypt a `KeyProtectedByPassword`. To be
secure, your application MUST NOT EVER compute `SHA256($password)` and use or
store it for any reason. You must also make sure that other libraries your
application is using don't compute it either.
Then, when the user logs in, Dave's code will load the protected key from the
user's account record, unlock it to get a `Key` object, and save the `Key`
object somewhere safe (like temporary memory-backed session storage or
a cookie). Note that wherever Dave's code saves the key, it must be destroyed
once the user logs out, or else the attacker might be able to find users' keys
even if they were never logged in during the attack.
```php
<?php
use Defuse\Crypto\KeyProtectedByPassword;
// ... authenticate the user using a good password hashing scheme
// keep the user's password in $password
$protected_key_encoded = // ... load it from the user's account record
$protected_key = KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
$user_key = $protected_key->unlockKey($password);
$user_key_encoded = $user_key->saveToAsciiSafeString();
// ... save $user_key_encoded in a cookie
```
```php
<?php
// ... when the user is logging out ...
// ... securely wipe the saved $user_key_encoded from the system ...
```
When a user adds their credit card number, Dave's code will get the key from the
memory-backed session or cookie and use it to encrypt the credit card number:
```php
<?php
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
// ...
$user_key_encoded = // ... get it out of the cookie ...
$user_key = Key::loadFromAsciiSafeString($user_key_encoded);
// ...
$credit_card_number = // ... get credit card number from the user
$encrypted_card_number = Crypto::encrypt($credit_card_number, $user_key);
// ... save $encrypted_card_number in the database
```
When the application needs to use the credit card number, it will decrypt it:
```php
<?php
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
// ...
$user_key_encoded = // ... get it out of the cookie
$user_key = Key::loadFromAsciiSafeString($user_key_encoded);
// ...
$encrypted_card_number = // ... load it from the database ...
try {
$credit_card_number = Crypto::decrypt($encrypted_card_number, $user_key);
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
// Either there's a bug in our code, we're trying to decrypt with the
// wrong key, or the encrypted credit card number was corrupted in the
// database.
// ... handle this case ...
}
```
With all caveats carefully heeded, this solution limits credit card number
exposure in the case where Alice's server gets hacked for a short amount of
time. Remember to think about the attacks that *aren't* included in our threat
model. The attacker is still free to do all sorts of harmful things like
modifying the server's data which may go undetected if Alice doesn't have secure
backups to compare against.
Getting Help
-------------
If you're having difficulty using the library, see if your problem is already
solved by an answer in the [FAQ](FAQ.md).

View File

@@ -0,0 +1,51 @@
Upgrading From Version 1.2
===========================
With version 2.0.0 of this library came major changes to the ciphertext format,
algorithms used for encryption, and API.
In version 1.2, keys were represented by 16-byte string variables. In version
2.0.0, keys are represented by objects, instances of the `Key` class. This
change was made in order to make it harder to misuse the API. For example, in
version 1.2, you could pass in *any* 16-byte string, but in version 2.0.0 you
need a `Key` object, which you can only get if you're "doing the right thing."
This means that for all of your old version 1.2 keys, you'll have to:
1. Generate a new version 2.0.0 key.
2. For all of the ciphertexts encrypted under the old key:
1. Decrypt the ciphertext using the old version 1.2 key.
2. Re-encrypt it using the new version 2.0.0 key.
Use the special `Crypto::legacyDecrypt()` method to decrypt the old ciphertexts
using the old key and then re-encrypt them using `Crypto::encrypt()` with the
new key. Your code will look something like the following. To avoid data loss,
securely back up your keys and data before running your upgrade code.
```php
<?php
// ...
$legacy_ciphertext = // ... get the ciphertext you want to upgrade ...
$legacy_key = // ... get the key to decrypt this ciphertext ...
// Generate the new key that we'll re-encrypt the ciphertext with.
$new_key = Key::createNewRandomKey();
// ... save it somewhere ...
// Decrypt it.
try {
$plaintext = Crypto::legacyDecrypt($legacy_ciphertext, $legacy_key);
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex)
{
// ... TODO: handle this case appropriately ...
}
// Re-encrypt it.
$new_ciphertext = Crypto::encrypt($plaintext, $new_key);
// ... replace the old $legacy_ciphertext with the new $new_ciphertext
// ...
```

View File

@@ -0,0 +1,280 @@
Class: Defuse\Crypto\Crypto
============================
The `Crypto` class provides encryption and decryption of strings either using
a secret key or secret password. For encryption and decryption of large files,
see the `File` class.
This code for this class is in `src/Crypto.php`.
Instance Methods
-----------------
This class has no instance methods.
Static Methods
---------------
### Crypto::encrypt($plaintext, Key $key, $raw\_binary = false)
**Description:**
Encrypts a plaintext string using a secret key.
**Parameters:**
1. `$plaintext` is the string to encrypt.
2. `$key` is an instance of `Key` containing the secret key for encryption.
3. `$raw_binary` determines whether the output will be a byte string (true) or
hex encoded (false, the default).
**Return value:**
Returns a ciphertext string representing `$plaintext` encrypted with the key
`$key`. Knowledge of `$key` is required in order to decrypt the ciphertext and
recover the plaintext.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `\TypeError` is thrown if the parameters are not of the expected types.
**Side-effects and performance:**
This method runs a small and very fast set of self-tests if it is the very first
time one of the `Crypto` methods has been called. The performance overhead is
negligible and can be safely ignored in all applications.
**Cautions:**
The ciphertext returned by this method is decryptable by anyone with knowledge
of the key `$key`. It is the caller's responsibility to keep `$key` secret.
Where `$key` should be stored is up to the caller and depends on the threat
model the caller is designing their application under. If you are unsure where
to store `$key`, consult with a professional cryptographer to get help designing
your application.
Please note that **encryption does not, and is not intended to, hide the
*length* of the data being encrypted.** For example, it is not safe to encrypt
a field in which only a small number of different-length values are possible
(e.g. "male" or "female") since it would be possible to tell what the plaintext
is by looking at the length of the ciphertext. In order to do this safely, it is
your responsibility to, before encrypting, pad the data out to the length of the
longest string that will ever be encrypted. This way, all plaintexts are the
same length, and no information about the plaintext can be gleaned from the
length of the ciphertext.
### Crypto::decrypt($ciphertext, Key $key, $raw\_binary = false)
**Description:**
Decrypts a ciphertext string using a secret key.
**Parameters:**
1. `$ciphertext` is the ciphertext to be decrypted.
2. `$key` is an instance of `Key` containing the secret key for decryption.
3. `$raw_binary` must have the same value as the `$raw_binary` given to the
call to `encrypt()` that generated `$ciphertext`.
**Return value:**
If the decryption succeeds, returns a string containing the same value as the
string that was passed to `encrypt()` when `$ciphertext` was produced. Upon
a successful return, the caller can be assured that `$ciphertext` could not have
been produced except by someone with knowledge of `$key`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$key` is not the correct key for the given ciphertext, or if the
ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
- `\TypeError` is thrown if the parameters are not of the expected types.
**Side-effects and performance:**
This method runs a small and very fast set of self-tests if it is the very first
time one of the `Crypto` methods has been called. The performance overhead is
negligible and can be safely ignored in all applications.
**Cautions:**
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong key and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.
### Crypto::encryptWithPassword($plaintext, $password, $raw\_binary = false)
**Description:**
Encrypts a plaintext string using a secret password.
**Parameters:**
1. `$plaintext` is the string to encrypt.
2. `$password` is a string containing the secret password used for encryption.
3. `$raw_binary` determines whether the output will be a byte string (true) or
hex encoded (false, the default).
**Return value:**
Returns a ciphertext string representing `$plaintext` encrypted with a key
derived from `$password`. Knowledge of `$password` is required in order to
decrypt the ciphertext and recover the plaintext.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `\TypeError` is thrown if the parameters are not of the expected types.
**Side-effects and performance:**
This method is intentionally slow, using a lot of CPU resources for a fraction
of a second. It applies key stretching to the password in order to make password
guessing attacks more computationally expensive. If you need a faster way to
encrypt multiple ciphertexts under the same password, see the
`KeyProtectedByPassword` class.
This method runs a small and very fast set of self-tests if it is the very first
time one of the `Crypto` methods has been called. The performance overhead is
negligible and can be safely ignored in all applications.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
### Crypto::decryptWithPassword($ciphertext, $password, $raw\_binary = false)
**Description:**
Decrypts a ciphertext string using a secret password.
**Parameters:**
1. `$ciphertext` is the ciphertext to be decrypted.
2. `$password` is a string containing the secret password used for decryption.
3. `$raw_binary` must have the same value as the `$raw_binary` given to the
call to `encryptWithPassword()` that generated `$ciphertext`.
**Return value:**
If the decryption succeeds, returns a string containing the same value as the
string that was passed to `encryptWithPassword()` when `$ciphertext` was
produced. Upon a successful return, the caller can be assured that `$ciphertext`
could not have been produced except by someone with knowledge of `$password`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$password` is not the correct password for the given ciphertext, or if
the ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
- `\TypeError` is thrown if the parameters are not of the expected types.
**Side-effects:**
This method is intentionally slow. It applies key stretching to the password in
order to make password guessing attacks more computationally expensive. If you
need a faster way to encrypt multiple ciphertexts under the same password, see
the `KeyProtectedByPassword` class.
This method runs a small and very fast set of self-tests if it is the very first
time one of the `Crypto` methods has been called. The performance overhead is
negligible and can be safely ignored in all applications.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong password and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.
### Crypto::legacyDecrypt($ciphertext, $key)
**Description:**
Decrypts a ciphertext produced by version 1 of this library so that the
plaintext can be re-encrypted into a version 2 ciphertext. See [Upgrading from
v1.2](../UpgradingFromV1.2.md).
**Parameters:**
1. `$ciphertext` is a ciphertext produced by version 1.x of this library.
2. `$key` is a 16-byte string (*not* a Key object) containing the key that was
used with version 1.x of this library to produce `$ciphertext`.
**Return value:**
If the decryption succeeds, returns the string that was encrypted to make
`$ciphertext` by version 1.x of this library. Upon a successful return, the
caller can be assured that `$ciphertext` could not have been produced except by
someone with knowledge of `$key`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$key` is not the correct key for the given ciphertext, or if the
ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
- `\TypeError` is thrown if the parameters are not of the expected types.
**Side-effects:**
This method runs a small and very fast set of self-tests if it is the very first
time one of the `Crypto` methods has been called. The performance overhead is
negligible and can be safely ignored in all applications.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$key` may be leaked out to an attacker through the stack trace. We
recommend configuring PHP to never output stack traces (either displaying them
to the user or saving them to log files).
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong key and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.

View File

@@ -0,0 +1,486 @@
Class: Defuse\Crypto\File
==========================
Instance Methods
-----------------
This class has no instance methods.
Static Methods
---------------
### File::encryptFile($inputFilename, $outputFilename, Key $key)
**Description:**
Encrypts a file using a secret key.
**Parameters:**
1. `$inputFilename` is the path to a file containing the plaintext to encrypt.
2. `$outputFilename` is the path to save the ciphertext file.
3. `$key` is an instance of `Key` containing the secret key for encryption.
**Behavior:**
Encrypts the contents of the input file, writing the result to the output file.
If the output file already exists, it is overwritten.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
None.
**Cautions:**
The ciphertext output by this method is decryptable by anyone with knowledge of
the key `$key`. It is the caller's responsibility to keep `$key` secret. Where
`$key` should be stored is up to the caller and depends on the threat model the
caller is designing their application under. If you are unsure where to store
`$key`, consult with a professional cryptographer to get help designing your
application.
Please note that **encryption does not, and is not intended to, hide the
*length* of the data being encrypted.** For example, it is not safe to encrypt
a field in which only a small number of different-length values are possible
(e.g. "male" or "female") since it would be possible to tell what the plaintext
is by looking at the length of the ciphertext. In order to do this safely, it is
your responsibility to, before encrypting, pad the data out to the length of the
longest string that will ever be encrypted. This way, all plaintexts are the
same length, and no information about the plaintext can be gleaned from the
length of the ciphertext.
### File::decryptFile($inputFilename, $outputFilename, Key $key)
**Description:**
Decrypts a file using a secret key.
**Parameters:**
1. `$inputFilename` is the path to a file containing the ciphertext to decrypt.
2. `$outputFilename` is the path to save the decrypted plaintext file.
3. `$key` is an instance of `Key` containing the secret key for decryption.
**Behavior:**
Decrypts the contents of the input file, writing the result to the output file.
If the output file already exists, it is overwritten.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$key` is not the correct key for the given ciphertext, or if the
ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
**Side-effects and performance:**
The input ciphertext is processed in two passes. The first pass verifies the
integrity and the second pass performs the actual decryption of the file and
writing to the output file. This is done in a streaming manner so that only
a small part of the file is ever loaded into memory at a time.
**Cautions:**
Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
thrown, some partial plaintext data may have been written to the output. Any
plaintext data that is output is guaranteed to be a prefix of the original
plaintext (i.e. at worst it was truncated). This can only happen if an attacker
modifies the input between the first pass (integrity check) and the second pass
(decryption) over the file.
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong key and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.
### File::encryptFileWithPassword($inputFilename, $outputFilename, $password)
**Description:**
Encrypts a file with a password.
**Parameters:**
1. `$inputFilename` is the path to a file containing the plaintext to encrypt.
2. `$outputFilename` is the path to save the ciphertext file.
3. `$password` is the password used for decryption.
**Behavior:**
Encrypts the contents of the input file, writing the result to the output file.
If the output file already exists, it is overwritten.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
This method is intentionally slow, using a lot of CPU resources for a fraction
of a second. It applies key stretching to the password in order to make password
guessing attacks more computationally expensive. If you need a faster way to
encrypt multiple ciphertexts under the same password, see the
`KeyProtectedByPassword` class.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
Please note that **encryption does not, and is not intended to, hide the
*length* of the data being encrypted.** For example, it is not safe to encrypt
a field in which only a small number of different-length values are possible
(e.g. "male" or "female") since it would be possible to tell what the plaintext
is by looking at the length of the ciphertext. In order to do this safely, it is
your responsibility to, before encrypting, pad the data out to the length of the
longest string that will ever be encrypted. This way, all plaintexts are the
same length, and no information about the plaintext can be gleaned from the
length of the ciphertext.
### File::decryptFileWithPassword($inputFilename, $outputFilename, $password)
**Description:**
Decrypts a file with a password.
**Parameters:**
1. `$inputFilename` is the path to a file containing the ciphertext to decrypt.
2. `$outputFilename` is the path to save the decrypted plaintext file.
3. `$password` is the password used for decryption.
**Behavior:**
Decrypts the contents of the input file, writing the result to the output file.
If the output file already exists, it is overwritten.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$password` is not the correct key for the given ciphertext, or if the
ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
**Side-effects and performance:**
This method is intentionally slow, using a lot of CPU resources for a fraction
of a second. It applies key stretching to the password in order to make password
guessing attacks more computationally expensive. If you need a faster way to
encrypt multiple ciphertexts under the same password, see the
`KeyProtectedByPassword` class.
The input ciphertext is processed in two passes. The first pass verifies the
integrity and the second pass performs the actual decryption of the file and
writing to the output file. This is done in a streaming manner so that only
a small part of the file is ever loaded into memory at a time.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
thrown, some partial plaintext data may have been written to the output. Any
plaintext data that is output is guaranteed to be a prefix of the original
plaintext (i.e. at worst it was truncated). This can only happen if an attacker
modifies the input between the first pass (integrity check) and the second pass
(decryption) over the file.
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong password and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.
### File::encryptResource($inputHandle, $outputHandle, Key $key)
**Description:**
Encrypts a resource (stream) with a secret key.
**Parameters:**
1. `$inputHandle` is a handle to a resource (like a file pointer) containing the
plaintext to encrypt.
2. `$outputHandle` is a handle to a resource (like a file pointer) that the
ciphertext will be written to.
3. `$key` is an instance of `Key` containing the secret key for encryption.
**Behavior:**
Encrypts the data read from the input stream and writes it to the output stream.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
None.
**Cautions:**
The ciphertext output by this method is decryptable by anyone with knowledge of
the key `$key`. It is the caller's responsibility to keep `$key` secret. Where
`$key` should be stored is up to the caller and depends on the threat model the
caller is designing their application under. If you are unsure where to store
`$key`, consult with a professional cryptographer to get help designing your
application.
Please note that **encryption does not, and is not intended to, hide the
*length* of the data being encrypted.** For example, it is not safe to encrypt
a field in which only a small number of different-length values are possible
(e.g. "male" or "female") since it would be possible to tell what the plaintext
is by looking at the length of the ciphertext. In order to do this safely, it is
your responsibility to, before encrypting, pad the data out to the length of the
longest string that will ever be encrypted. This way, all plaintexts are the
same length, and no information about the plaintext can be gleaned from the
length of the ciphertext.
### File::decryptResource($inputHandle, $outputHandle, Key $key)
**Description:**
Decrypts a resource (stream) with a secret key.
**Parameters:**
1. `$inputHandle` is a handle to a file-backed resource containing the
ciphertext to decrypt. It must be a file not a network stream or standard
input.
2. `$outputHandle` is a handle to a resource (like a file pointer) that the
plaintext will be written to.
3. `$key` is an instance of `Key` containing the secret key for decryption.
**Behavior:**
Decrypts the data read from the input stream and writes it to the output stream.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$key` is not the correct key for the given ciphertext, or if the
ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
**Side-effects and performance:**
The input ciphertext is processed in two passes. The first pass verifies the
integrity and the second pass performs the actual decryption of the file and
writing to the output file. This is done in a streaming manner so that only
a small part of the file is ever loaded into memory at a time.
**Cautions:**
Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
thrown, some partial plaintext data may have been written to the output. Any
plaintext data that is output is guaranteed to be a prefix of the original
plaintext (i.e. at worst it was truncated). This can only happen if an attacker
modifies the input between the first pass (integrity check) and the second pass
(decryption) over the file.
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong key and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.
### File::encryptResourceWithPassword($inputHandle, $outputHandle, $password)
**Description:**
Encrypts a resource (stream) with a password.
**Parameters:**
1. `$inputHandle` is a handle to a resource (like a file pointer) containing the
plaintext to encrypt.
2. `$outputHandle` is a handle to a resource (like a file pointer) that the
ciphertext will be written to.
3. `$password` is the password used for encryption.
**Behavior:**
Encrypts the data read from the input stream and writes it to the output stream.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
This method is intentionally slow, using a lot of CPU resources for a fraction
of a second. It applies key stretching to the password in order to make password
guessing attacks more computationally expensive. If you need a faster way to
encrypt multiple ciphertexts under the same password, see the
`KeyProtectedByPassword` class.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
Please note that **encryption does not, and is not intended to, hide the
*length* of the data being encrypted.** For example, it is not safe to encrypt
a field in which only a small number of different-length values are possible
(e.g. "male" or "female") since it would be possible to tell what the plaintext
is by looking at the length of the ciphertext. In order to do this safely, it is
your responsibility to, before encrypting, pad the data out to the length of the
longest string that will ever be encrypted. This way, all plaintexts are the
same length, and no information about the plaintext can be gleaned from the
length of the ciphertext.
### File::decryptResourceWithPassword($inputHandle, $outputHandle, $password)
**Description:**
Decrypts a resource (stream) with a password.
**Parameters:**
1. `$inputHandle` is a handle to a file-backed resource containing the
ciphertext to decrypt. It must be a file not a network stream or standard
input.
2. `$outputHandle` is a handle to a resource (like a file pointer) that the
plaintext will be written to.
3. `$password` is the password used for decryption.
**Behavior:**
Decrypts the data read from the input stream and writes it to the output stream.
**Return value:**
Does not return a value.
**Exceptions:**
- `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
the `$password` is not the correct key for the given ciphertext, or if the
ciphertext has been modified (possibly maliciously). There is no way to
distinguish between these two cases.
**Side-effects and performance:**
This method is intentionally slow, using a lot of CPU resources for a fraction
of a second. It applies key stretching to the password in order to make password
guessing attacks more computationally expensive. If you need a faster way to
encrypt multiple ciphertexts under the same password, see the
`KeyProtectedByPassword` class.
The input ciphertext is processed in two passes. The first pass verifies the
integrity and the second pass performs the actual decryption of the file and
writing to the output file. This is done in a streaming manner so that only
a small part of the file is ever loaded into memory at a time.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
thrown, some partial plaintext data may have been written to the output. Any
plaintext data that is output is guaranteed to be a prefix of the original
plaintext (i.e. at worst it was truncated). This can only happen if an attacker
modifies the input between the first pass (integrity check) and the second pass
(decryption) over the file.
It is impossible in principle to distinguish between the case where you attempt
to decrypt with the wrong password and the case where you attempt to decrypt
a modified (corrupted) ciphertext. It is up to the caller how to best deal with
this ambiguity, as it depends on the application this library is being used in.
If in doubt, consult with a professional cryptographer.

View File

@@ -0,0 +1,117 @@
Class: Defuse\Crypto\Key
=========================
The `Key` class represents a secret key used for encrypting and decrypting. Once
you have a `Key` instance, you can use it with the `Crypto` class to encrypt and
decrypt strings and with the `File` class to encrypt and decrypt files.
Instance Methods
-----------------
### saveToAsciiSafeString()
**Description:**
Saves the encryption key to a string of printable ASCII characters, which can be
loaded again into a `Key` instance using `Key::loadFromAsciiSafeString()`.
**Parameters:**
This method does not take any parameters.
**Return value:**
Returns a string of printable ASCII characters representing this `Key` instance,
which can be loaded back into an instance of `Key` using
`Key::loadFromAsciiSafeString()`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
None.
**Cautions:**
This method currently returns a hexadecimal string. You should not rely on this
behavior. For example, it may be improved in the future to return a base64
string.
Static Methods
---------------
### Key::createNewRandomKey()
**Description:**
Generates a new random key and returns an instance of `Key`.
**Parameters:**
This method does not take any parameters.
**Return value:**
Returns an instance of `Key` containing a randomly-generated encryption key.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
None.
**Cautions:**
None.
### Key::loadFromAsciiSafeString($saved\_key\_string, $do\_not\_trim = false)
**Description:**
Loads an instance of `Key` that was saved to a string by
`saveToAsciiSafeString()`.
By default, this function will call `Encoding::trimTrailingWhitespace()`
to remove trailing CR, LF, NUL, TAB, and SPACE characters, which are commonly
appended to files when working with text editors.
**Parameters:**
1. `$saved_key_string` is the string returned from `saveToAsciiSafeString()`
when the original `Key` instance was saved.
2. `$do_not_trim` should be set to `TRUE` if you do not wish for the library
to automatically strip trailing whitespace from the string.
**Return value:**
Returns an instance of `Key` representing the same encryption key as the one
that was represented by the `Key` instance that got saved into
`$saved_key_string` by a call to `saveToAsciiSafeString()`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\BadFormatException` is thrown whenever
`$saved_key_string` does not represent a valid `Key` instance.
**Side-effects and performance:**
None.
**Cautions:**
None.

View File

@@ -0,0 +1,259 @@
Class: Defuse\Crypto\KeyProtectedByPassword
============================================
The `KeyProtectedByPassword` class represents a key that is "locked" with
a password. In order to obtain an instance of `Key` that you can use for
encrypting and decrypting, a `KeyProtectedByPassword` must first be "unlocked"
by providing the correct password.
`KeyProtectedByPassword` provides an alternative to using the
`encryptWithPassword()`, `decryptWithPassword()`, `encryptFileWithPassword()`,
and `decryptFileWithPassword()` methods with several advantages:
- The slow and computationally-expensive key stretching is run only once when
you unlock a `KeyProtectedByPassword` to obtain the `Key`.
- You do not have to keep the original password in memory to encrypt and decrypt
things. After you've obtained the `Key` from a `KeyProtectedByPassword`, the
password is no longer necessary.
Instance Methods
-----------------
### saveToAsciiSafeString()
**Description:**
Saves the protected key to a string of printable ASCII characters, which can be
loaded again into a `KeyProtectedByPassword` instance using
`KeyProtectedByPassword::loadFromAsciiSafeString()`.
**Parameters:**
This method does not take any parameters.
**Return value:**
Returns a string of printable ASCII characters representing this
`KeyProtectedByPassword` instance, which can be loaded back into an instance of
`KeyProtectedByPassword` using
`KeyProtectedByPassword::loadFromAsciiSafeString()`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
None.
**Cautions:**
This method currently returns a hexadecimal string. You should not rely on this
behavior. For example, it may be improved in the future to return a base64
string.
### unlockKey($password)
**Description:**
Unlocks the password-protected key, obtaining a `Key` which can be used for
encryption and decryption.
**Parameters:**
1. `$password` is the password required to unlock this `KeyProtectedByPassword`
to obtain the `Key`.
**Return value:**
If `$password` is the correct password, then this method returns an instance of
the `Key` class.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
either the given `$password` is not the correct password for this
`KeyProtectedByPassword` or the ciphertext stored internally by this object
has been modified, i.e. it was accidentally corrupted or intentionally
corrupted by an attacker. There is no way for the caller to distinguish
between these two cases.
**Side-effects and performance:**
This method runs a small and very fast set of self-tests if it is the very first
time this method or one of the `Crypto` methods has been called. The performance
overhead is negligible and can be safely ignored in all applications.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
It is impossible in principle to distinguish between the case where you attempt
to unlock with the wrong password and the case where you attempt to unlock
a modified (corrupted) `KeyProtectedByPassword`. It is up to the caller how to
best deal with this ambiguity, as it depends on the application this library is
being used in. If in doubt, consult with a professional cryptographer.
### changePassword($current\_password, $new\_password)
**Description:**
Changes the password, so that calling `unlockKey` on this object in the future
will require you to pass `$new\_password` instead of the old password. It is
your responsibility to overwrite all stored copies of this
`KeyProtectedByPassword`. Any copies you leave lying around can still be
decrypted with the old password.
**Parameters:**
1. `$current\_password` is the password that this `KeyProtectedByPassword` is
currently protected with.
2. `$new\_password` is the new password, which the `KeyProtectedByPassword` will
be protected with once this operation completes.
**Return value:**
If `$current\_password` is the correct password, then this method updates itself
to be protected with the new password, and also returns itself.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
either the given `$current\_password` is not the correct password for this
`KeyProtectedByPassword` or the ciphertext stored internally by this object
has been modified, i.e. it was accidentally corrupted or intentionally
corrupted by an attacker. There is no way for the caller to distinguish
between these two cases.
**Side-effects and performance:**
This method runs a small and very fast set of self-tests if it is the very first
time this method or one of the `Crypto` methods has been called. The performance
overhead is negligible and can be safely ignored in all applications.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
It is impossible in principle to distinguish between the case where you attempt
to unlock with the wrong password and the case where you attempt to unlock
a modified (corrupted) `KeyProtectedByPassword`. It is up to the caller how to
best deal with this ambiguity, as it depends on the application this library is
being used in. If in doubt, consult with a professional cryptographer.
**WARNING:** Because of the way `KeyProtectedByPassword` is implemented, knowing
`SHA256($password)` is enough to decrypt a `KeyProtectedByPassword`. To be
secure, your application MUST NOT EVER compute `SHA256($password)` and use or
store it for any reason. You must also make sure that other libraries your
application is using don't compute it either.
Static Methods
---------------
### KeyProtectedByPassword::createRandomPasswordProtectedKey($password)
**Description:**
Generates a new random key that's protected by the string `$password` and
returns an instance of `KeyProtectedByPassword`.
**Parameters:**
1. `$password` is the password used to protect the random key.
**Return value:**
Returns an instance of `KeyProtectedByPassword` containing a randomly-generated
encryption key that's protected by the password `$password`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
**Side-effects and performance:**
This method runs a small and very fast set of self-tests if it is the very first
time this method or one of the `Crypto` methods has been called. The performance
overhead is negligible and can be safely ignored in all applications.
**Cautions:**
PHP stack traces display (portions of) the arguments passed to methods on the
call stack. If an exception is thrown inside this call, and it is uncaught, the
value of `$password` may be leaked out to an attacker through the stack trace.
We recommend configuring PHP to never output stack traces (either displaying
them to the user or saving them to log files).
Be aware that if you protecting multiple keys with the same password, an
attacker with write access to your system will be able to swap the protected
keys around so that the wrong key gets used next time it is unlocked. This could
lead to data being encrypted with the wrong key, perhaps one that the attacker
knows.
**WARNING:** Because of the way `KeyProtectedByPassword` is implemented, knowing
`SHA256($password)` is enough to decrypt a `KeyProtectedByPassword`. To be
secure, your application MUST NOT EVER compute `SHA256($password)` and use or
store it for any reason. You must also make sure that other libraries your
application is using don't compute it either.
### KeyProtectedByPassword::loadFromAsciiSafeString($saved\_key\_string)
**Description:**
Loads an instance of `KeyProtectedByPassword` that was saved to a string by
`saveToAsciiSafeString()`.
**Parameters:**
1. `$saved_key_string` is the string returned from `saveToAsciiSafeString()`
when the original `KeyProtectedByPassword` instance was saved.
**Return value:**
Returns an instance of `KeyProtectedByPassword` representing the same
password-protected key as the one that was represented by the
`KeyProtectedByPassword` instance that got saved into `$saved_key_string` by
a call to `saveToAsciiSafeString()`.
**Exceptions:**
- `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
the platform the code is running on cannot safely perform encryption for some
reason (e.g. it lacks a secure random number generator), or the runtime tests
detected a bug in this library.
- `Defuse\Crypto\Exception\BadFormatException` is thrown whenever
`$saved_key_string` does not represent a valid `KeyProtectedByPassword`
instance.
**Side-effects and performance:**
None.
**Cautions:**
None.

View File

@@ -1,29 +0,0 @@
<?php
/**
* 2010-2021 Webkul.
*
* NOTICE OF LICENSE
*
* All right is reserved,
* Please go through this link for complete license : https://store.webkul.com/license.html
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to newer
* versions in the future. If you wish to customize this module for your
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
*
* @author Webkul IN <support@webkul.com>
* @copyright 2010-2021 Webkul IN
* @license https://store.webkul.com/license.html
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -45,32 +45,31 @@ final class Core
* @throws Ex\EnvironmentIsBrokenException
*
* @return string
*
* @psalm-suppress RedundantCondition - It's valid to use is_int to check for overflow.
*/
public static function incrementCounter($ctr, $inc)
{
if (Core::ourStrlen($ctr) !== Core::BLOCK_BYTE_SIZE) {
throw new Ex\EnvironmentIsBrokenException(
'Trying to increment a nonce of the wrong size.'
);
}
Core::ensureTrue(
Core::ourStrlen($ctr) === Core::BLOCK_BYTE_SIZE,
'Trying to increment a nonce of the wrong size.'
);
if (! \is_int($inc)) {
throw new Ex\EnvironmentIsBrokenException(
'Trying to increment nonce by a non-integer.'
);
}
Core::ensureTrue(
\is_int($inc),
'Trying to increment nonce by a non-integer.'
);
if ($inc < 0) {
throw new Ex\EnvironmentIsBrokenException(
'Trying to increment nonce by a negative amount.'
);
}
// The caller is probably re-using CTR-mode keystream if they increment by 0.
Core::ensureTrue(
$inc > 0,
'Trying to increment a nonce by a nonpositive amount'
);
if ($inc > PHP_INT_MAX - 255) {
throw new Ex\EnvironmentIsBrokenException(
'Integer overflow may occur.'
);
}
Core::ensureTrue(
$inc <= PHP_INT_MAX - 255,
'Integer overflow may occur'
);
/*
* We start at the rightmost byte (big-endian)
@@ -80,11 +79,7 @@ final class Core
$sum = \ord($ctr[$i]) + $inc;
/* Detect integer overflow and fail. */
if (! \is_int($sum)) {
throw new Ex\EnvironmentIsBrokenException(
'Integer overflow in CTR mode nonce increment.'
);
}
Core::ensureTrue(\is_int($sum), 'Integer overflow in CTR mode nonce increment');
$ctr[$i] = \pack('C', $sum & 0xFF);
$inc = $sum >> 8;
@@ -103,9 +98,14 @@ final class Core
*/
public static function secureRandom($octets)
{
if ($octets <= 0) {
throw new Ex\CryptoException(
'A zero or negative amount of random bytes was requested.'
);
}
self::ensureFunctionExists('random_bytes');
try {
return \random_bytes($octets);
return \random_bytes(max(1, $octets));
} catch (\Exception $ex) {
throw new Ex\EnvironmentIsBrokenException(
'Your system does not have a secure random number generator.'
@@ -124,20 +124,30 @@ final class Core
* @param string $salt
*
* @throws Ex\EnvironmentIsBrokenException
* @psalm-suppress UndefinedFunction - We're checking if the function exists first.
*
* @return string
*/
public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
{
static $nativeHKDF = null;
if ($nativeHKDF === null) {
$nativeHKDF = \is_callable('\\hash_hkdf');
}
if ($nativeHKDF) {
if (\is_null($salt)) {
$salt = '';
}
return \hash_hkdf($hash, $ikm, $length, $info, $salt);
}
$digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true));
// Sanity-check the desired output length.
if (empty($length) || ! \is_int($length) ||
$length < 0 || $length > 255 * $digest_length) {
throw new Ex\EnvironmentIsBrokenException(
'Bad output length requested of HKDF.'
);
}
Core::ensureTrue(
!empty($length) && \is_int($length) && $length >= 0 && $length <= 255 * $digest_length,
'Bad output length requested of HDKF.'
);
// "if [salt] not provided, is set to a string of HashLen zeroes."
if (\is_null($salt)) {
@@ -152,9 +162,7 @@ final class Core
// HKDF-Expand:
// This check is useless, but it serves as a reminder to the spec.
if (Core::ourStrlen($prk) < $digest_length) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(Core::ourStrlen($prk) >= $digest_length);
// T(0) = ''
$t = '';
@@ -172,10 +180,9 @@ final class Core
}
// ORM = first L octets of T
/** @var string $orm */
$orm = Core::ourSubstr($t, 0, $length);
if ($orm === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(\is_string($orm));
return $orm;
}
@@ -209,9 +216,7 @@ final class Core
// We're not attempting to make variable-length string comparison
// secure, as that's very difficult. Make sure the strings are the same
// length.
if (Core::ourStrlen($expected) !== Core::ourStrlen($given)) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(Core::ourStrlen($expected) === Core::ourStrlen($given));
$blind = Core::secureRandom(32);
$message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind);
@@ -222,27 +227,47 @@ final class Core
* Throws an exception if the constant doesn't exist.
*
* @param string $name
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
*/
public static function ensureConstantExists($name)
{
if (! \defined($name)) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(
\defined($name),
'Constant '.$name.' does not exists'
);
}
/**
* Throws an exception if the function doesn't exist.
*
* @param string $name
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
*/
public static function ensureFunctionExists($name)
{
if (! \function_exists($name)) {
throw new Ex\EnvironmentIsBrokenException();
Core::ensureTrue(
\function_exists($name),
'function '.$name.' does not exists'
);
}
/**
* Throws an exception if the condition is false.
*
* @param bool $condition
* @param string $message
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
*/
public static function ensureTrue($condition, $message = '')
{
if (!$condition) {
throw new Ex\EnvironmentIsBrokenException($message);
}
}
@@ -265,13 +290,11 @@ final class Core
{
static $exists = null;
if ($exists === null) {
$exists = \function_exists('mb_strlen');
$exists = \extension_loaded('mbstring') && \function_exists('mb_strlen');
}
if ($exists) {
$length = \mb_strlen($str, '8bit');
if ($length === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue($length !== false);
return $length;
} else {
return \strlen($str);
@@ -287,39 +310,52 @@ final class Core
*
* @throws Ex\EnvironmentIsBrokenException
*
* @return string
* @return string|bool
*/
public static function ourSubstr($str, $start, $length = null)
{
static $exists = null;
if ($exists === null) {
$exists = \function_exists('mb_substr');
$exists = \extension_loaded('mbstring') && \function_exists('mb_substr');
}
// This is required to make mb_substr behavior identical to substr.
// Without this, mb_substr() would return false, contra to what the
// PHP documentation says (it doesn't say it can return false.)
$input_len = Core::ourStrlen($str);
if ($start === $input_len && !$length) {
return '';
}
if ($start > $input_len) {
return false;
}
// mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP 5.3,
// so we have to find the length ourselves. Also, substr() doesn't
// accept null for the length.
if (! isset($length)) {
if ($start >= 0) {
$length = $input_len - $start;
} else {
$length = -$start;
}
}
if ($length < 0) {
throw new \InvalidArgumentException(
"Negative lengths are not supported with ourSubstr."
);
}
if ($exists) {
// mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP
// 5.3, so we have to find the length ourselves.
if (! isset($length)) {
if ($start >= 0) {
$length = Core::ourStrlen($str) - $start;
} else {
$length = -$start;
}
}
// This is required to make mb_substr behavior identical to substr.
// Without this, mb_substr() would return false, contra to what the
// PHP documentation says (it doesn't say it can return false.)
if ($start === Core::ourStrlen($str) && $length === 0) {
return '';
}
if ($start > Core::ourStrlen($str)) {
return false;
}
$substr = \mb_substr($str, $start, $length, '8bit');
if (Core::ourStrlen($substr) !== $length) {
// At this point there are two cases where mb_substr can
// legitimately return an empty string. Either $length is 0, or
// $start is equal to the length of the string (both mb_substr and
// substr return an empty string when this happens). It should never
// ever return a string that's longer than $length.
if (Core::ourStrlen($substr) > $length || (Core::ourStrlen($substr) === 0 && $length !== 0 && $start !== $input_len)) {
throw new Ex\EnvironmentIsBrokenException(
'Your version of PHP has bug #66797. Its implementation of
mb_substr() is incorrect. See the details here:
@@ -329,12 +365,7 @@ final class Core
return $substr;
}
// Unlike mb_substr(), substr() doesn't accept NULL for length
if (isset($length)) {
return \substr($str, $start, $length);
} else {
return \substr($str, $start);
}
return \substr($str, $start, $length);
}
/**
@@ -355,7 +386,15 @@ final class Core
*
* @return string A $key_length-byte key derived from the password and salt.
*/
public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
public static function pbkdf2(
$algorithm,
#[\SensitiveParameter]
$password,
$salt,
$count,
$key_length,
$raw_output = false
)
{
// Type checks:
if (! \is_string($algorithm)) {
@@ -378,28 +417,22 @@ final class Core
$key_length += 0;
$algorithm = \strtolower($algorithm);
if (! \in_array($algorithm, \hash_algos(), true)) {
throw new Ex\EnvironmentIsBrokenException(
'Invalid or unsupported hash algorithm.'
);
}
Core::ensureTrue(
\in_array($algorithm, \hash_algos(), true),
'Invalid or unsupported hash algorithm.'
);
// Whitelist, or we could end up with people using CRC32.
$ok_algorithms = [
'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool',
];
if (! \in_array($algorithm, $ok_algorithms, true)) {
throw new Ex\EnvironmentIsBrokenException(
'Algorithm is not a secure cryptographic hash function.'
);
}
Core::ensureTrue(
\in_array($algorithm, $ok_algorithms, true),
'Algorithm is not a secure cryptographic hash function.'
);
if ($count <= 0 || $key_length <= 0) {
throw new Ex\EnvironmentIsBrokenException(
'Invalid PBKDF2 parameters.'
);
}
Core::ensureTrue($count > 0 && $key_length > 0, 'Invalid PBKDF2 parameters.');
if (\function_exists('hash_pbkdf2')) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
@@ -420,15 +453,18 @@ final class Core
$last = $xorsum = \hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
/**
* @psalm-suppress InvalidOperand
*/
$xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if ($raw_output) {
return Core::ourSubstr($output, 0, $key_length);
return (string) Core::ourSubstr($output, 0, $key_length);
} else {
return Encoding::binToHex(Core::ourSubstr($output, 0, $key_length));
return Encoding::binToHex((string) Core::ourSubstr($output, 0, $key_length));
}
}
}

View File

@@ -14,11 +14,27 @@ class Crypto
* @param bool $raw_binary
*
* @throws Ex\EnvironmentIsBrokenException
* @throws \TypeError
*
* @return string
*/
public static function encrypt($plaintext, Key $key, $raw_binary = false)
public static function encrypt($plaintext, $key, $raw_binary = false)
{
if (!\is_string($plaintext)) {
throw new \TypeError(
'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
);
}
if (!($key instanceof Key)) {
throw new \TypeError(
'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
);
}
if (!\is_bool($raw_binary)) {
throw new \TypeError(
'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
);
}
return self::encryptInternal(
$plaintext,
KeyOrPassword::createFromKey($key),
@@ -35,11 +51,32 @@ class Crypto
* @param bool $raw_binary
*
* @throws Ex\EnvironmentIsBrokenException
* @throws \TypeError
*
* @return string
*/
public static function encryptWithPassword($plaintext, $password, $raw_binary = false)
public static function encryptWithPassword(
$plaintext,
#[\SensitiveParameter]
$password,
$raw_binary = false
)
{
if (!\is_string($plaintext)) {
throw new \TypeError(
'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
);
}
if (!\is_string($password)) {
throw new \TypeError(
'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.'
);
}
if (!\is_bool($raw_binary)) {
throw new \TypeError(
'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
);
}
return self::encryptInternal(
$plaintext,
KeyOrPassword::createFromPassword($password),
@@ -54,13 +91,29 @@ class Crypto
* @param Key $key
* @param bool $raw_binary
*
* @throws \TypeError
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\WrongKeyOrModifiedCiphertextException
*
* @return string
*/
public static function decrypt($ciphertext, Key $key, $raw_binary = false)
public static function decrypt($ciphertext, $key, $raw_binary = false)
{
if (!\is_string($ciphertext)) {
throw new \TypeError(
'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
);
}
if (!($key instanceof Key)) {
throw new \TypeError(
'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
);
}
if (!\is_bool($raw_binary)) {
throw new \TypeError(
'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
);
}
return self::decryptInternal(
$ciphertext,
KeyOrPassword::createFromKey($key),
@@ -78,11 +131,32 @@ class Crypto
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\WrongKeyOrModifiedCiphertextException
* @throws \TypeError
*
* @return string
*/
public static function decryptWithPassword($ciphertext, $password, $raw_binary = false)
public static function decryptWithPassword(
$ciphertext,
#[\SensitiveParameter]
$password,
$raw_binary = false
)
{
if (!\is_string($ciphertext)) {
throw new \TypeError(
'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
);
}
if (!\is_string($password)) {
throw new \TypeError(
'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.'
);
}
if (!\is_bool($raw_binary)) {
throw new \TypeError(
'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
);
}
return self::decryptInternal(
$ciphertext,
KeyOrPassword::createFromPassword($password),
@@ -98,11 +172,27 @@ class Crypto
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\WrongKeyOrModifiedCiphertextException
* @throws \TypeError
*
* @return string
*/
public static function legacyDecrypt($ciphertext, $key)
public static function legacyDecrypt(
$ciphertext,
#[\SensitiveParameter]
$key
)
{
if (!\is_string($ciphertext)) {
throw new \TypeError(
'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
);
}
if (!\is_string($key)) {
throw new \TypeError(
'String expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
);
}
RuntimeTests::runtimeTest();
// Extract the HMAC from the front of the ciphertext.
@@ -111,14 +201,16 @@ class Crypto
'Ciphertext is too short.'
);
}
/**
* @var string
*/
$hmac = Core::ourSubstr($ciphertext, 0, Core::LEGACY_MAC_BYTE_SIZE);
if ($hmac === false) {
throw new Ex\EnvironmentIsBrokenException();
}
$ciphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE);
if ($ciphertext === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(\is_string($hmac));
/**
* @var string
*/
$messageCiphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE);
Core::ensureTrue(\is_string($messageCiphertext));
// Regenerate the same authentication sub-key.
$akey = Core::HKDF(
@@ -129,7 +221,7 @@ class Crypto
null
);
if (self::verifyHMAC($hmac, $ciphertext, $akey)) {
if (self::verifyHMAC($hmac, $messageCiphertext, $akey)) {
// Regenerate the same encryption sub-key.
$ekey = Core::HKDF(
Core::LEGACY_HASH_FUNCTION_NAME,
@@ -140,22 +232,25 @@ class Crypto
);
// Extract the IV from the ciphertext.
if (Core::ourStrlen($ciphertext) <= Core::LEGACY_BLOCK_BYTE_SIZE) {
if (Core::ourStrlen($messageCiphertext) <= Core::LEGACY_BLOCK_BYTE_SIZE) {
throw new Ex\WrongKeyOrModifiedCiphertextException(
'Ciphertext is too short.'
);
}
$iv = Core::ourSubstr($ciphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE);
if ($iv === false) {
throw new Ex\EnvironmentIsBrokenException();
}
$ciphertext = Core::ourSubstr($ciphertext, Core::LEGACY_BLOCK_BYTE_SIZE);
if ($ciphertext === false) {
throw new Ex\EnvironmentIsBrokenException();
}
/**
* @var string
*/
$iv = Core::ourSubstr($messageCiphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE);
Core::ensureTrue(\is_string($iv));
/**
* @var string
*/
$actualCiphertext = Core::ourSubstr($messageCiphertext, Core::LEGACY_BLOCK_BYTE_SIZE);
Core::ensureTrue(\is_string($actualCiphertext));
// Do the decryption.
$plaintext = self::plainDecrypt($ciphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD);
$plaintext = self::plainDecrypt($actualCiphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD);
return $plaintext;
} else {
throw new Ex\WrongKeyOrModifiedCiphertextException(
@@ -226,6 +321,7 @@ class Crypto
}
// Get and check the version header.
/** @var string $header */
$header = Core::ourSubstr($ciphertext, 0, Core::HEADER_VERSION_SIZE);
if ($header !== Core::CURRENT_VERSION) {
throw new Ex\WrongKeyOrModifiedCiphertextException(
@@ -234,36 +330,34 @@ class Crypto
}
// Get the salt.
/** @var string $salt */
$salt = Core::ourSubstr(
$ciphertext,
Core::HEADER_VERSION_SIZE,
Core::SALT_BYTE_SIZE
);
if ($salt === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(\is_string($salt));
// Get the IV.
/** @var string $iv */
$iv = Core::ourSubstr(
$ciphertext,
Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE,
Core::BLOCK_BYTE_SIZE
);
if ($iv === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(\is_string($iv));
// Get the HMAC.
/** @var string $hmac */
$hmac = Core::ourSubstr(
$ciphertext,
Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE,
Core::MAC_BYTE_SIZE
);
if ($hmac === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(\is_string($hmac));
// Get the actual encrypted ciphertext.
/** @var string $encrypted */
$encrypted = Core::ourSubstr(
$ciphertext,
Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE +
@@ -271,9 +365,7 @@ class Crypto
Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE -
Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE
);
if ($encrypted === false) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(\is_string($encrypted));
// Derive the separate encryption and authentication keys from the key
// or password, whichever it is.
@@ -300,10 +392,17 @@ class Crypto
*
* @return string
*/
protected static function plainEncrypt($plaintext, $key, $iv)
protected static function plainEncrypt(
$plaintext,
#[\SensitiveParameter]
$key,
#[\SensitiveParameter]
$iv
)
{
Core::ensureConstantExists('OPENSSL_RAW_DATA');
Core::ensureFunctionExists('openssl_encrypt');
/** @var string $ciphertext */
$ciphertext = \openssl_encrypt(
$plaintext,
Core::CIPHER_METHOD,
@@ -312,11 +411,7 @@ class Crypto
$iv
);
if ($ciphertext === false) {
throw new Ex\EnvironmentIsBrokenException(
'openssl_encrypt() failed.'
);
}
Core::ensureTrue(\is_string($ciphertext), 'openssl_encrypt() failed');
return $ciphertext;
}
@@ -333,10 +428,19 @@ class Crypto
*
* @return string
*/
protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod)
protected static function plainDecrypt(
$ciphertext,
#[\SensitiveParameter]
$key,
#[\SensitiveParameter]
$iv,
$cipherMethod
)
{
Core::ensureConstantExists('OPENSSL_RAW_DATA');
Core::ensureFunctionExists('openssl_decrypt');
/** @var string $plaintext */
$plaintext = \openssl_decrypt(
$ciphertext,
$cipherMethod,
@@ -344,11 +448,7 @@ class Crypto
OPENSSL_RAW_DATA,
$iv
);
if ($plaintext === false) {
throw new Ex\EnvironmentIsBrokenException(
'openssl_decrypt() failed.'
);
}
Core::ensureTrue(\is_string($plaintext), 'openssl_decrypt() failed.');
return $plaintext;
}
@@ -356,7 +456,7 @@ class Crypto
/**
* Verifies an HMAC without leaking information through side-channels.
*
* @param string $correct_hmac
* @param string $expected_hmac
* @param string $message
* @param string $key
*
@@ -364,9 +464,14 @@ class Crypto
*
* @return bool
*/
protected static function verifyHMAC($correct_hmac, $message, $key)
protected static function verifyHMAC(
$expected_hmac,
$message,
#[\SensitiveParameter]
$key
)
{
$message_hmac = \hash_hmac(Core::HASH_FUNCTION_NAME, $message, $key, true);
return Core::hashEquals($correct_hmac, $message_hmac);
return Core::hashEquals($message_hmac, $expected_hmac);
}
}

View File

@@ -2,13 +2,25 @@
namespace Defuse\Crypto;
/**
* Class DerivedKeys
* @package Defuse\Crypto
*/
final class DerivedKeys
{
private $akey = null;
private $ekey = null;
/**
* @var string
*/
private $akey = '';
/**
* @var string
*/
private $ekey = '';
/**
* Returns the authentication key.
* @return string
*/
public function getAuthenticationKey()
{
@@ -17,6 +29,7 @@ final class DerivedKeys
/**
* Returns the encryption key.
* @return string
*/
public function getEncryptionKey()
{

View File

@@ -46,6 +46,7 @@ final class Encoding
* @throws Ex\EnvironmentIsBrokenException
*
* @return string
* @psalm-suppress TypeDoesNotContainType
*/
public static function hexToBin($hex_string)
{
@@ -78,6 +79,62 @@ final class Encoding
return $bin;
}
/**
* Remove trialing whitespace without table look-ups or branches.
*
* Calling this function may leak the length of the string as well as the
* number of trailing whitespace characters through side-channels.
*
* @param string $string
* @return string
*/
public static function trimTrailingWhitespace($string = '')
{
$length = Core::ourStrlen($string);
if ($length < 1) {
return '';
}
do {
$prevLength = $length;
$last = $length - 1;
$chr = \ord($string[$last]);
/* Null Byte (0x00), a.k.a. \0 */
// if ($chr === 0x00) $length -= 1;
$sub = (($chr - 1) >> 8 ) & 1;
$length -= $sub;
$last -= $sub;
/* Horizontal Tab (0x09) a.k.a. \t */
$chr = \ord($string[$last]);
// if ($chr === 0x09) $length -= 1;
$sub = (((0x08 - $chr) & ($chr - 0x0a)) >> 8) & 1;
$length -= $sub;
$last -= $sub;
/* New Line (0x0a), a.k.a. \n */
$chr = \ord($string[$last]);
// if ($chr === 0x0a) $length -= 1;
$sub = (((0x09 - $chr) & ($chr - 0x0b)) >> 8) & 1;
$length -= $sub;
$last -= $sub;
/* Carriage Return (0x0D), a.k.a. \r */
$chr = \ord($string[$last]);
// if ($chr === 0x0d) $length -= 1;
$sub = (((0x0c - $chr) & ($chr - 0x0e)) >> 8) & 1;
$length -= $sub;
$last -= $sub;
/* Space */
$chr = \ord($string[$last]);
// if ($chr === 0x20) $length -= 1;
$sub = (((0x1f - $chr) & ($chr - 0x21)) >> 8) & 1;
$length -= $sub;
} while ($prevLength !== $length && $length > 0);
return (string) Core::ourSubstr($string, 0, $length);
}
/*
* SECURITY NOTE ON APPLYING CHECKSUMS TO SECRETS:
*
@@ -118,15 +175,18 @@ final class Encoding
*
* @return string
*/
public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
public static function saveBytesToChecksummedAsciiSafeString(
$header,
#[\SensitiveParameter]
$bytes
)
{
// Headers must be a constant length to prevent one type's header from
// being a prefix of another type's header, leading to ambiguity.
if (Core::ourStrlen($header) !== self::SERIALIZE_HEADER_BYTES) {
throw new Ex\EnvironmentIsBrokenException(
'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
);
}
Core::ensureTrue(
Core::ourStrlen($header) === self::SERIALIZE_HEADER_BYTES,
'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
);
return Encoding::binToHex(
$header .
@@ -151,16 +211,21 @@ final class Encoding
*
* @return string
*/
public static function loadBytesFromChecksummedAsciiSafeString($expected_header, $string)
public static function loadBytesFromChecksummedAsciiSafeString(
$expected_header,
#[\SensitiveParameter]
$string
)
{
// Headers must be a constant length to prevent one type's header from
// being a prefix of another type's header, leading to ambiguity.
if (Core::ourStrlen($expected_header) !== self::SERIALIZE_HEADER_BYTES) {
throw new Ex\EnvironmentIsBrokenException(
'Header must be 4 bytes.'
);
}
Core::ensureTrue(
Core::ourStrlen($expected_header) === self::SERIALIZE_HEADER_BYTES,
'Header must be 4 bytes.'
);
/* If you get an exception here when attempting to load from a file, first pass your
key to Encoding::trimTrailingWhitespace() to remove newline characters, etc. */
$bytes = Encoding::hexToBin($string);
/* Make sure we have enough bytes to get the version header and checksum. */
@@ -171,7 +236,7 @@ final class Encoding
}
/* Grab the version header. */
$actual_header = Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES);
$actual_header = (string) Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES);
if ($actual_header !== $expected_header) {
throw new Ex\BadFormatException(
@@ -180,14 +245,14 @@ final class Encoding
}
/* Grab the bytes that are part of the checksum. */
$checked_bytes = Core::ourSubstr(
$checked_bytes = (string) Core::ourSubstr(
$bytes,
0,
Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE
);
/* Grab the included checksum. */
$checksum_a = Core::ourSubstr(
$checksum_a = (string) Core::ourSubstr(
$bytes,
Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE,
self::CHECKSUM_BYTE_SIZE
@@ -203,7 +268,7 @@ final class Encoding
);
}
return Core::ourSubstr(
return (string) Core::ourSubstr(
$bytes,
self::SERIALIZE_HEADER_BYTES,
Core::ourStrlen($bytes) - self::SERIALIZE_HEADER_BYTES - self::CHECKSUM_BYTE_SIZE

View File

@@ -1,29 +0,0 @@
<?php
/**
* 2010-2021 Webkul.
*
* NOTICE OF LICENSE
*
* All right is reserved,
* Please go through this link for complete license : https://store.webkul.com/license.html
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to newer
* versions in the future. If you wish to customize this module for your
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
*
* @author Webkul IN <support@webkul.com>
* @copyright 2010-2021 Webkul IN
* @license https://store.webkul.com/license.html
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -12,6 +12,7 @@ final class File
* @param string $inputFilename
* @param string $outputFilename
* @param Key $key
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
@@ -32,11 +33,17 @@ final class File
* @param string $inputFilename
* @param string $outputFilename
* @param string $password
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
*/
public static function encryptFileWithPassword($inputFilename, $outputFilename, $password)
public static function encryptFileWithPassword(
$inputFilename,
$outputFilename,
#[\SensitiveParameter]
$password
)
{
self::encryptFileInternal(
$inputFilename,
@@ -51,6 +58,7 @@ final class File
* @param string $inputFilename
* @param string $outputFilename
* @param Key $key
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
@@ -72,12 +80,18 @@ final class File
* @param string $inputFilename
* @param string $outputFilename
* @param string $password
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
* @throws Ex\WrongKeyOrModifiedCiphertextException
*/
public static function decryptFileWithPassword($inputFilename, $outputFilename, $password)
public static function decryptFileWithPassword(
$inputFilename,
$outputFilename,
#[\SensitiveParameter]
$password
)
{
self::decryptFileInternal(
$inputFilename,
@@ -93,6 +107,7 @@ final class File
* @param resource $inputHandle
* @param resource $outputHandle
* @param Key $key
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\WrongKeyOrModifiedCiphertextException
@@ -114,12 +129,18 @@ final class File
* @param resource $inputHandle
* @param resource $outputHandle
* @param string $password
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
* @throws Ex\WrongKeyOrModifiedCiphertextException
*/
public static function encryptResourceWithPassword($inputHandle, $outputHandle, $password)
public static function encryptResourceWithPassword(
$inputHandle,
$outputHandle,
#[\SensitiveParameter]
$password
)
{
self::encryptResourceInternal(
$inputHandle,
@@ -135,6 +156,7 @@ final class File
* @param resource $inputHandle
* @param resource $outputHandle
* @param Key $key
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
@@ -156,12 +178,18 @@ final class File
* @param resource $inputHandle
* @param resource $outputHandle
* @param string $password
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
* @throws Ex\WrongKeyOrModifiedCiphertextException
*/
public static function decryptResourceWithPassword($inputHandle, $outputHandle, $password)
public static function decryptResourceWithPassword(
$inputHandle,
$outputHandle,
#[\SensitiveParameter]
$password
)
{
self::decryptResourceInternal(
$inputHandle,
@@ -176,14 +204,21 @@ final class File
* @param string $inputFilename
* @param string $outputFilename
* @param KeyOrPassword $secret
* @return void
*
* @throws Ex\CryptoException
* @throws Ex\IOException
*/
private static function encryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret)
{
if (file_exists($inputFilename) && file_exists($outputFilename) && realpath($inputFilename) === realpath($outputFilename)) {
throw new Ex\IOException('Input and output filenames must be different.');
}
/* Open the input file. */
self::removePHPUnitErrorHandler();
$if = @\fopen($inputFilename, 'rb');
self::restorePHPUnitErrorHandler();
if ($if === false) {
throw new Ex\IOException(
'Cannot open input file for encrypting: ' .
@@ -196,7 +231,9 @@ final class File
}
/* Open the output file. */
self::removePHPUnitErrorHandler();
$of = @\fopen($outputFilename, 'wb');
self::restorePHPUnitErrorHandler();
if ($of === false) {
\fclose($if);
throw new Ex\IOException(
@@ -240,14 +277,21 @@ final class File
* @param string $inputFilename
* @param string $outputFilename
* @param KeyOrPassword $secret
* @return void
*
* @throws Ex\CryptoException
* @throws Ex\IOException
*/
private static function decryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret)
{
if (file_exists($inputFilename) && file_exists($outputFilename) && realpath($inputFilename) === realpath($outputFilename)) {
throw new Ex\IOException('Input and output filenames must be different.');
}
/* Open the input file. */
self::removePHPUnitErrorHandler();
$if = @\fopen($inputFilename, 'rb');
self::restorePHPUnitErrorHandler();
if ($if === false) {
throw new Ex\IOException(
'Cannot open input file for decrypting: ' .
@@ -261,7 +305,9 @@ final class File
}
/* Open the output file. */
self::removePHPUnitErrorHandler();
$of = @\fopen($outputFilename, 'wb');
self::restorePHPUnitErrorHandler();
if ($of === false) {
\fclose($if);
throw new Ex\IOException(
@@ -306,9 +352,13 @@ final class File
* @param resource $inputHandle
* @param resource $outputHandle
* @param KeyOrPassword $secret
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
* @psalm-suppress PossiblyInvalidArgument
* Fixes erroneous errors caused by PHP 7.2 switching the return value
* of hash_init from a resource to a HashContext.
*/
private static function encryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret)
{
@@ -335,12 +385,12 @@ final class File
$iv = Core::secureRandom($ivsize);
/* Initialize a streaming HMAC state. */
/** @var mixed $hmac */
$hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey);
if ($hmac === false) {
throw new Ex\EnvironmentIsBrokenException(
'Cannot initialize a hash context'
);
}
Core::ensureTrue(
\is_resource($hmac) || \is_object($hmac),
'Cannot initialize a hash context'
);
/* Write the header, salt, and IV. */
self::writeBytes(
@@ -358,14 +408,18 @@ final class File
$thisIv = $iv;
/* How many blocks do we encrypt at a time? We increment by this value. */
$inc = Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE;
/**
* @psalm-suppress RedundantCast
*/
$inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE);
/* Loop until we reach the end of the input file. */
$at_file_end = false;
while (! (\feof($inputHandle) || $at_file_end)) {
/* Find out if we can read a full buffer, or only a partial one. */
/** @var int */
$pos = \ftell($inputHandle);
if ($pos === false) {
if (!\is_int($pos)) {
throw new Ex\IOException(
'Could not get current position in input file during encryption'
);
@@ -385,6 +439,7 @@ final class File
}
/* Encrypt this buffer. */
/** @var string */
$encrypted = \openssl_encrypt(
$read,
Core::CIPHER_METHOD,
@@ -393,11 +448,7 @@ final class File
$thisIv
);
if ($encrypted === false) {
throw new Ex\EnvironmentIsBrokenException(
'OpenSSL encryption error'
);
}
Core::ensureTrue(\is_string($encrypted), 'OpenSSL encryption error');
/* Write this buffer's ciphertext. */
self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted));
@@ -422,10 +473,14 @@ final class File
* @param resource $inputHandle
* @param resource $outputHandle
* @param KeyOrPassword $secret
* @return void
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\IOException
* @throws Ex\WrongKeyOrModifiedCiphertextException
* @psalm-suppress PossiblyInvalidArgument
* Fixes erroneous errors caused by PHP 7.2 switching the return value
* of hash_init from a resource to a HashContext.
*/
public static function decryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret)
{
@@ -476,18 +531,22 @@ final class File
$thisIv = $iv;
/* How many blocks do we encrypt at a time? We increment by this value. */
$inc = Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE;
/**
* @psalm-suppress RedundantCast
*/
$inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE);
/* Get the HMAC. */
if (\fseek($inputHandle, (-1 * Core::MAC_BYTE_SIZE), SEEK_END) === false) {
if (\fseek($inputHandle, (-1 * Core::MAC_BYTE_SIZE), SEEK_END) === -1) {
throw new Ex\IOException(
'Cannot seek to beginning of MAC within input file'
);
}
/* Get the position of the last byte in the actual ciphertext. */
/** @var int $cipher_end */
$cipher_end = \ftell($inputHandle);
if ($cipher_end === false) {
if (!\is_int($cipher_end)) {
throw new Ex\IOException(
'Cannot read input file'
);
@@ -496,25 +555,23 @@ final class File
--$cipher_end;
/* Read the HMAC. */
/** @var string $stored_mac */
$stored_mac = self::readBytes($inputHandle, Core::MAC_BYTE_SIZE);
/* Initialize a streaming HMAC state. */
/** @var mixed $hmac */
$hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey);
if ($hmac === false) {
throw new Ex\EnvironmentIsBrokenException(
'Cannot initialize a hash context'
);
}
Core::ensureTrue(\is_resource($hmac) || \is_object($hmac), 'Cannot initialize a hash context');
/* Reset file pointer to the beginning of the file after the header */
if (\fseek($inputHandle, Core::HEADER_VERSION_SIZE, SEEK_SET) === false) {
if (\fseek($inputHandle, Core::HEADER_VERSION_SIZE, SEEK_SET) === -1) {
throw new Ex\IOException(
'Cannot read seek within input file'
);
}
/* Seek to the start of the actual ciphertext. */
if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize, SEEK_CUR) === false) {
if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize, SEEK_CUR) === -1) {
throw new Ex\IOException(
'Cannot seek input file to beginning of ciphertext'
);
@@ -525,12 +582,14 @@ final class File
\hash_update($hmac, $header);
\hash_update($hmac, $file_salt);
\hash_update($hmac, $iv);
/** @var mixed $hmac2 */
$hmac2 = \hash_copy($hmac);
$break = false;
while (! $break) {
/** @var int $pos */
$pos = \ftell($inputHandle);
if ($pos === false) {
if (!\is_int($pos)) {
throw new Ex\IOException(
'Could not get current position in input file during decryption'
);
@@ -554,16 +613,14 @@ final class File
\hash_update($hmac, $read);
/* Remember this buffer-sized chunk's HMAC. */
/** @var mixed $chunk_mac */
$chunk_mac = \hash_copy($hmac);
if ($chunk_mac === false) {
throw new Ex\EnvironmentIsBrokenException(
'Cannot duplicate a hash context'
);
}
Core::ensureTrue(\is_resource($chunk_mac) || \is_object($chunk_mac), 'Cannot duplicate a hash context');
$macs []= \hash_final($chunk_mac);
}
/* Get the final HMAC, which should match the stored one. */
/** @var string $final_mac */
$final_mac = \hash_final($hmac, true);
/* Verify the HMAC. */
@@ -576,7 +633,7 @@ final class File
/* PASS #2: Decrypt and write output. */
/* Rewind to the start of the actual ciphertext. */
if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize + Core::HEADER_VERSION_SIZE, SEEK_SET) === false) {
if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize + Core::HEADER_VERSION_SIZE, SEEK_SET) === -1) {
throw new Ex\IOException(
'Could not move the input file pointer during decryption'
);
@@ -584,8 +641,9 @@ final class File
$at_file_end = false;
while (! $at_file_end) {
/** @var int $pos */
$pos = \ftell($inputHandle);
if ($pos === false) {
if (!\is_int($pos)) {
throw new Ex\IOException(
'Could not get current position in input file during decryption'
);
@@ -609,12 +667,9 @@ final class File
* remembered from pass #1 to ensure attackers didn't change the
* ciphertext after MAC verification. */
\hash_update($hmac2, $read);
/** @var mixed $calc_mac */
$calc_mac = \hash_copy($hmac2);
if ($calc_mac === false) {
throw new Ex\EnvironmentIsBrokenException(
'Cannot duplicate a hash context'
);
}
Core::ensureTrue(\is_resource($calc_mac) || \is_object($calc_mac), 'Cannot duplicate a hash context');
$calc = \hash_final($calc_mac);
if (empty($macs)) {
@@ -628,6 +683,7 @@ final class File
}
/* Decrypt this buffer-sized chunk. */
/** @var string $decrypted */
$decrypted = \openssl_decrypt(
$read,
Core::CIPHER_METHOD,
@@ -635,11 +691,7 @@ final class File
OPENSSL_RAW_DATA,
$thisIv
);
if ($decrypted === false) {
throw new Ex\EnvironmentIsBrokenException(
'OpenSSL decryption error'
);
}
Core::ensureTrue(\is_string($decrypted), 'OpenSSL decryption error');
/* Write the plaintext to the output file. */
self::writeBytes(
@@ -649,6 +701,7 @@ final class File
);
/* Increment the IV by the amount of blocks in a buffer. */
/** @var string $thisIv */
$thisIv = Core::incrementCounter($thisIv, $inc);
/* WARNING: Usually, unless the file is a multiple of the buffer
* size, $thisIv will contain an incorrect value here on the last
@@ -661,27 +714,25 @@ final class File
*
* @param resource $stream
* @param int $num_bytes
* @return string
*
* @throws Ex\IOException
* @throws Ex\EnvironmentIsBrokenException
*
* @return string
*/
public static function readBytes($stream, $num_bytes)
{
if ($num_bytes < 0) {
throw new Ex\EnvironmentIsBrokenException(
'Tried to read less than 0 bytes'
);
} elseif ($num_bytes === 0) {
Core::ensureTrue($num_bytes >= 0, 'Tried to read less than 0 bytes');
if ($num_bytes === 0) {
return '';
}
$buf = '';
$remaining = $num_bytes;
while ($remaining > 0 && ! \feof($stream)) {
/** @var string $read */
$read = \fread($stream, $remaining);
if ($read === false) {
if (!\is_string($read)) {
throw new Ex\IOException(
'Could not read from the file'
);
@@ -703,10 +754,9 @@ final class File
* @param resource $stream
* @param string $buf
* @param int $num_bytes
* @return int
*
* @throws Ex\IOException
*
* @return string
*/
public static function writeBytes($stream, $buf, $num_bytes = null)
{
@@ -726,13 +776,14 @@ final class File
}
$remaining = $num_bytes;
while ($remaining > 0) {
/** @var int $written */
$written = \fwrite($stream, $buf, $remaining);
if ($written === false) {
if (!\is_int($written)) {
throw new Ex\IOException(
'Could not write to the file'
);
}
$buf = Core::ourSubstr($buf, $written, null);
$buf = (string) Core::ourSubstr($buf, $written, null);
$remaining -= $written;
}
return $num_bytes;
@@ -747,9 +798,38 @@ final class File
{
$error = error_get_last();
if ($error === null) {
return '[no PHP error]';
return '[no PHP error, or you have a custom error handler set]';
} else {
return $error['message'];
}
}
/**
* PHPUnit sets an error handler, which prevents getLastErrorMessage() from working,
* because error_get_last does not work when custom handlers are set.
*
* This is a workaround, which should be a no-op in production deployments, to make
* getLastErrorMessage() return the error messages that the PHPUnit tests expect.
*
* If, in a production deployment, a custom error handler is set, the exception
* handling will still work as usual, but the error messages will be confusing.
*
* @return void
*/
private static function removePHPUnitErrorHandler() {
if (defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__')) {
set_error_handler(null);
}
}
/**
* Undoes what removePHPUnitErrorHandler did.
*
* @return void
*/
private static function restorePHPUnitErrorHandler() {
if (defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__')) {
restore_error_handler();
}
}
}

View File

@@ -9,7 +9,10 @@ final class Key
const KEY_CURRENT_VERSION = "\xDE\xF0\x00\x00";
const KEY_BYTE_SIZE = 32;
private $key_bytes = null;
/**
* @var string
*/
private $key_bytes;
/**
* Creates new random key.
@@ -26,15 +29,27 @@ final class Key
/**
* Loads a Key from its encoded form.
*
* By default, this function will call Encoding::trimTrailingWhitespace()
* to remove trailing CR, LF, NUL, TAB, and SPACE characters, which are
* commonly appended to files when working with text editors.
*
* @param string $saved_key_string
* @param bool $do_not_trim (default: false)
*
* @throws Ex\BadFormatException
* @throws Ex\EnvironmentIsBrokenException
*
* @return Key
*/
public static function loadFromAsciiSafeString($saved_key_string)
public static function loadFromAsciiSafeString(
#[\SensitiveParameter]
$saved_key_string,
$do_not_trim = false
)
{
if (!$do_not_trim) {
$saved_key_string = Encoding::trimTrailingWhitespace($saved_key_string);
}
$key_bytes = Encoding::loadBytesFromChecksummedAsciiSafeString(self::KEY_CURRENT_VERSION, $saved_key_string);
return new Key($key_bytes);
}
@@ -71,13 +86,15 @@ final class Key
*
* @throws Ex\EnvironmentIsBrokenException
*/
private function __construct($bytes)
private function __construct(
#[\SensitiveParameter]
$bytes
)
{
if (Core::ourStrlen($bytes) !== self::KEY_BYTE_SIZE) {
throw new Ex\EnvironmentIsBrokenException(
'Bad key length.'
);
}
Core::ensureTrue(
Core::ourStrlen($bytes) === self::KEY_BYTE_SIZE,
'Bad key length.'
);
$this->key_bytes = $bytes;
}

View File

@@ -10,8 +10,15 @@ final class KeyOrPassword
const SECRET_TYPE_KEY = 1;
const SECRET_TYPE_PASSWORD = 2;
private $secret_type = null;
private $secret = null;
/**
* @var int
*/
private $secret_type = 0;
/**
* @var Key|string
*/
private $secret;
/**
* Initializes an instance of KeyOrPassword from a key.
@@ -32,7 +39,10 @@ final class KeyOrPassword
*
* @return KeyOrPassword
*/
public static function createFromPassword($password)
public static function createFromPassword(
#[\SensitiveParameter]
$password
)
{
return new KeyOrPassword(self::SECRET_TYPE_PASSWORD, $password);
}
@@ -43,17 +53,23 @@ final class KeyOrPassword
*
* @param string $salt
*
* @throws Ex\CryptoException
* @throws Ex\EnvironmentIsBrokenException
*
* @return DerivedKeys
*/
public function deriveKeys($salt)
{
if (Core::ourStrlen($salt) !== Core::SALT_BYTE_SIZE) {
throw new Ex\EnvironmentIsBrokenException('Bad salt.');
}
Core::ensureTrue(
Core::ourStrlen($salt) === Core::SALT_BYTE_SIZE,
'Bad salt.'
);
if ($this->secret_type === self::SECRET_TYPE_KEY) {
Core::ensureTrue($this->secret instanceof Key);
/**
* @psalm-suppress PossiblyInvalidMethodCall
*/
$akey = Core::HKDF(
Core::HASH_FUNCTION_NAME,
$this->secret->getRawBytes(),
@@ -61,6 +77,9 @@ final class KeyOrPassword
Core::AUTHENTICATION_INFO_STRING,
$salt
);
/**
* @psalm-suppress PossiblyInvalidMethodCall
*/
$ekey = Core::HKDF(
Core::HASH_FUNCTION_NAME,
$this->secret->getRawBytes(),
@@ -70,12 +89,18 @@ final class KeyOrPassword
);
return new DerivedKeys($akey, $ekey);
} elseif ($this->secret_type === self::SECRET_TYPE_PASSWORD) {
Core::ensureTrue(\is_string($this->secret));
/* Our PBKDF2 polyfill is vulnerable to a DoS attack documented in
* GitHub issue #230. The fix is to pre-hash the password to ensure
* it is short. We do the prehashing here instead of in pbkdf2() so
* that pbkdf2() still computes the function as defined by the
* standard. */
/**
* @psalm-suppress PossiblyInvalidArgument
*/
$prehash = \hash(Core::HASH_FUNCTION_NAME, $this->secret, true);
$prekey = Core::pbkdf2(
Core::HASH_FUNCTION_NAME,
$prehash,
@@ -111,8 +136,20 @@ final class KeyOrPassword
* @param int $secret_type
* @param mixed $secret (either a Key or a password string)
*/
private function __construct($secret_type, $secret)
private function __construct(
$secret_type,
#[\SensitiveParameter]
$secret
)
{
// The constructor is private, so these should never throw.
if ($secret_type === self::SECRET_TYPE_KEY) {
Core::ensureTrue($secret instanceof Key);
} elseif ($secret_type === self::SECRET_TYPE_PASSWORD) {
Core::ensureTrue(\is_string($secret));
} else {
throw new Ex\EnvironmentIsBrokenException('Bad secret type.');
}
$this->secret_type = $secret_type;
$this->secret = $secret;
}

View File

@@ -8,7 +8,10 @@ final class KeyProtectedByPassword
{
const PASSWORD_KEY_CURRENT_VERSION = "\xDE\xF1\x00\x00";
private $encrypted_key = null;
/**
* @var string
*/
private $encrypted_key = '';
/**
* Creates a random key protected by the provided password.
@@ -19,7 +22,10 @@ final class KeyProtectedByPassword
*
* @return KeyProtectedByPassword
*/
public static function createRandomPasswordProtectedKey($password)
public static function createRandomPasswordProtectedKey(
#[\SensitiveParameter]
$password
)
{
$inner_key = Key::createNewRandomKey();
/* The password is hashed as a form of poor-man's domain separation
@@ -44,7 +50,10 @@ final class KeyProtectedByPassword
*
* @return KeyProtectedByPassword
*/
public static function loadFromAsciiSafeString($saved_key_string)
public static function loadFromAsciiSafeString(
#[\SensitiveParameter]
$saved_key_string
)
{
$encrypted_key = Encoding::loadBytesFromChecksummedAsciiSafeString(
self::PASSWORD_KEY_CURRENT_VERSION,
@@ -76,9 +85,13 @@ final class KeyProtectedByPassword
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\WrongKeyOrModifiedCiphertextException
*
* @param string $password
* @return Key
*/
public function unlockKey($password)
public function unlockKey(
#[\SensitiveParameter]
$password
)
{
try {
$inner_key_encoded = Crypto::decryptWithPassword(
@@ -100,6 +113,40 @@ final class KeyProtectedByPassword
}
}
/**
* Changes the password.
*
* @param string $current_password
* @param string $new_password
*
* @throws Ex\EnvironmentIsBrokenException
* @throws Ex\WrongKeyOrModifiedCiphertextException
*
* @return KeyProtectedByPassword
*/
public function changePassword(
#[\SensitiveParameter]
$current_password,
#[\SensitiveParameter]
$new_password
)
{
$inner_key = $this->unlockKey($current_password);
/* The password is hashed as a form of poor-man's domain separation
* between this use of encryptWithPassword() and other uses of
* encryptWithPassword() that the user may also be using as part of the
* same protocol. */
$encrypted_key = Crypto::encryptWithPassword(
$inner_key->saveToAsciiSafeString(),
\hash(Core::HASH_FUNCTION_NAME, $new_password, true),
true
);
$this->encrypted_key = $encrypted_key;
return $this;
}
/**
* Constructor for KeyProtectedByPassword.
*

View File

@@ -17,6 +17,7 @@ class RuntimeTests extends Crypto
* Runs the runtime tests.
*
* @throws Ex\EnvironmentIsBrokenException
* @return void
*/
public static function runtimeTest()
{
@@ -58,13 +59,9 @@ class RuntimeTests extends Crypto
RuntimeTests::HKDFTestVector();
RuntimeTests::testEncryptDecrypt();
if (Core::ourStrlen(Key::createNewRandomKey()->getRawBytes()) != Core::KEY_BYTE_SIZE) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(Core::ourStrlen(Key::createNewRandomKey()->getRawBytes()) === Core::KEY_BYTE_SIZE);
if (Core::ENCRYPTION_INFO_STRING == Core::AUTHENTICATION_INFO_STRING) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(Core::ENCRYPTION_INFO_STRING !== Core::AUTHENTICATION_INFO_STRING);
} catch (Ex\EnvironmentIsBrokenException $ex) {
// Do this, otherwise it will stay in the "tests are running" state.
$test_state = 3;
@@ -79,6 +76,7 @@ class RuntimeTests extends Crypto
* High-level tests of Crypto operations.
*
* @throws Ex\EnvironmentIsBrokenException
* @return void
*/
private static function testEncryptDecrypt()
{
@@ -95,9 +93,7 @@ class RuntimeTests extends Crypto
// the user into thinking it's just an invalid ciphertext!
throw new Ex\EnvironmentIsBrokenException();
}
if ($decrypted !== $data) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue($decrypted === $data);
// Modifying the ciphertext: Appending a string.
try {
@@ -148,6 +144,7 @@ class RuntimeTests extends Crypto
* Test HKDF against test vectors.
*
* @throws Ex\EnvironmentIsBrokenException
* @return void
*/
private static function HKDFTestVector()
{
@@ -164,9 +161,7 @@ class RuntimeTests extends Crypto
'34007208d5b887185865'
);
$computed_okm = Core::HKDF('sha256', $ikm, $length, $info, $salt);
if ($computed_okm !== $okm) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue($computed_okm === $okm);
// Test Case 7
$ikm = \str_repeat("\x0c", 22);
@@ -177,15 +172,14 @@ class RuntimeTests extends Crypto
'673a081d70cce7acfc48'
);
$computed_okm = Core::HKDF('sha1', $ikm, $length, '', null);
if ($computed_okm !== $okm) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue($computed_okm === $okm);
}
/**
* Test HMAC against test vectors.
*
* @throws Ex\EnvironmentIsBrokenException
* @return void
*/
private static function HMACTestVector()
{
@@ -193,15 +187,16 @@ class RuntimeTests extends Crypto
$key = \str_repeat("\x0b", 20);
$data = 'Hi There';
$correct = 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7';
if (\hash_hmac(Core::HASH_FUNCTION_NAME, $data, $key) !== $correct) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue(
\hash_hmac(Core::HASH_FUNCTION_NAME, $data, $key) === $correct
);
}
/**
* Test AES against test vectors.
*
* @throws Ex\EnvironmentIsBrokenException
* @return void
*/
private static function AESTestVector()
{
@@ -225,18 +220,9 @@ class RuntimeTests extends Crypto
);
$computed_ciphertext = Crypto::plainEncrypt($plaintext, $key, $iv);
if ($computed_ciphertext !== $ciphertext) {
echo \str_repeat("\n", 30);
echo \bin2hex($computed_ciphertext);
echo "\n---\n";
echo \bin2hex($ciphertext);
echo \str_repeat("\n", 30);
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue($computed_ciphertext === $ciphertext);
$computed_plaintext = Crypto::plainDecrypt($ciphertext, $key, $iv, Core::CIPHER_METHOD);
if ($computed_plaintext !== $plaintext) {
throw new Ex\EnvironmentIsBrokenException();
}
Core::ensureTrue($computed_plaintext === $plaintext);
}
}

View File

@@ -1,29 +0,0 @@
<?php
/**
* 2010-2021 Webkul.
*
* NOTICE OF LICENSE
*
* All right is reserved,
* Please go through this link for complete license : https://store.webkul.com/license.html
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to newer
* versions in the future. If you wish to customize this module for your
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
*
* @author Webkul IN <support@webkul.com>
* @copyright 2010-2021 Webkul IN
* @license https://store.webkul.com/license.html
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -1,35 +1,202 @@
geoip.inc
Copyright (C) 2007 MaxMind LLC
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
1. Definitions.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
geoipcity.inc
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
Copyright (C) 2004 Maxmind LLC
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

7
tools/geoip/autoload.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitd5b813279dd5262ff846bf9ff4d279c4::getLoader();

View File

@@ -0,0 +1,572 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
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;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
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 true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,350 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}

View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,14 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'MaxMind\\WebService\\' => array($vendorDir . '/maxmind/web-service-common/src/WebService'),
'MaxMind\\Exception\\' => array($vendorDir . '/maxmind/web-service-common/src/Exception'),
'MaxMind\\Db\\' => array($vendorDir . '/maxmind-db/reader/src/MaxMind/Db'),
'GeoIp2\\' => array($vendorDir . '/geoip2/geoip2/src'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
);

View File

@@ -0,0 +1,57 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitd5b813279dd5262ff846bf9ff4d279c4
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitd5b813279dd5262ff846bf9ff4d279c4', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInitd5b813279dd5262ff846bf9ff4d279c4', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitd5b813279dd5262ff846bf9ff4d279c4::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

View File

@@ -0,0 +1,62 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitd5b813279dd5262ff846bf9ff4d279c4
{
public static $prefixLengthsPsr4 = array (
'M' =>
array (
'MaxMind\\WebService\\' => 19,
'MaxMind\\Exception\\' => 18,
'MaxMind\\Db\\' => 11,
),
'G' =>
array (
'GeoIp2\\' => 7,
),
'C' =>
array (
'Composer\\CaBundle\\' => 18,
),
);
public static $prefixDirsPsr4 = array (
'MaxMind\\WebService\\' =>
array (
0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/WebService',
),
'MaxMind\\Exception\\' =>
array (
0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/Exception',
),
'MaxMind\\Db\\' =>
array (
0 => __DIR__ . '/..' . '/maxmind-db/reader/src/MaxMind/Db',
),
'GeoIp2\\' =>
array (
0 => __DIR__ . '/..' . '/geoip2/geoip2/src',
),
'Composer\\CaBundle\\' =>
array (
0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitd5b813279dd5262ff846bf9ff4d279c4::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd5b813279dd5262ff846bf9ff4d279c4::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitd5b813279dd5262ff846bf9ff4d279c4::$classMap;
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,19 @@
Copyright (C) 2016 Composer
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,85 @@
composer/ca-bundle
==================
Small utility library that lets you find a path to the system CA bundle,
and includes a fallback to the Mozilla CA bundle.
Originally written as part of [composer/composer](https://github.com/composer/composer),
now extracted and made available as a stand-alone library.
Installation
------------
Install the latest version with:
```bash
$ composer require composer/ca-bundle
```
Requirements
------------
* PHP 5.3.2 is required but using the latest version of PHP is highly recommended.
Basic usage
-----------
### `Composer\CaBundle\CaBundle`
- `CaBundle::getSystemCaRootBundlePath()`: Returns the system CA bundle path, or a path to the bundled one as fallback
- `CaBundle::getBundledCaBundlePath()`: Returns the path to the bundled CA file
- `CaBundle::validateCaFile($filename)`: Validates a CA file using openssl_x509_parse only if it is safe to use
- `CaBundle::isOpensslParseSafe()`: Test if it is safe to use the PHP function openssl_x509_parse()
- `CaBundle::reset()`: Resets the static caches
#### To use with curl
```php
$curl = curl_init("https://example.org/");
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile)) {
curl_setopt($curl, CURLOPT_CAPATH, $caPathOrFile);
} else {
curl_setopt($curl, CURLOPT_CAINFO, $caPathOrFile);
}
$result = curl_exec($curl);
```
#### To use with php streams
```php
$opts = array(
'http' => array(
'method' => "GET"
)
);
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile)) {
$opts['ssl']['capath'] = $caPathOrFile;
} else {
$opts['ssl']['cafile'] = $caPathOrFile;
}
$context = stream_context_create($opts);
$result = file_get_contents('https://example.com', false, $context);
```
#### To use with Guzzle
```php
$client = new \GuzzleHttp\Client([
\GuzzleHttp\RequestOptions::VERIFY => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath()
]);
```
License
-------
composer/ca-bundle is licensed under the MIT License, see the LICENSE file for details.

View File

@@ -0,0 +1,54 @@
{
"name": "composer/ca-bundle",
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"type": "library",
"license": "MIT",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues"
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^7.2 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^8 || ^9",
"phpstan/phpstan": "^1.10",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Composer\\CaBundle\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"scripts": {
"test": "@php phpunit",
"phpstan": "@php phpstan analyse"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
<?php
/*
* This file is part of composer/ca-bundle.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\CaBundle;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\PhpProcess;
/**
* @author Chris Smith <chris@cs278.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class CaBundle
{
/** @var string|null */
private static $caPath;
/** @var array<string, bool> */
private static $caFileValidity = array();
/**
* Returns the system CA bundle path, or a path to the bundled one
*
* This method was adapted from Sslurp.
* https://github.com/EvanDotPro/Sslurp
*
* (c) Evan Coury <me@evancoury.com>
*
* For the full copyright and license information, please see below:
*
* Copyright (c) 2013, Evan Coury
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
* @return string path to a CA bundle file or directory
*/
public static function getSystemCaRootBundlePath(?LoggerInterface $logger = null)
{
if (self::$caPath !== null) {
return self::$caPath;
}
$caBundlePaths = array();
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE');
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR');
$caBundlePaths[] = ini_get('openssl.cafile');
$caBundlePaths[] = ini_get('openssl.capath');
$otherLocations = array(
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
'/etc/ssl/cert.pem', // OpenBSD
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
'/usr/local/etc/openssl@1.1/cert.pem', // OS X homebrew, openssl@1.1 package
'/opt/homebrew/etc/openssl@3/cert.pem', // macOS silicon homebrew, openssl@3 package
'/opt/homebrew/etc/openssl@1.1/cert.pem', // macOS silicon homebrew, openssl@1.1 package
'/etc/pki/tls/certs',
'/etc/ssl/certs', // FreeBSD
);
$caBundlePaths = array_merge($caBundlePaths, $otherLocations);
foreach ($caBundlePaths as $caBundle) {
if ($caBundle && self::caFileUsable($caBundle, $logger)) {
return self::$caPath = $caBundle;
}
if ($caBundle && self::caDirUsable($caBundle, $logger)) {
return self::$caPath = $caBundle;
}
}
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
}
/**
* Returns the path to the bundled CA file
*
* In case you don't want to trust the user or the system, you can use this directly
*
* @return string path to a CA bundle file
*/
public static function getBundledCaBundlePath()
{
$caBundleFile = __DIR__.'/../res/cacert.pem';
// cURL does not understand 'phar://' paths
// see https://github.com/composer/ca-bundle/issues/10
if (0 === strpos($caBundleFile, 'phar://')) {
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-');
if (false === $tempCaBundleFile) {
throw new \RuntimeException('Could not create a temporary file to store the bundled CA file');
}
file_put_contents(
$tempCaBundleFile,
file_get_contents($caBundleFile)
);
register_shutdown_function(function() use ($tempCaBundleFile) {
@unlink($tempCaBundleFile);
});
$caBundleFile = $tempCaBundleFile;
}
return $caBundleFile;
}
/**
* Validates a CA file using opensl_x509_parse only if it is safe to use
*
* @param string $filename
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
*
* @return bool
*/
public static function validateCaFile($filename, ?LoggerInterface $logger = null)
{
static $warned = false;
if (isset(self::$caFileValidity[$filename])) {
return self::$caFileValidity[$filename];
}
$contents = file_get_contents($filename);
if (is_string($contents) && strlen($contents) > 0) {
$contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents);
if (null === $contents) {
// regex extraction failed
$isValid = false;
} else {
$isValid = (bool) openssl_x509_parse($contents);
}
} else {
$isValid = false;
}
if ($logger) {
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
}
return self::$caFileValidity[$filename] = $isValid;
}
/**
* Test if it is safe to use the PHP function openssl_x509_parse().
*
* This checks if OpenSSL extensions is vulnerable to remote code execution
* via the exploit documented as CVE-2013-6420.
*
* @return bool
*/
public static function isOpensslParseSafe()
{
return true;
}
/**
* Resets the static caches
* @return void
*/
public static function reset()
{
self::$caFileValidity = array();
self::$caPath = null;
}
/**
* @param string $name
* @return string|false
*/
private static function getEnvVariable($name)
{
if (isset($_SERVER[$name])) {
return (string) $_SERVER[$name];
}
if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) {
return (string) $value;
}
return false;
}
/**
* @param string|false $certFile
* @param LoggerInterface|null $logger
* @return bool
*/
private static function caFileUsable($certFile, ?LoggerInterface $logger = null)
{
return $certFile
&& self::isFile($certFile, $logger)
&& self::isReadable($certFile, $logger)
&& self::validateCaFile($certFile, $logger);
}
/**
* @param string|false $certDir
* @param LoggerInterface|null $logger
* @return bool
*/
private static function caDirUsable($certDir, ?LoggerInterface $logger = null)
{
return $certDir
&& self::isDir($certDir, $logger)
&& self::isReadable($certDir, $logger)
&& self::glob($certDir . '/*', $logger);
}
/**
* @param string $certFile
* @param LoggerInterface|null $logger
* @return bool
*/
private static function isFile($certFile, ?LoggerInterface $logger = null)
{
$isFile = @is_file($certFile);
if (!$isFile && $logger) {
$logger->debug(sprintf('Checked CA file %s does not exist or it is not a file.', $certFile));
}
return $isFile;
}
/**
* @param string $certDir
* @param LoggerInterface|null $logger
* @return bool
*/
private static function isDir($certDir, ?LoggerInterface $logger = null)
{
$isDir = @is_dir($certDir);
if (!$isDir && $logger) {
$logger->debug(sprintf('Checked directory %s does not exist or it is not a directory.', $certDir));
}
return $isDir;
}
/**
* @param string $certFileOrDir
* @param LoggerInterface|null $logger
* @return bool
*/
private static function isReadable($certFileOrDir, ?LoggerInterface $logger = null)
{
$isReadable = @is_readable($certFileOrDir);
if (!$isReadable && $logger) {
$logger->debug(sprintf('Checked file or directory %s is not readable.', $certFileOrDir));
}
return $isReadable;
}
/**
* @param string $pattern
* @param LoggerInterface|null $logger
* @return bool
*/
private static function glob($pattern, ?LoggerInterface $logger = null)
{
$certs = glob($pattern);
if ($certs === false) {
if ($logger) {
$logger->debug(sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern));
}
return false;
}
if (count($certs) === 0) {
if ($logger) {
$logger->debug(sprintf("No CA files found for pattern: %s", $pattern));
}
return false;
}
return true;
}
}

View File

@@ -0,0 +1,266 @@
{
"packages": [
{
"name": "composer/ca-bundle",
"version": "1.5.4",
"version_normalized": "1.5.4.0",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "bc0593537a463e55cadf45fd938d23b75095b7e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/bc0593537a463e55cadf45fd938d23b75095b7e1",
"reference": "bc0593537a463e55cadf45fd938d23b75095b7e1",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^7.2 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^8 || ^9",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"time": "2024-11-27T15:35:25+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"keywords": [
"cabundle",
"cacert",
"certificate",
"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.5.4"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"install-path": "./ca-bundle"
},
{
"name": "geoip2/geoip2",
"version": "v3.1.0",
"version_normalized": "3.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/GeoIP2-php.git",
"reference": "c86fbeaa7e42279dd9e7af0b015384e721832b88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/c86fbeaa7e42279dd9e7af0b015384e721832b88",
"reference": "c86fbeaa7e42279dd9e7af0b015384e721832b88",
"shasum": ""
},
"require": {
"ext-json": "*",
"maxmind-db/reader": "^1.12.0",
"maxmind/web-service-common": "~0.10",
"php": ">=8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^10.0",
"squizlabs/php_codesniffer": "3.*"
},
"time": "2024-11-15T16:33:31+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"GeoIp2\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Gregory J. Oschwald",
"email": "goschwald@maxmind.com",
"homepage": "https://www.maxmind.com/"
}
],
"description": "MaxMind GeoIP2 PHP API",
"homepage": "https://github.com/maxmind/GeoIP2-php",
"keywords": [
"IP",
"geoip",
"geoip2",
"geolocation",
"maxmind"
],
"support": {
"issues": "https://github.com/maxmind/GeoIP2-php/issues",
"source": "https://github.com/maxmind/GeoIP2-php/tree/v3.1.0"
},
"install-path": "../geoip2/geoip2"
},
{
"name": "maxmind-db/reader",
"version": "v1.12.0",
"version_normalized": "1.12.0.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
"reference": "5b2d7a721dedfaef9dc20822c5fe7d26f9f8eb90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/5b2d7a721dedfaef9dc20822c5fe7d26f9f8eb90",
"reference": "5b2d7a721dedfaef9dc20822c5fe7d26f9f8eb90",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"conflict": {
"ext-maxminddb": "<1.11.1 || >=2.0.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": ">=8.0.0,<10.0.0",
"squizlabs/php_codesniffer": "3.*"
},
"suggest": {
"ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
"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": "2024-11-14T22:43:47+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"MaxMind\\Db\\": "src/MaxMind/Db"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Gregory J. Oschwald",
"email": "goschwald@maxmind.com",
"homepage": "https://www.maxmind.com/"
}
],
"description": "MaxMind DB Reader API",
"homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php",
"keywords": [
"database",
"geoip",
"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.12.0"
},
"install-path": "../maxmind-db/reader"
},
{
"name": "maxmind/web-service-common",
"version": "v0.10.0",
"version_normalized": "0.10.0.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/web-service-common-php.git",
"reference": "d7c7c42fc31bff26e0ded73a6e187bcfb193f9c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/d7c7c42fc31bff26e0ded73a6e187bcfb193f9c4",
"reference": "d7c7c42fc31bff26e0ded73a6e187bcfb193f9c4",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.0.3",
"ext-curl": "*",
"ext-json": "*",
"php": ">=8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "3.*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"time": "2024-11-14T23:14:52+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"MaxMind\\Exception\\": "src/Exception",
"MaxMind\\WebService\\": "src/WebService"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Gregory Oschwald",
"email": "goschwald@maxmind.com"
}
],
"description": "Internal MaxMind Web Service API",
"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.10.0"
},
"install-path": "../maxmind/web-service-common"
}
],
"dev": true,
"dev-package-names": []
}

View File

@@ -0,0 +1,59 @@
<?php return array(
'root' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => NULL,
'name' => '__root__',
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => NULL,
'dev_requirement' => false,
),
'composer/ca-bundle' => array(
'pretty_version' => '1.5.4',
'version' => '1.5.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/./ca-bundle',
'aliases' => array(),
'reference' => 'bc0593537a463e55cadf45fd938d23b75095b7e1',
'dev_requirement' => false,
),
'geoip2/geoip2' => array(
'pretty_version' => 'v3.1.0',
'version' => '3.1.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../geoip2/geoip2',
'aliases' => array(),
'reference' => 'c86fbeaa7e42279dd9e7af0b015384e721832b88',
'dev_requirement' => false,
),
'maxmind-db/reader' => array(
'pretty_version' => 'v1.12.0',
'version' => '1.12.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../maxmind-db/reader',
'aliases' => array(),
'reference' => '5b2d7a721dedfaef9dc20822c5fe7d26f9f8eb90',
'dev_requirement' => false,
),
'maxmind/web-service-common' => array(
'pretty_version' => 'v0.10.0',
'version' => '0.10.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../maxmind/web-service-common',
'aliases' => array(),
'reference' => 'd7c7c42fc31bff26e0ded73a6e187bcfb193f9c4',
'dev_requirement' => false,
),
),
);

View File

@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 80100)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@@ -1,820 +0,0 @@
<?php
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */
/* geoip.inc
*
* Copyright (C) 2007 MaxMind LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Hack PrestaShop because some users may use mod_geoip with this lib and it will cause a crash
*/
if (!defined("GEOIP_COUNTRY_BEGIN"))
define("GEOIP_COUNTRY_BEGIN", 16776960);
if (!defined("GEOIP_STATE_BEGIN_REV0"))
define("GEOIP_STATE_BEGIN_REV0", 16700000);
if (!defined("GEOIP_STATE_BEGIN_REV1"))
define("GEOIP_STATE_BEGIN_REV1", 16000000);
if (!defined("GEOIP_STANDARD"))
define("GEOIP_STANDARD", 0);
if (!defined("GEOIP_MEMORY_CACHE"))
define("GEOIP_MEMORY_CACHE", 1);
if (!defined("GEOIP_SHARED_MEMORY"))
define("GEOIP_SHARED_MEMORY", 2);
if (!defined("STRUCTURE_INFO_MAX_SIZE"))
define("STRUCTURE_INFO_MAX_SIZE", 20);
if (!defined("DATABASE_INFO_MAX_SIZE"))
define("DATABASE_INFO_MAX_SIZE", 100);
if (!defined("GEOIP_COUNTRY_EDITION"))
define("GEOIP_COUNTRY_EDITION", 106);
if (!defined("GEOIP_PROXY_EDITION"))
define("GEOIP_PROXY_EDITION", 8);
if (!defined("GEOIP_ASNUM_EDITION"))
define("GEOIP_ASNUM_EDITION", 9);
if (!defined("GEOIP_NETSPEED_EDITION"))
define("GEOIP_NETSPEED_EDITION", 10);
if (!defined("GEOIP_REGION_EDITION_REV0"))
define("GEOIP_REGION_EDITION_REV0", 112);
if (!defined("GEOIP_REGION_EDITION_REV1"))
define("GEOIP_REGION_EDITION_REV1", 3);
if (!defined("GEOIP_CITY_EDITION_REV0"))
define("GEOIP_CITY_EDITION_REV0", 111);
if (!defined("GEOIP_CITY_EDITION_REV1"))
define("GEOIP_CITY_EDITION_REV1", 2);
if (!defined("GEOIP_ORG_EDITION"))
define("GEOIP_ORG_EDITION", 110);
if (!defined("GEOIP_ISP_EDITION"))
define("GEOIP_ISP_EDITION", 4);
if (!defined("SEGMENT_RECORD_LENGTH"))
define("SEGMENT_RECORD_LENGTH", 3);
if (!defined("STANDARD_RECORD_LENGTH"))
define("STANDARD_RECORD_LENGTH", 3);
if (!defined("ORG_RECORD_LENGTH"))
define("ORG_RECORD_LENGTH", 4);
if (!defined("MAX_RECORD_LENGTH"))
define("MAX_RECORD_LENGTH", 4);
if (!defined("MAX_ORG_RECORD_LENGTH"))
define("MAX_ORG_RECORD_LENGTH", 300);
if (!defined("GEOIP_SHM_KEY"))
define("GEOIP_SHM_KEY", 0x4f415401);
if (!defined("US_OFFSET"))
define("US_OFFSET", 1);
if (!defined("CANADA_OFFSET"))
define("CANADA_OFFSET", 677);
if (!defined("WORLD_OFFSET"))
define("WORLD_OFFSET", 1353);
if (!defined("FIPS_RANGE"))
define("FIPS_RANGE", 360);
if (!defined("GEOIP_UNKNOWN_SPEED"))
define("GEOIP_UNKNOWN_SPEED", 0);
if (!defined("GEOIP_DIALUP_SPEED"))
define("GEOIP_DIALUP_SPEED", 1);
if (!defined("GEOIP_CABLEDSL_SPEED"))
define("GEOIP_CABLEDSL_SPEED", 2);
if (!defined("GEOIP_CORPORATE_SPEED"))
define("GEOIP_CORPORATE_SPEED", 3);
if (!defined("GEOIP_DOMAIN_EDITION"))
define("GEOIP_DOMAIN_EDITION", 11);
if (!defined("GEOIP_COUNTRY_EDITION_V6"))
define("GEOIP_COUNTRY_EDITION_V6", 12);
if (!defined("GEOIP_LOCATIONA_EDITION"))
define("GEOIP_LOCATIONA_EDITION", 13);
if (!defined("GEOIP_ACCURACYRADIUS_EDITION"))
define("GEOIP_ACCURACYRADIUS_EDITION", 14);
if (!defined("GEOIP_CITYCOMBINED_EDITION"))
define("GEOIP_CITYCOMBINED_EDITION", 15);
if (!defined("GEOIP_CITY_EDITION_REV1_V6"))
define("GEOIP_CITY_EDITION_REV1_V6", 30);
if (!defined("GEOIP_CITY_EDITION_REV0_V6"))
define("GEOIP_CITY_EDITION_REV0_V6",31);
if (!defined("GEOIP_NETSPEED_EDITION_REV1"))
define("GEOIP_NETSPEED_EDITION_REV1",32);
if (!defined("GEOIP_NETSPEED_EDITION_REV1_V6"))
define("GEOIP_NETSPEED_EDITION_REV1_V6",33);
if (!defined("GEOIP_USERTYPE_EDITION"))
define("GEOIP_USERTYPE_EDITION",28);
if (!defined("GEOIP_USERTYPE_EDITION_V6"))
define("GEOIP_USERTYPE_EDITION_V6",29);
if (!defined("GEOIP_ASNUM_EDITION_V6"))
define("GEOIP_ASNUM_EDITION_V6",21);
if (!defined("GEOIP_ISP_EDITION_V6"))
define("GEOIP_ISP_EDITION_V6",22);
if (!defined("GEOIP_ORG_EDITION_V6"))
define("GEOIP_ORG_EDITION_V6",23);
if (!defined("GEOIP_DOMAIN_EDITION_V6"))
define("GEOIP_DOMAIN_EDITION_V6",24);
if (!defined("CITYCOMBINED_FIXED_RECORD"))
define("CITYCOMBINED_FIXED_RECORD", 7 );
class GeoIP {
var $flags;
var $filehandle;
var $memory_buffer;
var $databaseType;
var $databaseSegments;
var $record_length;
var $shmid;
var $GEOIP_COUNTRY_CODE_TO_NUMBER = array(
"" => 0, "AP" => 1, "EU" => 2, "AD" => 3, "AE" => 4, "AF" => 5,
"AG" => 6, "AI" => 7, "AL" => 8, "AM" => 9, "CW" => 10, "AO" => 11,
"AQ" => 12, "AR" => 13, "AS" => 14, "AT" => 15, "AU" => 16, "AW" => 17,
"AZ" => 18, "BA" => 19, "BB" => 20, "BD" => 21, "BE" => 22, "BF" => 23,
"BG" => 24, "BH" => 25, "BI" => 26, "BJ" => 27, "BM" => 28, "BN" => 29,
"BO" => 30, "BR" => 31, "BS" => 32, "BT" => 33, "BV" => 34, "BW" => 35,
"BY" => 36, "BZ" => 37, "CA" => 38, "CC" => 39, "CD" => 40, "CF" => 41,
"CG" => 42, "CH" => 43, "CI" => 44, "CK" => 45, "CL" => 46, "CM" => 47,
"CN" => 48, "CO" => 49, "CR" => 50, "CU" => 51, "CV" => 52, "CX" => 53,
"CY" => 54, "CZ" => 55, "DE" => 56, "DJ" => 57, "DK" => 58, "DM" => 59,
"DO" => 60, "DZ" => 61, "EC" => 62, "EE" => 63, "EG" => 64, "EH" => 65,
"ER" => 66, "ES" => 67, "ET" => 68, "FI" => 69, "FJ" => 70, "FK" => 71,
"FM" => 72, "FO" => 73, "FR" => 74, "SX" => 75, "GA" => 76, "GB" => 77,
"GD" => 78, "GE" => 79, "GF" => 80, "GH" => 81, "GI" => 82, "GL" => 83,
"GM" => 84, "GN" => 85, "GP" => 86, "GQ" => 87, "GR" => 88, "GS" => 89,
"GT" => 90, "GU" => 91, "GW" => 92, "GY" => 93, "HK" => 94, "HM" => 95,
"HN" => 96, "HR" => 97, "HT" => 98, "HU" => 99, "ID" => 100, "IE" => 101,
"IL" => 102, "IN" => 103, "IO" => 104, "IQ" => 105, "IR" => 106, "IS" => 107,
"IT" => 108, "JM" => 109, "JO" => 110, "JP" => 111, "KE" => 112, "KG" => 113,
"KH" => 114, "KI" => 115, "KM" => 116, "KN" => 117, "KP" => 118, "KR" => 119,
"KW" => 120, "KY" => 121, "KZ" => 122, "LA" => 123, "LB" => 124, "LC" => 125,
"LI" => 126, "LK" => 127, "LR" => 128, "LS" => 129, "LT" => 130, "LU" => 131,
"LV" => 132, "LY" => 133, "MA" => 134, "MC" => 135, "MD" => 136, "MG" => 137,
"MH" => 138, "MK" => 139, "ML" => 140, "MM" => 141, "MN" => 142, "MO" => 143,
"MP" => 144, "MQ" => 145, "MR" => 146, "MS" => 147, "MT" => 148, "MU" => 149,
"MV" => 150, "MW" => 151, "MX" => 152, "MY" => 153, "MZ" => 154, "NA" => 155,
"NC" => 156, "NE" => 157, "NF" => 158, "NG" => 159, "NI" => 160, "NL" => 161,
"NO" => 162, "NP" => 163, "NR" => 164, "NU" => 165, "NZ" => 166, "OM" => 167,
"PA" => 168, "PE" => 169, "PF" => 170, "PG" => 171, "PH" => 172, "PK" => 173,
"PL" => 174, "PM" => 175, "PN" => 176, "PR" => 177, "PS" => 178, "PT" => 179,
"PW" => 180, "PY" => 181, "QA" => 182, "RE" => 183, "RO" => 184, "RU" => 185,
"RW" => 186, "SA" => 187, "SB" => 188, "SC" => 189, "SD" => 190, "SE" => 191,
"SG" => 192, "SH" => 193, "SI" => 194, "SJ" => 195, "SK" => 196, "SL" => 197,
"SM" => 198, "SN" => 199, "SO" => 200, "SR" => 201, "ST" => 202, "SV" => 203,
"SY" => 204, "SZ" => 205, "TC" => 206, "TD" => 207, "TF" => 208, "TG" => 209,
"TH" => 210, "TJ" => 211, "TK" => 212, "TM" => 213, "TN" => 214, "TO" => 215,
"TL" => 216, "TR" => 217, "TT" => 218, "TV" => 219, "TW" => 220, "TZ" => 221,
"UA" => 222, "UG" => 223, "UM" => 224, "US" => 225, "UY" => 226, "UZ" => 227,
"VA" => 228, "VC" => 229, "VE" => 230, "VG" => 231, "VI" => 232, "VN" => 233,
"VU" => 234, "WF" => 235, "WS" => 236, "YE" => 237, "YT" => 238, "RS" => 239,
"ZA" => 240, "ZM" => 241, "ME" => 242, "ZW" => 243, "A1" => 244, "A2" => 245,
"O1" => 246, "AX" => 247, "GG" => 248, "IM" => 249, "JE" => 250, "BL" => 251,
"MF" => 252, "BQ" => 253,
);
var $GEOIP_COUNTRY_CODES = array(
"","AP","EU","AD","AE","AF","AG","AI","AL","AM","CW",
"AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
"BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO",
"BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD",
"CF","CG","CH","CI","CK","CL","CM","CN","CO","CR",
"CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO",
"DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ",
"FK","FM","FO","FR","SX","GA","GB","GD","GE","GF",
"GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT",
"GU","GW","GY","HK","HM","HN","HR","HT","HU","ID",
"IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO",
"JP","KE","KG","KH","KI","KM","KN","KP","KR","KW",
"KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT",
"LU","LV","LY","MA","MC","MD","MG","MH","MK","ML",
"MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV",
"MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI",
"NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF",
"PG","PH","PK","PL","PM","PN","PR","PS","PT","PW",
"PY","QA","RE","RO","RU","RW","SA","SB","SC","SD",
"SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO",
"SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH",
"TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW",
"TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE",
"VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA",
"ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE",
"BL","MF", "BQ");
var $GEOIP_COUNTRY_CODES3 = array(
"","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","CUW",
"AGO","ATA","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB",
"BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL",
"BRA","BHS","BTN","BVT","BWA","BLR","BLZ","CAN","CCK","COD",
"CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI",
"CUB","CPV","CXR","CYP","CZE","DEU","DJI","DNK","DMA","DOM",
"DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI",
"FLK","FSM","FRO","FRA","SXM","GAB","GBR","GRD","GEO","GUF",
"GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","SGS","GTM",
"GUM","GNB","GUY","HKG","HMD","HND","HRV","HTI","HUN","IDN",
"IRL","ISR","IND","IOT","IRQ","IRN","ISL","ITA","JAM","JOR",
"JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT",
"CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU",
"LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI",
"MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV",
"MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC",
"NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF",
"PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW",
"PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN",
"SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM",
"SUR","STP","SLV","SYR","SWZ","TCA","TCD","ATF","TGO","THA",
"TJK","TKL","TKM","TUN","TON","TLS","TUR","TTO","TUV","TWN",
"TZA","UKR","UGA","UMI","USA","URY","UZB","VAT","VCT","VEN",
"VGB","VIR","VNM","VUT","WLF","WSM","YEM","MYT","SRB","ZAF",
"ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY",
"BLM","MAF", "BES"
);
var $GEOIP_COUNTRY_NAMES = array(
"","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Curacao",
"Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
"Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia",
"Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
"Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica",
"Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic",
"Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji",
"Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","Sint Maarten (Dutch part)","Gabon","United Kingdom","Grenada","Georgia","French Guiana",
"Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala",
"Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia",
"Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan",
"Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait",
"Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania",
"Luxembourg","Latvia","Libya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali",
"Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives",
"Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua",
"Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia",
"Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau",
"Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan",
"Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname",
"Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand",
"Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan",
"Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela",
"Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa",
"Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey",
"Saint Barthelemy","Saint Martin", "Bonaire, Saint Eustatius and Saba"
);
var $GEOIP_CONTINENT_CODES = array(
"--", "AS","EU","EU","AS","AS","NA","NA","EU","AS","NA",
"AF","AN","SA","OC","EU","OC","NA","AS","EU","NA",
"AS","EU","AF","EU","AS","AF","AF","NA","AS","SA",
"SA","NA","AS","AN","AF","EU","NA","NA","AS","AF",
"AF","AF","EU","AF","OC","SA","AF","AS","SA","NA",
"NA","AF","AS","AS","EU","EU","AF","EU","NA","NA",
"AF","SA","EU","AF","AF","AF","EU","AF","EU","OC",
"SA","OC","EU","EU","NA","AF","EU","NA","AS","SA",
"AF","EU","NA","AF","AF","NA","AF","EU","AN","NA",
"OC","AF","SA","AS","AN","NA","EU","NA","EU","AS",
"EU","AS","AS","AS","AS","AS","EU","EU","NA","AS",
"AS","AF","AS","AS","OC","AF","NA","AS","AS","AS",
"NA","AS","AS","AS","NA","EU","AS","AF","AF","EU",
"EU","EU","AF","AF","EU","EU","AF","OC","EU","AF",
"AS","AS","AS","OC","NA","AF","NA","EU","AF","AS",
"AF","NA","AS","AF","AF","OC","AF","OC","AF","NA",
"EU","EU","AS","OC","OC","OC","AS","NA","SA","OC",
"OC","AS","AS","EU","NA","OC","NA","AS","EU","OC",
"SA","AS","AF","EU","EU","AF","AS","OC","AF","AF",
"EU","AS","AF","EU","EU","EU","AF","EU","AF","AF",
"SA","AF","NA","AS","AF","NA","AF","AN","AF","AS",
"AS","OC","AS","AF","OC","AS","EU","NA","OC","AS",
"AF","EU","AF","OC","NA","SA","AS","EU","NA","SA",
"NA","NA","AS","OC","OC","OC","AS","AF","EU","AF",
"AF","EU","AF","--","--","--","EU","EU","EU","EU",
"NA","NA","NA"
);
}
if (!function_exists('geoip_load_shared_mem ')){
function geoip_load_shared_mem ($file) {
$fp = fopen($file, "rb");
if (!$fp) {
print "error opening $file: $php_errormsg\n";
exit;
}
$s_array = fstat($fp);
$size = $s_array['size'];
if ($shmid = @shmop_open (GEOIP_SHM_KEY, "w", 0, 0)) {
shmop_delete ($shmid);
shmop_close ($shmid);
}
$shmid = shmop_open (GEOIP_SHM_KEY, "c", 0644, $size);
shmop_write ($shmid, fread($fp, $size), 0);
shmop_close ($shmid);
}
}
if (!function_exists('_setup_segments')){
function _setup_segments($gi){
$gi->databaseType = GEOIP_COUNTRY_EDITION;
$gi->record_length = STANDARD_RECORD_LENGTH;
if ($gi->flags & GEOIP_SHARED_MEMORY) {
$offset = @shmop_size ($gi->shmid) - 3;
for ($i = 0; $i < STRUCTURE_INFO_MAX_SIZE; $i++) {
$delim = @shmop_read ($gi->shmid, $offset, 3);
$offset += 3;
if ($delim == (chr(255).chr(255).chr(255))) {
$gi->databaseType = ord(@shmop_read ($gi->shmid, $offset, 1));
$offset++;
if ($gi->databaseType == GEOIP_REGION_EDITION_REV0){
$gi->databaseSegments = GEOIP_STATE_BEGIN_REV0;
} else if ($gi->databaseType == GEOIP_REGION_EDITION_REV1){
$gi->databaseSegments = GEOIP_STATE_BEGIN_REV1;
} else if (($gi->databaseType == GEOIP_CITY_EDITION_REV0)||
($gi->databaseType == GEOIP_CITY_EDITION_REV1)
|| ($gi->databaseType == GEOIP_ORG_EDITION)
|| ($gi->databaseType == GEOIP_ORG_EDITION_V6)
|| ($gi->databaseType == GEOIP_DOMAIN_EDITION)
|| ($gi->databaseType == GEOIP_DOMAIN_EDITION_V6)
|| ($gi->databaseType == GEOIP_ISP_EDITION)
|| ($gi->databaseType == GEOIP_ISP_EDITION_V6)
|| ($gi->databaseType == GEOIP_USERTYPE_EDITION)
|| ($gi->databaseType == GEOIP_USERTYPE_EDITION_V6)
|| ($gi->databaseType == GEOIP_LOCATIONA_EDITION)
|| ($gi->databaseType == GEOIP_ACCURACYRADIUS_EDITION)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV0_V6)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV1_V6)
|| ($gi->databaseType == GEOIP_NETSPEED_EDITION_REV1)
|| ($gi->databaseType == GEOIP_NETSPEED_EDITION_REV1_V6)
|| ($gi->databaseType == GEOIP_ASNUM_EDITION)
|| ($gi->databaseType == GEOIP_ASNUM_EDITION_V6)){
$gi->databaseSegments = 0;
$buf = @shmop_read ($gi->shmid, $offset, SEGMENT_RECORD_LENGTH);
for ($j = 0;$j < SEGMENT_RECORD_LENGTH;$j++){
$gi->databaseSegments += (ord($buf[$j]) << ($j * 8));
}
if (($gi->databaseType == GEOIP_ORG_EDITION)
|| ($gi->databaseType == GEOIP_ORG_EDITION_V6)
|| ($gi->databaseType == GEOIP_DOMAIN_EDITION)
|| ($gi->databaseType == GEOIP_DOMAIN_EDITION_V6)
|| ($gi->databaseType == GEOIP_ISP_EDITION)
|| ($gi->databaseType == GEOIP_ISP_EDITION_V6)) {
$gi->record_length = ORG_RECORD_LENGTH;
}
}
break;
} else {
$offset -= 4;
}
}
if (($gi->databaseType == GEOIP_COUNTRY_EDITION)||
($gi->databaseType == GEOIP_COUNTRY_EDITION_V6)||
($gi->databaseType == GEOIP_PROXY_EDITION)||
($gi->databaseType == GEOIP_NETSPEED_EDITION)){
$gi->databaseSegments = GEOIP_COUNTRY_BEGIN;
}
} else {
$filepos = ftell($gi->filehandle);
fseek($gi->filehandle, -3, SEEK_END);
for ($i = 0; $i < STRUCTURE_INFO_MAX_SIZE; $i++) {
$delim = fread($gi->filehandle,3);
if ($delim == (chr(255).chr(255).chr(255))){
$gi->databaseType = ord(fread($gi->filehandle,1));
if ($gi->databaseType == GEOIP_REGION_EDITION_REV0){
$gi->databaseSegments = GEOIP_STATE_BEGIN_REV0;
}
else if ($gi->databaseType == GEOIP_REGION_EDITION_REV1){
$gi->databaseSegments = GEOIP_STATE_BEGIN_REV1;
} else if (($gi->databaseType == GEOIP_CITY_EDITION_REV0)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV1)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV0_V6)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV1_V6)
|| ($gi->databaseType == GEOIP_ORG_EDITION)
|| ($gi->databaseType == GEOIP_DOMAIN_EDITION)
|| ($gi->databaseType == GEOIP_ISP_EDITION)
|| ($gi->databaseType == GEOIP_ORG_EDITION_V6)
|| ($gi->databaseType == GEOIP_DOMAIN_EDITION_V6)
|| ($gi->databaseType == GEOIP_ISP_EDITION_V6)
|| ($gi->databaseType == GEOIP_LOCATIONA_EDITION)
|| ($gi->databaseType == GEOIP_ACCURACYRADIUS_EDITION)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV0_V6)
|| ($gi->databaseType == GEOIP_CITY_EDITION_REV1_V6)
|| ($gi->databaseType == GEOIP_NETSPEED_EDITION_REV1)
|| ($gi->databaseType == GEOIP_NETSPEED_EDITION_REV1_V6)
|| ($gi->databaseType == GEOIP_USERTYPE_EDITION)
|| ($gi->databaseType == GEOIP_USERTYPE_EDITION_V6)
|| ($gi->databaseType == GEOIP_ASNUM_EDITION)
|| ($gi->databaseType == GEOIP_ASNUM_EDITION_V6)){
$gi->databaseSegments = 0;
$buf = fread($gi->filehandle,SEGMENT_RECORD_LENGTH);
for ($j = 0;$j < SEGMENT_RECORD_LENGTH;$j++){
$gi->databaseSegments += (ord($buf[$j]) << ($j * 8));
}
if ( ( $gi->databaseType == GEOIP_ORG_EDITION )
|| ( $gi->databaseType == GEOIP_DOMAIN_EDITION )
|| ( $gi->databaseType == GEOIP_ISP_EDITION )
|| ( $gi->databaseType == GEOIP_ORG_EDITION_V6 )
|| ( $gi->databaseType == GEOIP_DOMAIN_EDITION_V6 )
|| ( $gi->databaseType == GEOIP_ISP_EDITION_V6 )) {
$gi->record_length = ORG_RECORD_LENGTH;
}
}
break;
} else {
fseek($gi->filehandle, -4, SEEK_CUR);
}
}
if (($gi->databaseType == GEOIP_COUNTRY_EDITION)||
($gi->databaseType == GEOIP_COUNTRY_EDITION_V6)||
($gi->databaseType == GEOIP_PROXY_EDITION)||
($gi->databaseType == GEOIP_NETSPEED_EDITION)){
$gi->databaseSegments = GEOIP_COUNTRY_BEGIN;
}
fseek($gi->filehandle,$filepos,SEEK_SET);
}
return $gi;
}
}
if (!function_exists('geoip_open')){
function geoip_open($filename, $flags) {
$gi = new GeoIP;
$gi->flags = $flags;
if ($gi->flags & GEOIP_SHARED_MEMORY) {
$gi->shmid = @shmop_open (GEOIP_SHM_KEY, "a", 0, 0);
} else {
$gi->filehandle = fopen($filename,"rb") or die( "Can not open $filename\n" );
if ($gi->flags & GEOIP_MEMORY_CACHE) {
$s_array = fstat($gi->filehandle);
$gi->memory_buffer = fread($gi->filehandle, $s_array['size']);
}
}
$gi = _setup_segments($gi);
return $gi;
}
}
if (!function_exists('geoip_close')){
function geoip_close($gi) {
if ($gi->flags & GEOIP_SHARED_MEMORY) {
return true;
}
return fclose($gi->filehandle);
}
}
if (!function_exists('geoip_country_id_by_name_v6')){
function geoip_country_id_by_name_v6($gi, $name) {
$rec = dns_get_record($name, DNS_AAAA);
if ( !$rec ) {
return false;
}
$addr = $rec[0]["ipv6"];
if (!$addr || $addr == $name) {
return false;
}
return geoip_country_id_by_addr_v6($gi, $addr);
}
}
if (!function_exists('geoip_country_id_by_name')){
function geoip_country_id_by_name($gi, $name) {
$addr = gethostbyname($name);
if (!$addr || $addr == $name) {
return false;
}
return geoip_country_id_by_addr($gi, $addr);
}
}
if (!function_exists('geoip_country_code_by_name_v6')){
function geoip_country_code_by_name_v6($gi, $name) {
$country_id = geoip_country_id_by_name_v6($gi,$name);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_CODES[$country_id];
}
return false;
}
}
if (!function_exists('geoip_country_code_by_name')){
function geoip_country_code_by_name($gi, $name) {
$country_id = geoip_country_id_by_name($gi,$name);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_CODES[$country_id];
}
return false;
}
}
if (!function_exists('geoip_country_name_by_name_v6')){
function geoip_country_name_by_name_v6($gi, $name) {
$country_id = geoip_country_id_by_name_v6($gi,$name);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_NAMES[$country_id];
}
return false;
}
}
if (!function_exists('geoip_country_name_by_name')){
function geoip_country_name_by_name($gi, $name) {
$country_id = geoip_country_id_by_name($gi,$name);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_NAMES[$country_id];
}
return false;
}
}
if (!function_exists('geoip_country_id_by_addr_v6')){
function geoip_country_id_by_addr_v6($gi, $addr) {
$ipnum = inet_pton($addr);
return _geoip_seek_country_v6($gi, $ipnum) - GEOIP_COUNTRY_BEGIN;
}
}
if (!function_exists('geoip_country_id_by_addr')){
function geoip_country_id_by_addr($gi, $addr) {
$ipnum = ip2long($addr);
return _geoip_seek_country($gi, $ipnum) - GEOIP_COUNTRY_BEGIN;
}
}
if (!function_exists('geoip_country_code_by_addr_v6')){
function geoip_country_code_by_addr_v6($gi, $addr) {
$country_id = geoip_country_id_by_addr_v6($gi,$addr);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_CODES[$country_id];
}
return false;
}
}
if (!function_exists('geoip_country_code_by_addr')){
function geoip_country_code_by_addr($gi, $addr) {
if ($gi->databaseType == GEOIP_CITY_EDITION_REV1) {
$record = geoip_record_by_addr($gi,$addr);
if ( $record !== false ) {
return $record->country_code;
}
} else {
$country_id = geoip_country_id_by_addr($gi,$addr);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_CODES[$country_id];
}
}
return false;
}
}
if (!function_exists('geoip_country_name_by_addr_v6')){
function geoip_country_name_by_addr_v6($gi, $addr) {
$country_id = geoip_country_id_by_addr_v6($gi,$addr);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_NAMES[$country_id];
}
return false;
}
}
if (!function_exists('geoip_country_name_by_addr')){
function geoip_country_name_by_addr($gi, $addr) {
if ($gi->databaseType == GEOIP_CITY_EDITION_REV1) {
$record = geoip_record_by_addr($gi,$addr);
return $record->country_name;
} else {
$country_id = geoip_country_id_by_addr($gi,$addr);
if ($country_id !== false) {
return $gi->GEOIP_COUNTRY_NAMES[$country_id];
}
}
return false;
}
}
if (!function_exists('_geoip_seek_country_v6')){
function _geoip_seek_country_v6($gi, $ipnum) {
# arrays from unpack start with offset 1
# yet another php mystery. array_merge work around
# this broken behaviour
$v6vec = array_merge(unpack( "C16", $ipnum));
$offset = 0;
for ($depth = 127; $depth >= 0; --$depth) {
if ($gi->flags & GEOIP_MEMORY_CACHE) {
// workaround php's broken substr, strpos, etc handling with
// mbstring.func_overload and mbstring.internal_encoding
$enc = mb_internal_encoding();
mb_internal_encoding('ISO-8859-1');
$buf = substr($gi->memory_buffer,
2 * $gi->record_length * $offset,
2 * $gi->record_length);
mb_internal_encoding($enc);
} elseif ($gi->flags & GEOIP_SHARED_MEMORY) {
$buf = @shmop_read ($gi->shmid,
2 * $gi->record_length * $offset,
2 * $gi->record_length );
} else {
fseek($gi->filehandle, 2 * $gi->record_length * $offset, SEEK_SET) == 0
or die("fseek failed");
$buf = fread($gi->filehandle, 2 * $gi->record_length);
}
$x = array(0,0);
for ($i = 0; $i < 2; ++$i) {
for ($j = 0; $j < $gi->record_length; ++$j) {
$x[$i] += ord($buf[$gi->record_length * $i + $j]) << ($j * 8);
}
}
$bnum = 127 - $depth;
$idx = $bnum >> 3;
$b_mask = 1 << ( $bnum & 7 ^ 7 );
if (($v6vec[$idx] & $b_mask) > 0) {
if ($x[1] >= $gi->databaseSegments) {
return $x[1];
}
$offset = $x[1];
} else {
if ($x[0] >= $gi->databaseSegments) {
return $x[0];
}
$offset = $x[0];
}
}
trigger_error("error traversing database - perhaps it is corrupt?", E_USER_ERROR);
return false;
}
}
if (!function_exists('_geoip_seek_country')){
function _geoip_seek_country($gi, $ipnum) {
$offset = 0;
for ($depth = 31; $depth >= 0; --$depth) {
if ($gi->flags & GEOIP_MEMORY_CACHE) {
// workaround php's broken substr, strpos, etc handling with
// mbstring.func_overload and mbstring.internal_encoding
$enc = mb_internal_encoding();
mb_internal_encoding('ISO-8859-1');
$buf = substr($gi->memory_buffer,
2 * $gi->record_length * $offset,
2 * $gi->record_length);
mb_internal_encoding($enc);
} elseif ($gi->flags & GEOIP_SHARED_MEMORY) {
$buf = @shmop_read ($gi->shmid,
2 * $gi->record_length * $offset,
2 * $gi->record_length );
} else {
fseek($gi->filehandle, 2 * $gi->record_length * $offset, SEEK_SET) == 0
or die("fseek failed");
$buf = fread($gi->filehandle, 2 * $gi->record_length);
}
$x = array(0,0);
for ($i = 0; $i < 2; ++$i) {
for ($j = 0; $j < $gi->record_length; ++$j) {
$x[$i] += ord($buf[$gi->record_length * $i + $j]) << ($j * 8);
}
}
if ($ipnum & (1 << $depth)) {
if ($x[1] >= $gi->databaseSegments) {
return $x[1];
}
$offset = $x[1];
} else {
if ($x[0] >= $gi->databaseSegments) {
return $x[0];
}
$offset = $x[0];
}
}
trigger_error("error traversing database - perhaps it is corrupt?", E_USER_ERROR);
return false;
}
}
if (!function_exists('_common_get_org')){
function _common_get_org($gi, $seek_org){
$record_pointer = $seek_org + (2 * $gi->record_length - 1) * $gi->databaseSegments;
if ($gi->flags & GEOIP_SHARED_MEMORY) {
$org_buf = @shmop_read ($gi->shmid, $record_pointer, MAX_ORG_RECORD_LENGTH);
} else {
fseek($gi->filehandle, $record_pointer, SEEK_SET);
$org_buf = fread($gi->filehandle,MAX_ORG_RECORD_LENGTH);
}
// workaround php's broken substr, strpos, etc handling with
// mbstring.func_overload and mbstring.internal_encoding
$enc = mb_internal_encoding();
mb_internal_encoding('ISO-8859-1');
$org_buf = substr($org_buf, 0, strpos($org_buf, "\0"));
mb_internal_encoding($enc);
return $org_buf;
}
}
if (!function_exists('_get_org_v6')){
function _get_org_v6($gi,$ipnum){
$seek_org = _geoip_seek_country_v6($gi,$ipnum);
if ($seek_org == $gi->databaseSegments) {
return NULL;
}
return _common_get_org($gi, $seek_org);
}
}
if (!function_exists('_get_org')){
function _get_org($gi,$ipnum){
$seek_org = _geoip_seek_country($gi,$ipnum);
if ($seek_org == $gi->databaseSegments) {
return NULL;
}
return _common_get_org($gi, $seek_org);
}
}
if (!function_exists('geoip_name_by_addr_v6 ')){
function geoip_name_by_addr_v6 ($gi,$addr) {
if ($addr == NULL) {
return 0;
}
$ipnum = inet_pton($addr);
return _get_org_v6($gi, $ipnum);
}
}
if (!function_exists('geoip_name_by_addr ')){
function geoip_name_by_addr ($gi,$addr) {
if ($addr == NULL) {
return 0;
}
$ipnum = ip2long($addr);
return _get_org($gi, $ipnum);
}
}
if (!function_exists('geoip_org_by_addr ')){
function geoip_org_by_addr ($gi,$addr) {
return geoip_name_by_addr($gi, $addr);
}
}
if (!function_exists('_get_region')){
function _get_region($gi,$ipnum){
if ($gi->databaseType == GEOIP_REGION_EDITION_REV0){
$seek_region = _geoip_seek_country($gi,$ipnum) - GEOIP_STATE_BEGIN_REV0;
if ($seek_region >= 1000){
$country_code = "US";
$region = chr(($seek_region - 1000)/26 + 65) . chr(($seek_region - 1000)%26 + 65);
} else {
$country_code = $gi->GEOIP_COUNTRY_CODES[$seek_region];
$region = "";
}
return array ($country_code,$region);
} else if ($gi->databaseType == GEOIP_REGION_EDITION_REV1) {
$seek_region = _geoip_seek_country($gi,$ipnum) - GEOIP_STATE_BEGIN_REV1;
//print $seek_region;
if ($seek_region < US_OFFSET){
$country_code = "";
$region = "";
} else if ($seek_region < CANADA_OFFSET) {
$country_code = "US";
$region = chr(($seek_region - US_OFFSET)/26 + 65) . chr(($seek_region - US_OFFSET)%26 + 65);
} else if ($seek_region < WORLD_OFFSET) {
$country_code = "CA";
$region = chr(($seek_region - CANADA_OFFSET)/26 + 65) . chr(($seek_region - CANADA_OFFSET)%26 + 65);
} else {
$country_code = $gi->GEOIP_COUNTRY_CODES[($seek_region - WORLD_OFFSET) / FIPS_RANGE];
$region = "";
}
return array ($country_code,$region);
}
}
}
if (!function_exists('geoip_region_by_addr ')){
function geoip_region_by_addr ($gi,$addr) {
if ($addr == NULL) {
return 0;
}
$ipnum = ip2long($addr);
return _get_region($gi, $ipnum);
}
}
if (!function_exists('getdnsattributes ')){
function getdnsattributes ($l,$ip){
$r = new Net_DNS_Resolver();
$r->nameservers = array("ws1.maxmind.com");
$p = $r->search($l."." . $ip .".s.maxmind.com","TXT","IN");
$str = is_object($p->answer[0])?$p->answer[0]->string():'';
$str = substr( $str, 1, -1 );
return $str;
}
}
?>

View File

@@ -0,0 +1,352 @@
CHANGELOG
=========
3.1.0 (2024-11-15)
------------------
* This library no longer uses implicitly nullable parameter types. This
will fix deprecation warning in PHP 8.4. Reported by Steven Lewis.
GitHub #230.
* The PHPDoc type hints have been improved for use with PHPStan.
3.0.0 (2023-12-04)
------------------
* IMPORTANT: PHP 8.1 or greater is now required.
* BREAKING: Read-only properties are now used for the model and record
classes rather than magic methods. This significantly improves performance.
* BREAKING: The `raw` property on model classess and the `record` property on
record classes have been removed.
* BREAKING: On `GeoIp2\Record\Traits`, the deprecated `isAnonymousProxy` and
`isSatelliteProvider` properties have been removed.
* BREAKING: The `jsonSerialize` output has changed.
* `GeoIp2\WebService\Client` methods now throw an `InvalidArgumentException`
if an invalid IP address is passed to them. Previously, they would make
a request to the web service and throw a
`GeoIp2\Exception\InvalidRequestException`.
* The `isAnycast` property was added to `GeoIp2\Record\Traits`. This returns
`true` if the IP address belongs to an [anycast
network](https://en.wikipedia.org/wiki/Anycast). This is available for the
GeoIP2 Country, City Plus, and Insights web services and the GeoIP2 Country,
City, and Enterprise databases.
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)
-------------------
* PHP 5.6 or greater is now required.
* The `network` property was added to `GeoIp2\Record\Traits`,
`GeoIp2\Model\AnonymousIp`, `GeoIp2\Model\Asn`,
`GeoIp2\Model\ConnectionType`, `Geoip2\Model\Domain`,
and `GeoIp2\Model\Isp`. This is a string in CIDR format representing the
largest network where all of the properties besides `ipAddress` have the
same value.
* Updated documentation of anonymizer properties - `isAnonymousVpn`
and `isHostingProvider` - to be more descriptive.
* The `userCount` property was added to `GeoIp2\Record\Traits`. This is an
integer which indicates the estimated number of users sharing the
IP/network during the past 24 hours. This output is available from GeoIP2
Precision Insights.
* The `staticIpScore` property was added to `GeoIp2\Record\Traits`. This is
a float which indicates how static or dynamic an IP address is. This
output is available from GeoIP2 Precision Insights.
2.9.0 (2018-04-10)
------------------
* Refer to account IDs using the terminology "account" rather than "user".
2.8.0 (2018-01-18)
------------------
* The `isInEuropeanUnion` property was added to `GeoIp2\Record\Country`
and `GeoIp2\Record\RepresentedCountry`. This property is `true` if the
country is a member state of the European Union.
2.7.0 (2017-10-27)
------------------
* The following new anonymizer properties were added to `GeoIp2\Record\Traits`
for use with GeoIP2 Precision Insights: `isAnonymous`, `isAnonymousVpn`,
`isHostingProvider`, `isPublicProxy`, and `isTorExitNode`.
2.6.0 (2017-07-10)
-----------------
* Code clean-up and tidying.
* Set minimum required PHP version to 5.4 in `composer.json`. Previously,
5.3 would work but was not tested. Now 5.4 is hard minimum version.
2.5.0 (2017-05-08)
------------------
* Support for PHP 5.3 was dropped.
* Added support for GeoLite2 ASN database.
2.4.5 (2017-01-31)
------------------
* Additional error checking on the data returned from `MaxMind\Db\Reader`
was added to help detect corrupt databases. GitHub #83.
2.4.4 (2016-10-11)
------------------
* `isset()` on `mostSpecificSubdivision` attribute now returns the
correct value. Reported by Juan Francisco Giordana. GitHub #81.
2.4.3 (2016-10-11)
------------------
* `isset()` on `name` attribute now returns the correct value. Reported by
Juan Francisco Giordana. GitHub #79.
2.4.2 (2016-08-17)
------------------
* Updated documentation to clarify what the accuracy radius refers to.
* Upgraded `maxmind/web-service-common` to 0.3.0. This version uses
`composer/ca-bundle` rather than our own CA bundle. GitHub #75.
* Improved PHP documentation generation.
2.4.1 (2016-06-10)
------------------
* Corrected type annotations in documentation. GitHub #66.
* Updated documentation to reflect that the accuracy radius is now included
in City.
* Upgraded web service client, which supports setting a proxy. GitHub #59.
2.4.0 (2016-04-15)
------------------
* Added support for the GeoIP2 Enterprise database.
2.3.3 (2015-09-24)
------------------
* Corrected case on `JsonSerializable` interface. Reported by Axel Etcheverry.
GitHub #56.
2.3.2 (2015-09-23)
------------------
* `JsonSerializable` compatibility interface was moved to `GeoIp2\Compat`
rather than the global namespace to prevent autoloading issues. Reported by
Tomas Buteler. GitHub #54.
* Missing documentation for the `$postal` property was added to the
`GeoIp2\Model\City` class. Fix by Roy Sindre Norangshol. GitHub #51.
* In the Phar distribution, source files for this module no longer have their
documentation stripped, allowing IDE introspection to work properly.
Reported by Dominic Black. GitHub #52.
2.3.1 (2015-06-30)
------------------
* Updated `maxmind/web-service-common` to version with fixes for PHP 5.3 and
5.4.
2.3.0 (2015-06-29)
------------------
* Support for demographics fields `averageIncome` and `populationDensity` in
the `Location` record, returned by the Insights endpoint.
* The `isAnonymousProxy` and `isSatelliteProvider` properties on
`GeoIP2\Record\Traits` have been deprecated. Please use our [GeoIP2
Anonymous IP database](https://www.maxmind.com/en/geoip2-anonymous-ip-database)
to determine whether an IP address is used by an anonymizing service.
2.2.0-beta1 (2015-06-09)
------------------------
* Typo fix in documentation.
2.2.0-alpha2 (2015-06-01)
-------------------------
* `maxmind-ws/web-service-common` was renamed to `maxmind/web-service-common`.
2.2.0-alpha1 (2015-05-22)
-------------------------
* The library no longer uses Guzzle and instead uses curl directly.
* Support for `timeout` and `connectTimout` were added to the `$options` array
passed to the `GeoIp2\WebService\Client` constructor. Pull request by Will
Bradley. GitHub #36.
2.1.1 (2014-12-03)
------------------
* The 2.1.0 Phar builds included a shebang line, causing issues when loading
it as a library. This has been corrected. GitHub #33.
2.1.0 (2014-10-29)
------------------
* Update ApiGen dependency to version that isn't broken on case sensitive
file systems.
* Added support for the GeoIP2 Anonymous IP database. The
`GeoIP2\Database\Reader` class now has an `anonymousIp` method which returns
a `GeoIP2\Model\AnonymousIp` object.
* Boolean attributes like those in the `GeoIP2\Record\Traits` class now return
`false` instead of `null` when they were not true.
2.0.0 (2014-09-22)
------------------
* First production release.
0.9.0 (2014-09-15)
------------------
* IMPORTANT: The deprecated `omni()` and `cityIspOrg()` methods have been
removed from `GeoIp2\WebService\Client`.
0.8.1 (2014-09-12)
------------------
* The check added to the `GeoIP2\Database\Reader` lookup methods in 0.8.0 did
not work with the GeoIP2 City Database Subset by Continent with World
Countries. This has been fixed. Fixes GitHub issue #23.
0.8.0 (2014-09-10)
------------------
* The `GeoIp2\Database\Reader` lookup methods (e.g., `city()`, `isp()`) now
throw a `BadMethodCallException` if they are used with a database that
does not match the method. In particular, doing a `city()` lookup on a
GeoIP2 Country database will result in an exception, and vice versa.
* A `metadata()` method has been added to the `GeoIP2\Database\Reader` class.
This returns a `MaxMind\Db\Reader\Metadata` class with information about the
database.
* The name attribute was missing from the RepresentedCountry class.
0.7.0 (2014-07-22)
------------------
* The web service client API has been updated for the v2.1 release of the web
service. In particular, the `cityIspOrg` and `omni` methods on
`GeoIp2\WebService\Client` should be considered deprecated. The `city`
method now provides all of the data formerly provided by `cityIspOrg`, and
the `omni` method has been replaced by the `insights` method.
* Support was added for GeoIP2 Connection Type, Domain and ISP databases.
0.6.3 (2014-05-12)
------------------
* With the previous Phar builds, some users received `phar error: invalid url
or non-existent phar` errors. The correct alias is now used for the Phar,
and this should no longer be an issue.
0.6.2 (2014-05-08)
------------------
* The Phar build was broken with Guzzle 3.9.0+. This has been fixed.
0.6.1 (2014-05-01)
------------------
* This API now officially supports HHVM.
* The `maxmind-db/reader` dependency was updated to a version that does not
require BC Math.
* The Composer compatibility autoload rules are now targeted more narrowly.
* A `box.json` file is included to build a Phar package.
0.6.0 (2014-02-19)
------------------
* This API is now licensed under the Apache License, Version 2.0.
* Model and record classes now implement `JsonSerializable`.
* `isset` now works with model and record classes.
0.5.0 (2013-10-21)
------------------
* Renamed $languages constructor parameters to $locales for both the Client
and Reader classes.
* Documentation and code clean-up (Ben Morel).
* Added the interface `GeoIp2\ProviderInterface`, which is implemented by both
`\GeoIp2\Database\Reader` and `\GeoIp2\WebService\Client`.
0.4.0 (2013-07-16)
------------------
* This is the first release with the GeoIP2 database reader. Please see the
`README.md` file and the `\GeoIp2\Database\Reader` class.
* The general exception classes were replaced with specific exception classes
representing particular types of errors, such as an authentication error.
0.3.0 (2013-07-12)
------------------
* In namespaces and class names, "GeoIP2" was renamed to "GeoIp2" to improve
consistency.
0.2.1 (2013-06-10)
------------------
* First official beta release.
* Documentation updates and corrections.
0.2.0 (2013-05-29)
------------------
* `GenericException` was renamed to `GeoIP2Exception`.
* We now support more languages. The new languages are de, es, fr, and pt-BR.
* The REST API now returns a record with data about your account. There is
a new `GeoIP\Records\MaxMind` class for this data.
* The `continentCode` attribute on `Continent` was renamed to `code`.
* Documentation updates.
0.1.1 (2013-05-14)
------------------
* Updated Guzzle version requirement.
* Fixed Composer example in README.md.
0.1.0 (2013-05-13)
------------------
* Initial release.

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,448 @@
# GeoIP2 PHP API #
## Description ##
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 ##
We recommend installing this package with [Composer](https://getcomposer.org/).
### Download Composer ###
To download Composer, run in the root directory of your project:
```bash
curl -sS https://getcomposer.org/installer | php
```
You should now have the file `composer.phar` in your project directory.
### Install Dependencies ###
Run in your project root:
```sh
php composer.phar require geoip2/geoip2:~2.0
```
You should now have the files `composer.json` and `composer.lock` as well as
the directory `vendor` in your project directory. If you use a version control
system, `composer.json` should be added to it.
### Require Autoloader ###
After installing the dependencies, you need to require the Composer autoloader
from your code:
```php
require 'vendor/autoload.php';
```
## Install via Phar ##
Although we strongly recommend using Composer, we also provide a
[phar archive](https://php.net/manual/en/book.phar.php) containing most of the
dependencies for GeoIP2. Our latest phar archive is available on
[our releases page](https://github.com/maxmind/GeoIP2-php/releases).
### Install Dependencies ###
In order to use the phar archive, you must have the PHP
[Phar extension](https://php.net/manual/en/book.phar.php) installed and
enabled.
If you will be making web service requests, you must have the PHP
[cURL extension](https://php.net/manual/en/book.curl.php)
installed to use this archive. For Debian based distributions, this can
typically be found in the the `php-curl` package. For other operating
systems, please consult the relevant documentation. After installing the
extension you may need to restart your web server.
If you are missing this extension, you will see errors like the following:
```
PHP Fatal error: Uncaught Error: Call to undefined function MaxMind\WebService\curl_version()
```
### Require Package ###
To use the archive, just require it from your script:
```php
require 'geoip2.phar';
```
## Optional C Extension ##
The [MaxMind DB API](https://github.com/maxmind/MaxMind-DB-Reader-php)
includes an optional C extension that you may install to dramatically increase
the performance of lookups in GeoIP2 or GeoLite2 databases. To install, please
follow the instructions included with that API.
The extension has no effect on web-service lookups.
## IP Geolocation Usage ##
IP geolocation is inherently imprecise. Locations are often near the center of
the population. Any location provided by a GeoIP2 database or web service
should not be used to identify a particular address or household.
## Database Reader ##
### Usage ###
To use this API, you must create a new `\GeoIp2\Database\Reader` object with
the path to the database file as the first argument to the constructor. You
may then call the method corresponding to the database you are using.
If the lookup succeeds, the method call will return a model class for the
record in the database. This model in turn contains multiple container
classes for the different parts of the data such as the city in which the
IP address is located.
If the record is not found, a `\GeoIp2\Exception\AddressNotFoundException`
is thrown. If the database is invalid or corrupt, a
`\MaxMind\Db\InvalidDatabaseException` will be thrown.
See the [API documentation](https://maxmind.github.io/GeoIP2-php/) for more
details.
### City Example ###
```php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
// This creates the Reader object, which should be reused across
// lookups.
$cityDbReader = new Reader('/usr/local/share/GeoIP/GeoIP2-City.mmdb');
// Replace "city" with the appropriate method for your database, e.g.,
// "country".
$record = $cityDbReader->city('128.101.101.101');
print($record->country->isoCode . "\n"); // 'US'
print($record->country->name . "\n"); // 'United States'
print($record->country->names['zh-CN'] . "\n"); // '美国'
print($record->mostSpecificSubdivision->name . "\n"); // 'Minnesota'
print($record->mostSpecificSubdivision->isoCode . "\n"); // 'MN'
print($record->city->name . "\n"); // 'Minneapolis'
print($record->postal->code . "\n"); // '55455'
print($record->location->latitude . "\n"); // 44.9733
print($record->location->longitude . "\n"); // -93.2323
print($record->traits->network . "\n"); // '128.101.101.101/32'
```
### Anonymous IP Example ###
```php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
// This creates the Reader object, which should be reused across
// lookups.
$anonymousDbReader = new Reader('/usr/local/share/GeoIP/GeoIP2-Anonymous-IP.mmdb');
$record = $anonymousDbReader->anonymousIp('128.101.101.101');
if ($record->isAnonymous) { print "anon\n"; }
print($record->ipAddress . "\n"); // '128.101.101.101'
print($record->network . "\n"); // '128.101.101.101/32'
```
### Connection-Type Example ###
```php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
// This creates the Reader object, which should be reused across
// lookups.
$connectionTypeDbReader = new Reader('/usr/local/share/GeoIP/GeoIP2-Connection-Type.mmdb');
$record = $connectionTypeDbReader->connectionType('128.101.101.101');
print($record->connectionType . "\n"); // 'Corporate'
print($record->ipAddress . "\n"); // '128.101.101.101'
print($record->network . "\n"); // '128.101.101.101/32'
```
### Domain Example ###
```php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
// This creates the Reader object, which should be reused across
// lookups.
$domainDbReader = new Reader('/usr/local/share/GeoIP/GeoIP2-Domain.mmdb');
$record = $domainDbReader->domain('128.101.101.101');
print($record->domain . "\n"); // 'umn.edu'
print($record->ipAddress . "\n"); // '128.101.101.101'
print($record->network . "\n"); // '128.101.101.101/32'
```
### Enterprise Example ###
```php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
// This creates the Reader object, which should be reused across
// lookups.
$enterpriseDbReader = new Reader('/usr/local/share/GeoIP/GeoIP2-Enterprise.mmdb');
// Use the ->enterprise method to do a lookup in the Enterprise database
$record = $enterpriseDbReader->enterprise('128.101.101.101');
print($record->country->confidence . "\n"); // 99
print($record->country->isoCode . "\n"); // 'US'
print($record->country->name . "\n"); // 'United States'
print($record->country->names['zh-CN'] . "\n"); // '美国'
print($record->mostSpecificSubdivision->confidence . "\n"); // 77
print($record->mostSpecificSubdivision->name . "\n"); // 'Minnesota'
print($record->mostSpecificSubdivision->isoCode . "\n"); // 'MN'
print($record->city->confidence . "\n"); // 60
print($record->city->name . "\n"); // 'Minneapolis'
print($record->postal->code . "\n"); // '55455'
print($record->location->accuracyRadius . "\n"); // 50
print($record->location->latitude . "\n"); // 44.9733
print($record->location->longitude . "\n"); // -93.2323
print($record->traits->network . "\n"); // '128.101.101.101/32'
```
### ISP Example ###
```php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
// This creates the Reader object, which should be reused across
// lookups.
$ispDbReader = new Reader('/usr/local/share/GeoIP/GeoIP2-ISP.mmdb');
$record = $ispDbReader->isp('128.101.101.101');
print($record->autonomousSystemNumber . "\n"); // 217
print($record->autonomousSystemOrganization . "\n"); // 'University of Minnesota'
print($record->isp . "\n"); // 'University of Minnesota'
print($record->organization . "\n"); // 'University of Minnesota'
print($record->ipAddress . "\n"); // '128.101.101.101'
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)
## Web Service Client ##
### Usage ###
To use this API, you must create a new `\GeoIp2\WebService\Client`
object with your `$accountId` and `$licenseKey`:
```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']);
```
To call the Sandbox GeoIP2 web service instead of the production GeoIP2 web
service:
```php
$client = new Client(42, 'abcdef123456', ['en'], ['host' => 'sandbox.maxmind.com']);
```
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.
See the [API documentation](https://maxmind.github.io/GeoIP2-php/) for more
details.
### Example ###
```php
<?php
require_once 'vendor/autoload.php';
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. Set the "host" to "geolite.info" in the fourth argument options
// array to use the GeoLite2 web service instead of the GeoIP2 web
// service. Set the "host" to "sandbox.maxmind.com" in the fourth argument
// options array to use the Sandbox GeoIP2 web service instead of the
// production GeoIP2 web service.
$client = new Client(42, 'abcdef123456');
// Replace "city" with the method corresponding to the web service that
// you are using, e.g., "country", "insights".
$record = $client->city('128.101.101.101');
print($record->country->isoCode . "\n"); // 'US'
print($record->country->name . "\n"); // 'United States'
print($record->country->names['zh-CN'] . "\n"); // '美国'
print($record->mostSpecificSubdivision->name . "\n"); // 'Minnesota'
print($record->mostSpecificSubdivision->isoCode . "\n"); // 'MN'
print($record->city->name . "\n"); // 'Minneapolis'
print($record->postal->code . "\n"); // '55455'
print($record->location->latitude . "\n"); // 44.9733
print($record->location->longitude . "\n"); // -93.2323
print($record->traits->network . "\n"); // '128.101.101.101/32'
```
## Values to use for Database or Array Keys ##
**We strongly discourage you from using a value from any `names` property as
a key in a database or array.**
These names may change between releases. Instead we recommend using one of the
following:
* `GeoIp2\Record\City` - `$city->geonameId`
* `GeoIp2\Record\Continent` - `$continent->code` or `$continent->geonameId`
* `GeoIp2\Record\Country` and `GeoIp2\Record\RepresentedCountry` -
`$country->isoCode` or `$country->geonameId`
* `GeoIp2\Record\Subdivision` - `$subdivision->isoCode` or `$subdivision->geonameId`
### What data is returned? ###
While many of the end points return the same basic records, the attributes
which can be populated vary between end points. In addition, while an end
point may offer a particular piece of data, MaxMind does not always have every
piece of data for any given IP address.
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 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`
attribute in the `GeoIp2\Record\Traits` record.
## Integration with GeoNames ##
[GeoNames](https://www.geonames.org/) offers web services and downloadable
databases with data on geographical features around the world, including
populated places. They offer both free and paid premium data. Each
feature is unique identified by a `geonameId`, which is an integer.
Many of the records returned by the GeoIP2 web services and databases
include a `geonameId` property. This is the ID of a geographical feature
(city, region, country, etc.) in the GeoNames database.
Some of the data that MaxMind provides is also sourced from GeoNames. We
source things like place names, ISO codes, and other similar data from
the GeoNames premium data set.
## Reporting data problems ##
If the problem you find is that an IP address is incorrectly mapped,
please
[submit your correction to MaxMind](https://www.maxmind.com/en/correction).
If you find some other sort of mistake, like an incorrect spelling,
please check the [GeoNames site](https://www.geonames.org/) first. Once
you've searched for a place and found it on the GeoNames map view, there
are a number of links you can use to correct data ("move", "edit",
"alternate names", etc.). Once the correction is part of the GeoNames
data set, it will be automatically incorporated into future MaxMind
releases.
If you are a paying MaxMind customer and you're not sure where to submit
a correction, please
[contact MaxMind support](https://www.maxmind.com/en/support) for help.
## Other Support ##
Please report all issues with this code using the
[GitHub issue tracker](https://github.com/maxmind/GeoIP2-php/issues).
If you are having an issue with a MaxMind service that is not specific
to the client API, please see
[our support page](https://www.maxmind.com/en/support).
## Requirements ##
This library requires PHP 8.1 or greater.
This library also relies on the [MaxMind DB Reader](https://github.com/maxmind/MaxMind-DB-Reader-php).
## Contributing ##
Patches and pull requests are encouraged. All code should follow the PSR-2
style guidelines. Please include unit tests whenever possible. You may obtain
the test data for the maxmind-db folder by running `git submodule update
--init --recursive` or adding `--recursive` to your initial clone, or from
https://github.com/maxmind/MaxMind-DB
## Versioning ##
The GeoIP2 PHP API uses [Semantic Versioning](https://semver.org/).
## Copyright and License ##
This software is Copyright (c) 2013-2024 by MaxMind, Inc.
This is free software, licensed under the Apache License, Version 2.0.

View File

@@ -0,0 +1,32 @@
{
"name": "geoip2/geoip2",
"description": "MaxMind GeoIP2 PHP API",
"keywords": ["geoip", "geoip2", "geolocation", "ip", "maxmind"],
"homepage": "https://github.com/maxmind/GeoIP2-php",
"type": "library",
"license": "Apache-2.0",
"authors": [
{
"name": "Gregory J. Oschwald",
"email": "goschwald@maxmind.com",
"homepage": "https://www.maxmind.com/"
}
],
"require": {
"maxmind-db/reader": "^1.12.0",
"maxmind/web-service-common": "~0.10",
"php": ">=8.1",
"ext-json": "*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "3.*",
"phpunit/phpunit": "^10.0",
"squizlabs/php_codesniffer": "3.*",
"phpstan/phpstan": "*"
},
"autoload": {
"psr-4": {
"GeoIp2\\": "src"
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;
srand(0);
$reader = new Reader('GeoIP2-City.mmdb');
$count = 500000;
$startTime = microtime(true);
for ($i = 0; $i < $count; ++$i) {
$ip = long2ip(rand(0, 2 ** 32 - 1));
try {
$t = $reader->city($ip);
} catch (AddressNotFoundException $e) {
}
if ($i % 10000 === 0) {
echo $i . ' ' . $ip . "\n";
}
}
$endTime = microtime(true);
$duration = $endTime - $startTime;
echo 'Requests per second: ' . $count / $duration . "\n";

View File

@@ -0,0 +1,271 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Database;
use GeoIp2\Exception\AddressNotFoundException;
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;
use MaxMind\Db\Reader\Metadata;
/**
* Instances of this class provide a reader for the GeoIP2 database format.
* IP addresses can be looked up using the database specific methods.
*
* ## Usage ##
*
* The basic API for this class is the same for every database. First, you
* create a reader object, specifying a file name. You then call the method
* corresponding to the specific database, passing it the IP address you want
* to look up.
*
* If the request succeeds, the method call will return a model class for
* the method you called. This model in turn contains multiple record classes,
* each of which represents part of the data returned by the database. If
* the database does not contain the requested information, the attributes
* on the record class will have a `null` value.
*
* If the address is not in the database, an
* {@link \GeoIp2\Exception\AddressNotFoundException} exception will be
* thrown. If an invalid IP address is passed to one of the methods, a
* SPL {@link \InvalidArgumentException} will be thrown. If the database is
* corrupt or invalid, a {@link \MaxMind\Db\Reader\InvalidDatabaseException}
* will be thrown.
*/
class Reader implements ProviderInterface
{
private DbReader $dbReader;
private string $dbType;
/**
* @var array<string>
*/
private array $locales;
/**
* Constructor.
*
* @param string $filename the path to the GeoIP2 database file
* @param array<string> $locales list of locale codes to use in name property
* from most preferred to least preferred
*
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function __construct(
string $filename,
array $locales = ['en']
) {
$this->dbReader = new DbReader($filename);
$this->dbType = $this->dbReader->metadata()->databaseType;
$this->locales = $locales;
}
/**
* This method returns a GeoIP2 City model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function city(string $ipAddress): City
{
return $this->modelFor(City::class, 'City', $ipAddress);
}
/**
* This method returns a GeoIP2 Country model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function country(string $ipAddress): Country
{
return $this->modelFor(Country::class, 'Country', $ipAddress);
}
/**
* This method returns a GeoIP2 Anonymous IP model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function anonymousIp(string $ipAddress): AnonymousIp
{
return $this->flatModelFor(
AnonymousIp::class,
'GeoIP2-Anonymous-IP',
$ipAddress
);
}
/**
* This method returns a GeoLite2 ASN model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function asn(string $ipAddress): Asn
{
return $this->flatModelFor(
Asn::class,
'GeoLite2-ASN',
$ipAddress
);
}
/**
* This method returns a GeoIP2 Connection Type model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function connectionType(string $ipAddress): ConnectionType
{
return $this->flatModelFor(
ConnectionType::class,
'GeoIP2-Connection-Type',
$ipAddress
);
}
/**
* This method returns a GeoIP2 Domain model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function domain(string $ipAddress): Domain
{
return $this->flatModelFor(
Domain::class,
'GeoIP2-Domain',
$ipAddress
);
}
/**
* This method returns a GeoIP2 Enterprise model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function enterprise(string $ipAddress): Enterprise
{
return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress);
}
/**
* This method returns a GeoIP2 ISP model.
*
* @param string $ipAddress an IPv4 or IPv6 address as a string
*
* @throws AddressNotFoundException if the address is not in the database
* @throws InvalidDatabaseException if the database is corrupt or invalid
*/
public function isp(string $ipAddress): Isp
{
return $this->flatModelFor(
Isp::class,
'GeoIP2-ISP',
$ipAddress
);
}
private function modelFor(string $class, string $type, string $ipAddress): object
{
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
$record['traits']['ip_address'] = $ipAddress;
$record['traits']['prefix_len'] = $prefixLen;
return new $class($record, $this->locales);
}
private function flatModelFor(string $class, string $type, string $ipAddress): object
{
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
$record['ip_address'] = $ipAddress;
$record['prefix_len'] = $prefixLen;
return new $class($record);
}
/**
* @return array{0:array<string, mixed>, 1:int}
*/
private function getRecord(string $class, string $type, string $ipAddress): array
{
if (!str_contains($this->dbType, $type)) {
$method = lcfirst((new \ReflectionClass($class))->getShortName());
throw new \BadMethodCallException(
"The $method method cannot be used to open a {$this->dbType} database"
);
}
[$record, $prefixLen] = $this->dbReader->getWithPrefixLen($ipAddress);
if ($record === null) {
throw new AddressNotFoundException(
"The address $ipAddress is not in the database."
);
}
if (!\is_array($record)) {
// This can happen on corrupt databases. Generally,
// MaxMind\Db\Reader will throw a
// MaxMind\Db\Reader\InvalidDatabaseException, but occasionally
// the lookup may result in a record that looks valid but is not
// an array. This mostly happens when the user is ignoring all
// exceptions and the more frequent InvalidDatabaseException
// exceptions go unnoticed.
throw new InvalidDatabaseException(
"Expected an array when looking up $ipAddress but received: "
. \gettype($record)
);
}
return [$record, $prefixLen];
}
/**
* @throws \InvalidArgumentException if arguments are passed to the method
* @throws \BadMethodCallException if the database has been closed
*
* @return Metadata object for the database
*/
public function metadata(): Metadata
{
return $this->dbReader->metadata();
}
/**
* Closes the GeoIP2 database and returns the resources to the system.
*/
public function close(): void
{
$this->dbReader->close();
}
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
* This class represents a generic error.
*/
// phpcs:disable
class AddressNotFoundException extends GeoIp2Exception {}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
* This class represents a generic error.
*/
// phpcs:disable
class AuthenticationException extends GeoIp2Exception {}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
* This class represents a generic error.
*/
// phpcs:disable
class GeoIp2Exception extends \Exception {}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
* This class represents an HTTP transport error.
*/
class HttpException extends GeoIp2Exception
{
/**
* The URI queried.
*/
public string $uri;
public function __construct(
string $message,
int $httpStatus,
string $uri,
?\Exception $previous = null
) {
$this->uri = $uri;
parent::__construct($message, $httpStatus, $previous);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
* This class represents an error returned by MaxMind's GeoIP2
* web service.
*/
class InvalidRequestException extends HttpException
{
/**
* The code returned by the MaxMind web service.
*/
public string $error;
public function __construct(
string $message,
string $error,
int $httpStatus,
string $uri,
?\Exception $previous = null
) {
$this->error = $error;
parent::__construct($message, $httpStatus, $uri, $previous);
}
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Exception;
/**
* This class represents a generic error.
*/
// phpcs:disable
class OutOfQueriesException extends GeoIp2Exception {}

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
/**
* This class provides the GeoIP2 Anonymous IP model.
*/
class AnonymousIp implements \JsonSerializable
{
/**
* @var bool this is true if the IP address belongs to
* any sort of anonymous network
*/
public readonly bool $isAnonymous;
/**
* @var bool This is true if the IP address is
* 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.
*/
public readonly bool $isAnonymousVpn;
/**
* @var bool this is true if the IP address belongs
* to a hosting or VPN provider (see description of isAnonymousVpn property)
*/
public readonly bool $isHostingProvider;
/**
* @var bool this is true if the IP address belongs to
* a public proxy
*/
public readonly bool $isPublicProxy;
/**
* @var bool this is true if the IP address is
* on a suspected anonymizing network and belongs to a residential ISP
*/
public readonly bool $isResidentialProxy;
/**
* @var bool this is true if the IP address is a Tor
* exit node
*/
public readonly bool $isTorExitNode;
/**
* @var string the IP address that the data in the model is
* for
*/
public readonly string $ipAddress;
/**
* @var string 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.
*/
public readonly string $network;
/**
* @ignore
*
* @param array<string, mixed> $raw
*/
public function __construct(array $raw)
{
$this->isAnonymous = $raw['is_anonymous'] ?? false;
$this->isAnonymousVpn = $raw['is_anonymous_vpn'] ?? false;
$this->isHostingProvider = $raw['is_hosting_provider'] ?? false;
$this->isPublicProxy = $raw['is_public_proxy'] ?? false;
$this->isResidentialProxy = $raw['is_residential_proxy'] ?? false;
$this->isTorExitNode = $raw['is_tor_exit_node'] ?? false;
$ipAddress = $raw['ip_address'];
$this->ipAddress = $ipAddress;
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = [];
$js['is_anonymous'] = $this->isAnonymous;
$js['is_anonymous_vpn'] = $this->isAnonymousVpn;
$js['is_hosting_provider'] = $this->isHostingProvider;
$js['is_public_proxy'] = $this->isPublicProxy;
$js['is_residential_proxy'] = $this->isResidentialProxy;
$js['is_tor_exit_node'] = $this->isTorExitNode;
$js['ip_address'] = $this->ipAddress;
$js['network'] = $this->network;
return $js;
}
}

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
/**
* This class provides the GeoLite2 ASN model.
*/
class Asn implements \JsonSerializable
{
/**
* @var int|null the autonomous system number
* associated with the IP address
*/
public readonly ?int $autonomousSystemNumber;
/**
* @var string|null the organization
* associated with the registered autonomous system number for the IP
* address
*/
public readonly ?string $autonomousSystemOrganization;
/**
* @var string the IP address that the data in the model is
* for
*/
public readonly string $ipAddress;
/**
* @var string 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.
*/
public readonly string $network;
/**
* @ignore
*
* @param array<string, mixed> $raw
*/
public function __construct(array $raw)
{
$this->autonomousSystemNumber = $raw['autonomous_system_number'] ?? null;
$this->autonomousSystemOrganization =
$raw['autonomous_system_organization'] ?? null;
$ipAddress = $raw['ip_address'];
$this->ipAddress = $ipAddress;
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = [];
if ($this->autonomousSystemNumber !== null) {
$js['autonomous_system_number'] = $this->autonomousSystemNumber;
}
if ($this->autonomousSystemOrganization !== null) {
$js['autonomous_system_organization'] = $this->autonomousSystemOrganization;
}
$js['ip_address'] = $this->ipAddress;
$js['network'] = $this->network;
return $js;
}
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Record\City as CityRecord;
use GeoIp2\Record\Location;
use GeoIp2\Record\Postal;
use GeoIp2\Record\Subdivision;
/**
* Model class for the data returned by City Plus web service and City
* database.
*
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more
* details.
*/
class City extends Country
{
/**
* @var CityRecord city data for the requested IP address
*/
public readonly CityRecord $city;
/**
* @var Location location data for the requested IP address
*/
public readonly Location $location;
/**
* @var Subdivision An object representing the most specific subdivision
* returned. If the response did not contain any
* subdivisions, this method returns an empty
* \GeoIp2\Record\Subdivision object.
*/
public readonly Subdivision $mostSpecificSubdivision;
/**
* @var Postal postal data for the
* requested IP address
*/
public readonly Postal $postal;
/**
* @var array<\GeoIp2\Record\Subdivision> An array of \GeoIp2\Record\Subdivision
* objects representing the country
* subdivisions for the requested IP
* address. The number and type of
* subdivisions varies by country,
* but a subdivision is typically a
* state, province, county, etc.
* Subdivisions are ordered from most
* general (largest) to most specific
* (smallest). If the response did
* not contain any subdivisions, this
* method returns an empty array.
*/
public readonly array $subdivisions;
/**
* @ignore
*
* @param array<string, mixed> $raw
* @param list<string> $locales
*/
public function __construct(array $raw, array $locales = ['en'])
{
parent::__construct($raw, $locales);
$this->city = new CityRecord($raw['city'] ?? [], $locales);
$this->location = new Location($raw['location'] ?? []);
$this->postal = new Postal($raw['postal'] ?? []);
if (!isset($raw['subdivisions'])) {
$this->subdivisions = [];
$this->mostSpecificSubdivision =
new Subdivision([], $locales);
return;
}
$subdivisions = [];
foreach ($raw['subdivisions'] as $sub) {
$subdivisions[] =
new Subdivision($sub, $locales)
;
}
// Not using end as we don't want to modify internal pointer.
$this->mostSpecificSubdivision =
$subdivisions[\count($subdivisions) - 1];
$this->subdivisions = $subdivisions;
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = parent::jsonSerialize();
$city = $this->city->jsonSerialize();
if (!empty($city)) {
$js['city'] = $city;
}
$location = $this->location->jsonSerialize();
if (!empty($location)) {
$js['location'] = $location;
}
$postal =
$this->postal->jsonSerialize();
if (!empty($postal)) {
$js['postal'] = $postal;
}
$subdivisions = [];
foreach ($this->subdivisions as $sub) {
$subdivisions[] = $sub->jsonSerialize();
}
if (!empty($subdivisions)) {
$js['subdivisions'] = $subdivisions;
}
return $js;
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
/**
* This class provides the GeoIP2 Connection-Type model.
*/
class ConnectionType implements \JsonSerializable
{
/**
* @var string|null The connection type may take the
* following values: "Dialup", "Cable/DSL", "Corporate", "Cellular", and
* "Satellite". Additional values may be added in the future.
*/
public readonly ?string $connectionType;
/**
* @var string the IP address that the data in the model is
* for
*/
public readonly string $ipAddress;
/**
* @var string 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.
*/
public readonly string $network;
/**
* @ignore
*
* @param array<string, mixed> $raw
*/
public function __construct(array $raw)
{
$this->connectionType = $raw['connection_type'] ?? null;
$ipAddress = $raw['ip_address'];
$this->ipAddress = $ipAddress;
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = [];
if ($this->connectionType !== null) {
$js['connection_type'] = $this->connectionType;
}
$js['ip_address'] = $this->ipAddress;
$js['network'] = $this->network;
return $js;
}
}

View File

@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Record\Continent;
use GeoIp2\Record\Country as CountryRecord;
use GeoIp2\Record\MaxMind;
use GeoIp2\Record\RepresentedCountry;
use GeoIp2\Record\Traits;
/**
* Model class for the data returned by GeoIP2 Country web service and database.
*
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more details.
*/
class Country implements \JsonSerializable
{
/**
* @var Continent continent data for the requested IP address
*/
public readonly Continent $continent;
/**
* @var CountryRecord Country data for the requested IP address. This
* object represents the country where MaxMind believes
* the end user is located.
*/
public readonly CountryRecord $country;
/**
* @var MaxMind data related to your MaxMind account
*/
public readonly MaxMind $maxmind;
/**
* @var CountryRecord Registered country data for the requested IP address.
* This record represents the country where the ISP has
* registered a given IP block and may differ from the
* user's country.
*/
public readonly CountryRecord $registeredCountry;
/**
* @var RepresentedCountry Represented country data for the requested IP
* address. The represented country is used for
* things like military bases. It is only present
* when the represented country differs from the
* country.
*/
public readonly RepresentedCountry $representedCountry;
/**
* @var Traits data for the traits of the requested IP address
*/
public readonly Traits $traits;
/**
* @ignore
*
* @param array<string, mixed> $raw
* @param list<string> $locales
*/
public function __construct(array $raw, array $locales = ['en'])
{
$this->continent = new Continent(
$raw['continent'] ?? [],
$locales
);
$this->country = new CountryRecord(
$raw['country'] ?? [],
$locales
);
$this->maxmind = new MaxMind($raw['maxmind'] ?? []);
$this->registeredCountry = new CountryRecord(
$raw['registered_country'] ?? [],
$locales
);
$this->representedCountry = new RepresentedCountry(
$raw['represented_country'] ?? [],
$locales
);
$this->traits = new Traits($raw['traits'] ?? []);
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = [];
$continent = $this->continent->jsonSerialize();
if (!empty($continent)) {
$js['continent'] = $continent;
}
$country = $this->country->jsonSerialize();
if (!empty($country)) {
$js['country'] = $country;
}
$maxmind = $this->maxmind->jsonSerialize();
if (!empty($maxmind)) {
$js['maxmind'] = $maxmind;
}
$registeredCountry = $this->registeredCountry->jsonSerialize();
if (!empty($registeredCountry)) {
$js['registered_country'] = $registeredCountry;
}
$representedCountry = $this->representedCountry->jsonSerialize();
if (!empty($representedCountry)) {
$js['represented_country'] = $representedCountry;
}
$traits = $this->traits->jsonSerialize();
if (!empty($traits)) {
$js['traits'] = $traits;
}
return $js;
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
/**
* This class provides the GeoIP2 Domain model.
*/
class Domain implements \JsonSerializable
{
/**
* @var string|null The second level domain associated with the
* IP address. This will be something like "example.com" or
* "example.co.uk", not "foo.example.com".
*/
public readonly ?string $domain;
/**
* @var string the IP address that the data in the model is
* for
*/
public readonly string $ipAddress;
/**
* @var string 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.
*/
public readonly string $network;
/**
* @ignore
*
* @param array<string, mixed> $raw
*/
public function __construct(array $raw)
{
$this->domain = $raw['domain'] ?? null;
$ipAddress = $raw['ip_address'];
$this->ipAddress = $ipAddress;
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = [];
if ($this->domain !== null) {
$js['domain'] = $this->domain;
}
$js['ip_address'] = $this->ipAddress;
$js['network'] = $this->network;
return $js;
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
* Model class for the data returned by GeoIP2 Enterprise database lookups.
*
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more
* details.
*/
// phpcs:disable
class Enterprise extends City {}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
/**
* Model class for the data returned by GeoIP2 Insights web service.
*
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for
* more details.
*/
// phpcs:disable
class Insights extends City {}

View File

@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Model;
use GeoIp2\Util;
/**
* This class provides the GeoIP2 ISP model.
*/
class Isp implements \JsonSerializable
{
/**
* @var int|null the autonomous system number
* associated with the IP address
*/
public readonly ?int $autonomousSystemNumber;
/**
* @var string|null the organization
* associated with the registered autonomous system number for the IP
* address
*/
public readonly ?string $autonomousSystemOrganization;
/**
* @var string|null the name of the ISP associated with the IP
* address
*/
public readonly ?string $isp;
/**
* @var string|null The [mobile country code
* (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
* the IP address and ISP.
*/
public readonly ?string $mobileCountryCode;
/**
* @var string|null The [mobile network code
* (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
* the IP address and ISP.
*/
public readonly ?string $mobileNetworkCode;
/**
* @var string|null the name of the organization associated
* with the IP address
*/
public readonly ?string $organization;
/**
* @var string the IP address that the data in the model is
* for
*/
public readonly string $ipAddress;
/**
* @var string 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.
*/
public readonly string $network;
/**
* @ignore
*
* @param array<string, mixed> $raw
*/
public function __construct(array $raw)
{
$this->autonomousSystemNumber = $raw['autonomous_system_number'] ?? null;
$this->autonomousSystemOrganization =
$raw['autonomous_system_organization'] ?? null;
$this->isp = $raw['isp'] ?? null;
$this->mobileCountryCode = $raw['mobile_country_code'] ?? null;
$this->mobileNetworkCode = $raw['mobile_network_code'] ?? null;
$this->organization = $raw['organization'] ?? null;
$ipAddress = $raw['ip_address'];
$this->ipAddress = $ipAddress;
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
}
/**
* @return array<string, mixed>|null
*/
public function jsonSerialize(): ?array
{
$js = [];
if ($this->autonomousSystemNumber !== null) {
$js['autonomous_system_number'] = $this->autonomousSystemNumber;
}
if ($this->autonomousSystemOrganization !== null) {
$js['autonomous_system_organization'] = $this->autonomousSystemOrganization;
}
if ($this->isp !== null) {
$js['isp'] = $this->isp;
}
if ($this->mobileCountryCode !== null) {
$js['mobile_country_code'] = $this->mobileCountryCode;
}
if ($this->mobileNetworkCode !== null) {
$js['mobile_network_code'] = $this->mobileNetworkCode;
}
if ($this->organization !== null) {
$js['organization'] = $this->organization;
}
$js['ip_address'] = $this->ipAddress;
$js['network'] = $this->network;
return $js;
}
}

View File

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

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
abstract class AbstractNamedRecord implements \JsonSerializable
{
/**
* @var string|null The name based on the locales list
* passed to the constructor. This attribute is returned by all location
* services and databases.
*/
public readonly ?string $name;
/**
* @var array<string, string> An array map where the keys are locale codes
* and the values are names. This attribute is returned by all location
* services and databases.
*/
public readonly array $names;
/**
* @ignore
*
* @param array<string, mixed> $record
* @param list<string> $locales
*/
public function __construct(array $record, array $locales = ['en'])
{
$this->names = $record['names'] ?? [];
foreach ($locales as $locale) {
if (isset($this->names[$locale])) {
$this->name = $this->names[$locale];
return;
}
}
$this->name = null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = [];
if (!empty($this->names)) {
$js['names'] = $this->names;
}
return $js;
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
abstract class AbstractPlaceRecord extends AbstractNamedRecord
{
/**
* @var int|null A value from 0-100 indicating MaxMind's
* confidence that the location level is correct. This attribute is only available
* from the Insights service and the GeoIP2 Enterprise database.
*/
public readonly ?int $confidence;
/**
* @var int|null The GeoName ID for the location level. This attribute
* is returned by all location services and databases.
*/
public readonly ?int $geonameId;
/**
* @ignore
*
* @param array<string, mixed> $record
* @param list<string> $locales
*/
public function __construct(array $record, array $locales = ['en'])
{
parent::__construct($record, $locales);
$this->confidence = $record['confidence'] ?? null;
$this->geonameId = $record['geoname_id'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = parent::jsonSerialize();
if ($this->confidence !== null) {
$js['confidence'] = $this->confidence;
}
if ($this->geonameId !== null) {
$js['geoname_id'] = $this->geonameId;
}
return $js;
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* City-level data associated with an IP address.
*
* This record is returned by all location services and databases besides
* Country.
*/
// phpcs:disable
class City extends AbstractPlaceRecord {}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data for the continent record associated with an IP address.
*
* This record is returned by all location services and databases.
*/
class Continent extends AbstractNamedRecord
{
/**
* @var string|null A two character continent code like "NA" (North
* America) or "OC" (Oceania). This attribute is returned by all location
* services and databases.
*/
public readonly ?string $code;
/**
* @var int|null The GeoName ID for the continent. This
* attribute is returned by all location services and databases.
*/
public readonly ?int $geonameId;
/**
* @ignore
*
* @param array<string, mixed> $record
* @param list<string> $locales
*/
public function __construct(array $record, array $locales = ['en'])
{
parent::__construct($record, $locales);
$this->code = $record['code'] ?? null;
$this->geonameId = $record['geoname_id'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = parent::jsonSerialize();
if ($this->code !== null) {
$js['code'] = $this->code;
}
if ($this->geonameId !== null) {
$js['geoname_id'] = $this->geonameId;
}
return $js;
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data for the country record associated with an IP address.
*
* This record is returned by all location services and databases.
*/
class Country extends AbstractPlaceRecord
{
/**
* @var bool This is true if the country is a
* member state of the European Union. This attribute is returned by all
* location services and databases.
*/
public readonly bool $isInEuropeanUnion;
/**
* @var string|null The two-character ISO 3166-1 alpha code
* for the country. See https://en.wikipedia.org/wiki/ISO_3166-1. This
* attribute is returned by all location services and databases.
*/
public readonly ?string $isoCode;
/**
* @ignore
*
* @param array<string, mixed> $record
* @param list<string> $locales
*/
public function __construct(array $record, array $locales = ['en'])
{
parent::__construct($record, $locales);
$this->isInEuropeanUnion = $record['is_in_european_union'] ?? false;
$this->isoCode = $record['iso_code'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = parent::jsonSerialize();
if ($this->isInEuropeanUnion !== false) {
$js['is_in_european_union'] = $this->isInEuropeanUnion;
}
if ($this->isoCode !== null) {
$js['iso_code'] = $this->isoCode;
}
return $js;
}
}

View File

@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data for the location record associated with an IP address.
*
* This record is returned by all location services and databases besides
* Country.
*/
class Location implements \JsonSerializable
{
/**
* @var int|null The average income in US dollars
* associated with the requested IP address. This attribute is only available
* from the Insights service.
*/
public readonly ?int $averageIncome;
/**
* @var int|null The approximate accuracy radius in
* kilometers around the latitude and longitude for the IP address. This is
* the radius where we have a 67% confidence that the device using the IP
* address resides within the circle centered at the latitude and longitude
* with the provided radius.
*/
public readonly ?int $accuracyRadius;
/**
* @var float|null The approximate latitude of the location
* associated with the IP address. This value is not precise and should not be
* used to identify a particular address or household.
*/
public readonly ?float $latitude;
/**
* @var float|null The approximate longitude of the location
* associated with the IP address. This value is not precise and should not be
* used to identify a particular address or household.
*/
public readonly ?float $longitude;
/**
* @var int|null The metro code of the location if the location
* is in the US. MaxMind returns the same metro codes as the
* Google AdWords API. See
* https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions.
*/
public readonly ?int $metroCode;
/**
* @var int|null The estimated population per square
* kilometer associated with the IP address. This attribute is only available
* from the Insights service.
*/
public readonly ?int $populationDensity;
/**
* @var string|null The time zone associated with location, as
* specified by the IANA Time Zone Database, e.g., "America/New_York". See
* https://www.iana.org/time-zones.
*/
public readonly ?string $timeZone;
/**
* @ignore
*
* @param array<string, mixed> $record
*/
public function __construct(array $record)
{
$this->averageIncome = $record['average_income'] ?? null;
$this->accuracyRadius = $record['accuracy_radius'] ?? null;
$this->latitude = $record['latitude'] ?? null;
$this->longitude = $record['longitude'] ?? null;
$this->metroCode = $record['metro_code'] ?? null;
$this->populationDensity = $record['population_density'] ?? null;
$this->timeZone = $record['time_zone'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = [];
if ($this->averageIncome !== null) {
$js['average_income'] = $this->averageIncome;
}
if ($this->accuracyRadius !== null) {
$js['accuracy_radius'] = $this->accuracyRadius;
}
if ($this->latitude !== null) {
$js['latitude'] = $this->latitude;
}
if ($this->longitude !== null) {
$js['longitude'] = $this->longitude;
}
if ($this->metroCode !== null) {
$js['metro_code'] = $this->metroCode;
}
if ($this->populationDensity !== null) {
$js['population_density'] = $this->populationDensity;
}
if ($this->timeZone !== null) {
$js['time_zone'] = $this->timeZone;
}
return $js;
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data about your account.
*
* This record is returned by all location services and databases.
*/
class MaxMind implements \JsonSerializable
{
/**
* @var int|null the number of remaining queries you
* have for the service you are calling
*/
public readonly ?int $queriesRemaining;
/**
* @ignore
*
* @param array<string, mixed> $record
*/
public function __construct(array $record)
{
$this->queriesRemaining = $record['queries_remaining'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = [];
if ($this->queriesRemaining !== null) {
$js['queries_remaining'] = $this->queriesRemaining;
}
return $js;
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data for the postal record associated with an IP address.
*
* This record is returned by all location databases and services besides
* Country.
*/
class Postal implements \JsonSerializable
{
/**
* @var string|null The postal code of the location. Postal codes
* are not available for all countries. In some countries, this will only
* contain part of the postal code. This attribute is returned by all location
* databases and services besides Country.
*/
public readonly ?string $code;
/**
* @var int|null A value from 0-100 indicating MaxMind's
* confidence that the postal code is correct. This attribute is only
* available from the Insights service and the GeoIP2 Enterprise
* database.
*/
public readonly ?int $confidence;
/**
* @ignore
*
* @param array<string, mixed> $record
*/
public function __construct(array $record)
{
$this->code = $record['code'] ?? null;
$this->confidence = $record['confidence'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = [];
if ($this->code !== null) {
$js['code'] = $this->code;
}
if ($this->confidence !== null) {
$js['confidence'] = $this->confidence;
}
return $js;
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data for the represented country associated with an IP address.
*
* This class contains the country-level data associated with an IP address
* for the IP's represented country. The represented country is the country
* represented by something like a military base.
*/
class RepresentedCountry extends Country
{
/**
* @var string|null A string indicating the type of entity that is
* representing the country. Currently we only return <code>military</code>
* but this could expand to include other types in the future.
*/
public readonly ?string $type;
/**
* @ignore
*
* @param array<string, mixed> $record
* @param list<string> $locales
*/
public function __construct(array $record, array $locales = ['en'])
{
parent::__construct($record, $locales);
$this->type = $record['type'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = parent::jsonSerialize();
if ($this->type !== null) {
$js['type'] = $this->type;
}
return $js;
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
/**
* Contains data for the subdivisions associated with an IP address.
*
* This record is returned by all location databases and services besides
* Country.
*/
class Subdivision extends AbstractPlaceRecord
{
/**
* @var string|null This is a string up to three characters long
* contain the subdivision portion of the ISO 3166-2 code. See
* https://en.wikipedia.org/wiki/ISO_3166-2. This attribute is returned by all
* location databases and services except Country.
*/
public readonly ?string $isoCode;
/**
* @ignore
*
* @param array<string, mixed> $record
* @param list<string> $locales
*/
public function __construct(array $record, array $locales = ['en'])
{
parent::__construct($record, $locales);
$this->isoCode = $record['iso_code'] ?? null;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = parent::jsonSerialize();
if ($this->isoCode !== null) {
$js['iso_code'] = $this->isoCode;
}
return $js;
}
}

View File

@@ -0,0 +1,306 @@
<?php
declare(strict_types=1);
namespace GeoIp2\Record;
use GeoIp2\Util;
/**
* Contains data for the traits record associated with an IP address.
*
* This record is returned by all location services and databases.
*/
class Traits implements \JsonSerializable
{
/**
* @var int|null 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 Plus and Insights web services and the
* GeoIP2 Enterprise database.
*/
public readonly ?int $autonomousSystemNumber;
/**
* @var string|null 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 Plus and Insights web services and
* the GeoIP2 Enterprise database.
*/
public readonly ?string $autonomousSystemOrganization;
/**
* @var string|null The connection type may take the
* following values: "Dialup", "Cable/DSL", "Corporate", "Cellular", and
* "Satellite". Additional values may be added in the future. This attribute is
* only available from the City Plus and Insights web services and the GeoIP2
* Enterprise database.
*/
public readonly ?string $connectionType;
/**
* @var string|null 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 Plus and Insights web services and the GeoIP2 Enterprise
* database.
*/
public readonly ?string $domain;
/**
* @var string|null The IP address that the data in the model
* is for. If you performed a "me" lookup against the web service, this
* will be the externally routable IP address for the system the code is
* running on. If the system is behind a NAT, this may differ from the IP
* address locally assigned to it. This attribute is returned by all end
* points.
*/
public readonly ?string $ipAddress;
/**
* @var bool This is true if the IP address belongs to
* any sort of anonymous network. This property is only available from GeoIP2
* Insights.
*/
public readonly bool $isAnonymous;
/**
* @var bool This is true if the IP address is
* 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 Insights.
*/
public readonly bool $isAnonymousVpn;
/**
* @var bool This is true if the IP address belongs to an [anycast
* network](https://en.wikipedia.org/wiki/Anycast). This property is not
* available from GeoLite databases or web services.
*/
public readonly bool $isAnycast;
/**
* @var bool 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 Insights.
*/
public readonly bool $isHostingProvider;
/**
* @var bool 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.
*/
public readonly bool $isLegitimateProxy;
/**
* @var bool This is true if the IP address belongs to
* a public proxy. This property is only available from GeoIP2 Insights.
*/
public readonly bool $isPublicProxy;
/**
* @var bool 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.
*/
public readonly bool $isResidentialProxy;
/**
* @var bool This is true if the IP address is a Tor
* exit node. This property is only available from GeoIP2 Insights.
*/
public readonly bool $isTorExitNode;
/**
* @var string|null The name of the ISP associated with the IP
* address. This attribute is only available from the City Plus and Insights
* web services and the GeoIP2 Enterprise database.
*/
public readonly ?string $isp;
/**
* @var string|null 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.
*/
public readonly ?string $mobileCountryCode;
/**
* @var string|null 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.
*/
public readonly ?string $mobileNetworkCode;
/**
* @var string|null 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.
*/
public readonly ?string $network;
/**
* @var string|null 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.
*/
public readonly ?string $organization;
/**
* @var float|null An indicator of how static or
* dynamic an IP address is. This property is only available from GeoIP2
* Insights.
*/
public readonly ?float $staticIpScore;
/**
* @var int|null 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 Insights.
*/
public readonly ?int $userCount;
/**
* @var string|null <p>The user type associated with the IP
* address. This can be one of the following values:</p>
* <ul>
* <li>business
* <li>cafe
* <li>cellular
* <li>college
* <li>consumer_privacy_network
* <li>content_delivery_network
* <li>dialup
* <li>government
* <li>hosting
* <li>library
* <li>military
* <li>residential
* <li>router
* <li>school
* <li>search_engine_spider
* <li>traveler
* </ul>
* <p>
* This attribute is only available from the Insights web service and the
* GeoIP2 Enterprise database.
* </p>
*/
public readonly ?string $userType;
/**
* @ignore
*
* @param array<string, mixed> $record
*/
public function __construct(array $record)
{
$this->autonomousSystemNumber = $record['autonomous_system_number'] ?? null;
$this->autonomousSystemOrganization = $record['autonomous_system_organization'] ?? null;
$this->connectionType = $record['connection_type'] ?? null;
$this->domain = $record['domain'] ?? null;
$this->ipAddress = $record['ip_address'] ?? null;
$this->isAnonymous = $record['is_anonymous'] ?? false;
$this->isAnonymousVpn = $record['is_anonymous_vpn'] ?? false;
$this->isAnycast = $record['is_anycast'] ?? false;
$this->isHostingProvider = $record['is_hosting_provider'] ?? false;
$this->isLegitimateProxy = $record['is_legitimate_proxy'] ?? false;
$this->isp = $record['isp'] ?? null;
$this->isPublicProxy = $record['is_public_proxy'] ?? false;
$this->isResidentialProxy = $record['is_residential_proxy'] ?? false;
$this->isTorExitNode = $record['is_tor_exit_node'] ?? false;
$this->mobileCountryCode = $record['mobile_country_code'] ?? null;
$this->mobileNetworkCode = $record['mobile_network_code'] ?? null;
$this->organization = $record['organization'] ?? null;
$this->staticIpScore = $record['static_ip_score'] ?? null;
$this->userCount = $record['user_count'] ?? null;
$this->userType = $record['user_type'] ?? null;
if (isset($record['network'])) {
$this->network = $record['network'];
} else {
$this->network = isset($record['prefix_len']) ? Util::cidr($this->ipAddress, $record['prefix_len']) : null;
}
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array
{
$js = [];
if ($this->autonomousSystemNumber !== null) {
$js['autonomous_system_number'] = $this->autonomousSystemNumber;
}
if ($this->autonomousSystemOrganization !== null) {
$js['autonomous_system_organization'] = $this->autonomousSystemOrganization;
}
if ($this->connectionType !== null) {
$js['connection_type'] = $this->connectionType;
}
if ($this->domain !== null) {
$js['domain'] = $this->domain;
}
if ($this->ipAddress !== null) {
$js['ip_address'] = $this->ipAddress;
}
if ($this->isAnonymous !== false) {
$js['is_anonymous'] = $this->isAnonymous;
}
if ($this->isAnonymousVpn !== false) {
$js['is_anonymous_vpn'] = $this->isAnonymousVpn;
}
if ($this->isAnycast !== false) {
$js['is_anycast'] = $this->isAnycast;
}
if ($this->isHostingProvider !== false) {
$js['is_hosting_provider'] = $this->isHostingProvider;
}
if ($this->isLegitimateProxy !== false) {
$js['is_legitimate_proxy'] = $this->isLegitimateProxy;
}
if ($this->isPublicProxy !== false) {
$js['is_public_proxy'] = $this->isPublicProxy;
}
if ($this->isResidentialProxy !== false) {
$js['is_residential_proxy'] = $this->isResidentialProxy;
}
if ($this->isTorExitNode !== false) {
$js['is_tor_exit_node'] = $this->isTorExitNode;
}
if ($this->isp !== null) {
$js['isp'] = $this->isp;
}
if ($this->mobileCountryCode !== null) {
$js['mobile_country_code'] = $this->mobileCountryCode;
}
if ($this->mobileNetworkCode !== null) {
$js['mobile_network_code'] = $this->mobileNetworkCode;
}
if ($this->network !== null) {
$js['network'] = $this->network;
}
if ($this->organization !== null) {
$js['organization'] = $this->organization;
}
if ($this->staticIpScore !== null) {
$js['static_ip_score'] = $this->staticIpScore;
}
if ($this->userCount !== null) {
$js['user_count'] = $this->userCount;
}
if ($this->userType !== null) {
$js['user_type'] = $this->userType;
}
return $js;
}
}

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