yf_api classmodule: api
| File | plugins/api/classes/yf_api.class.php |
|---|---|
| Use | _class('api') |
A site sets them in its config with $PROJECT_CONF['api']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$API_SSL_VERIFY |
true |
|
$CACHE_TTL |
60 |
|
$CACHE_TTL_S |
30 |
|
$JSON_VULNERABILITY_PROTECTION |
true |
|
$ROBOT_NONE |
true |
|
$class |
null |
|
$class_i18n |
null |
|
$country_code |
'' |
|
$ip |
'' |
|
$ip4 |
'' |
|
$ip6 |
'' |
|
$ip_filter |
null |
|
$is_cli |
false |
|
$is_delete |
null |
|
$is_get |
null |
|
$is_head |
null |
|
$is_json |
null |
|
$is_jsonp |
null |
|
$is_patch |
null |
|
$is_post |
null |
|
$is_put |
null |
|
$is_request |
null |
|
$language_id |
null |
|
$method |
null |
|
$request |
null |
|
$request_method |
null |
None without a leading underscore.
_ hooks and helpers; not routable as page actionspublic function _cache($options = null)
public function _cache($options = null)
{
$_ttl = $_ttl_s = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
$h = [];
// none
if (($_no_store ?? null)) {
header('Cache-Control: no-store');
return;
}
// public/private
if (($_private ?? null)) {
$h[] = 'private';
} else {
$h[] = 'public';
}
switch (true) {
case $_no_cache ?? null : $h[] = 'no-cache';
break;
case $_ttl ?? null : $h[] = 'max-age=' . $_ttl;
// no break
case $_ttl_s ?? null : $h[] = 's-max-age=' . $_ttl_s;
break;
default:
$h[] = 'max-age=' . $this->CACHE_TTL;
$h[] = 's-maxage=' . $this->CACHE_TTL_S;
}
$h = implode(', ', $h);
header('Cache-Control: ' . $h);
}
public function _check_ip($options = null)
public function _check_ip($options = null)
{
$_ip_filter = $_ip = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// filter
$ip_filter = ($_ip_filter ?? null) ? $_ip_filter : $this->ip_filter;
is_string($ip_filter) && $ip_filter = (array) $ip_filter;
if ( ! is_array($ip_filter)) {
return null;
}
$ip_filter = $this->_ip_filter_valid($ip_filter);
if ( ! $ip_filter) {
return null;
}
// ip
$ip = isset($_ip) ? $_ip : $this->_ip();
foreach ($ip_filter as $range => $allow) {
$result = $this->_ip_in_range($ip, $range);
if ($result) {
return $allow;
}
}
return false;
}
public function _detect_country_code($force = false)
public function _detect_country_code($force = false)
{
if ($this->is_cli) {
return '';
}
if ( ! $force && $this->country_code) {
return $this->country_code;
}
$ip = $this->_detect_ip($force);
$r = $this->_geoip_country_code_by_name($ip);
if (empty($r)) {
return '';
}
$this->country_code = $r;
return $r;
}
public function _detect_ip($force = false)
public function _detect_ip($force = false)
{
if ($this->is_cli) {
return '';
}
if ( ! $force && $this->ip4) {
return $this->ip4;
}
$ip = $this->ip ?: $this->_ip();
if (empty($ip)) {
return '';
}
$this->ip = $ip;
if ($this->_is_ip6($ip)) {
$this->ip6 = $ip;
$ip4 = $this->_ip6_to_v4($ip);
if ($ip4) {
$this->ip4 = $ip4;
}
}
return $ip;
}
public function _detect_protocol($options = null)
public function _detect_protocol($options = null)
{
$result = 'HTTP/1.1';
if (function_exists('php_sapi_name')) { // PHP >= 4.1.0
$type = php_sapi_name();
substr($type, 0, 3) == 'cgi' && $result = 'Status:';
} else {
isset($_SERVER['SERVER_PROTOCOL']) && $result = ($_SERVER['SERVER_PROTOCOL'] ?? null);
}
return $result;
}
public function _detect_protocol_scheme($options = null)
public function _detect_protocol_scheme($options = null)
{
$result = 'http';
if ( ! empty($_SERVER['HTTPS']) || ($_SERVER['SERVER_PORT'] ?? null) == 443) {
$result .= 's';
}
return $result;
}
public function _error()
public function _error()
{
$this->_reject(500);
}
public function _forbidden()
public function _forbidden()
{
$this->_reject(403);
}
public function _geoip_country_code_by_name($v)
public function _geoip_country_code_by_name($v)
{
$r = '';
if ($this->_is_ip6($v)) {
$r = geoip_country_code_by_name_v6($v);
} else {
$r = geoip_country_code_by_name($v);
}
return $r ?: '';
}
public function _init()
public function _init()
{
$this->is_cli = (php_sapi_name() == 'cli');
ini_set('html_errors', 0);
$class = &$this->class;
$method = &$this->method;
$is_head = &$this->is_head;
$is_get = &$this->is_get;
$is_post = &$this->is_post;
$is_json = &$this->is_json;
$is_jsonp = &$this->is_jsonp;
$is_put = &$this->is_put;
$is_patch = &$this->is_patch;
$is_delete = &$this->is_delete;
$is_request = &$this->is_request;
$request_method = &$this->request_method;
// setup
$object = ($_GET['object'] ?? null);
if (empty($object) || $object != 'api') {
return null;
}
$class = ($_GET['action'] ?? null);
$method = ($_GET['id'] ?? null);
// language_id
$language_id = &$_GET['language_id'];
$this->language_id = $language_id;
$this->class_i18n = _class('i18n');
$this->class_i18n->_set_current_lang($language_id);
// http method
$request_method = ($_SERVER['REQUEST_METHOD'] ?? null) ?: 'GET';
switch ($request_method) {
case 'GET': $is_get = true;
break;
case 'POST': $is_post = true;
break;
case 'PUT': $is_put = true;
break;
case 'PATCH': $is_patch = true;
break;
case 'DELETE': $is_delete = true;
break;
}
if (strpos($_SERVER['CONTENT_TYPE'] ?? '', 'application/json') !== false) {
$is_json = true;
}
if ($is_get && is_string(($_GET['callback'] ?? null))) {
$is_jsonp = true;
}
// override
$class == 'show' && $class = ($_REQUEST['object'] ?? null);
! $method && $method = ($_REQUEST['action'] ?? null);
$this->_call($class, null, $method);
}
public function _ip($options = null)
public function _ip($options = null)
{
if ( ! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? ''));
$ip = reset($ips);
} else {
$ip =
($_SERVER['HTTP_CLIENT_IP'] ?? null)
?: ($_SERVER['HTTP_X_REAL_IP'] ?? null)
?: ($_SERVER['REMOTE_ADDR'] ?? null);
}
$result = trim($ip);
return $result;
}
public function _ip6_to_v4($ip)
public function _ip6_to_v4($ip)
{
$packed = inet_pton($ip);
if ($packed === false) {
return '';
}
if (strlen($packed) === 16 && substr($packed, 0, 10) === str_repeat("\0", 10) . str_repeat("\xff", 2)) {
// Extract the last 4 bytes and convert to IPv4
return inet_ntop(substr($packed, 12));
}
return '';
}
public function _ip_filter_valid($ip_filter)
public function _ip_filter_valid($ip_filter)
{
if ( ! is_array($ip_filter)) {
return false;
}
$result = [];
foreach ($ip_filter as $ip => $allow) {
if ( ! $ip) {
return false;
}
$r = strpos($ip, '.*');
$len = strlen($ip);
if ($r > 0 && ($len - $r) >= 2) {
$r = explode('.*', $ip);
unset($ip_filter[$ip]);
$count = count($r) - 1;
$mask = 32 - 8 * $count;
$ip = $r[0] . str_repeat('.0', $count) . '/' . $mask;
$ip_filter[$ip] = $allow;
}
$result[$ip] = $allow;
}
return $result;
}
public function _ip_in_range($ip, $range)
public function _ip_in_range($ip, $range)
{
if (strpos($range, '/') == false) {
$range .= '/32';
}
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - (int) $netmask)) - 1;
$netmask_decimal = ~$wildcard_decimal;
return ($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal);
}
public function _is_ip4($ip)
public function _is_ip4($ip)
{
return strpos($ip ?: '', '.') !== false;
}
public function _is_ip6($ip)
public function _is_ip6($ip)
{
return strpos($ip ?: '', ':') !== false;
}
public function _is_ip_external($ip)
public function _is_ip_external($ip)
{
$r = ! $this->_is_ip_internal($ip);
return $r;
}
public function _is_ip_internal($ip)
public function _is_ip_internal($ip)
{
$r = ! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
return $r;
}
public function _parse_request()
public function _parse_request()
{
$is_post = &$this->is_post;
$is_put = &$this->is_put;
$is_patch = &$this->is_patch;
$is_json = &$this->is_json;
$request = &$this->request;
if ($is_post || $is_put || $is_patch) {
$request = file_get_contents('php://input');
$request = json_decode(file_get_contents('php://input'), true);
$request && $is_json = true;
}
return $request;
}
public function _redirect($url, $message = null, $is_raw = true)
public function _redirect($url, $message = null, $is_raw = true)
{
list($protocol, $code, $status) = $this->_send_http_status(302);
// location
$url = $url ?: url('/');
$location = 'Location: ' . $url;
header($location);
// message
$this->_send_http_type();
$this->_send_http_content($message, $is_raw);
}
public function _reject($code = 503, $is_raw = true)
public function _reject($code = 503, $is_raw = true)
{
list($protocol, $code, $status) = $this->_send_http_status($code);
$this->_send_http_type();
$this->_send_http_content($status, $is_raw);
}
public function _request($url, $post = null, $options = null)
public function _request($url, $post = null, $options = null)
{
$_method = $_user = $_password = $_access_token = $_proxy = $_proxy_user_pwd = $_resolve = $_timeout = $_SSLCERT = $_SSLCERTPASSWD = $_SSLKEY = $_SSLKEYPASSWD = $_header = $_k = $_v = $_query = null;
// import options
is_array($options) && extract($options, EXTR_PREFIX_ALL | EXTR_REFS, '');
// options
$options = [
CURLOPT_RETURNTRANSFER => true,
// CURLOPT_URL => $url,
];
$user_agent = $_user_agent ?? 'YF.API';
if ($user_agent !== 'none') {
$options[CURLOPT_USERAGENT] = $user_agent;
}
$header = [];
if ( ! empty($post)) {
if (($_is_json ?? null)) {
$http_post = json_encode($post);
$options += [
CURLOPT_ENCODING => '',
CURLOPT_CUSTOMREQUEST => 'POST',
];
} else {
if (($_is_request_raw ?? null) || ($_is_post_raw ?? null)) {
$http_post = $post;
} else {
$http_post = http_build_query($post);
}
$options += [
CURLOPT_POST => true,
];
}
$options += [
CURLOPT_POSTFIELDS => $http_post,
];
}
if (($_is_post ?? null)) {
$options += [
CURLOPT_POST => true,
];
}
if (($_method ?? null)) {
$options += [
CURLOPT_CUSTOMREQUEST => $_method,
];
}
if (($_user ?? null)) {
$userpwd = $_user;
($_password ?? null) && $userpwd .= ':' . $_password;
$options += [
CURLOPT_HTTPAUTH => CURLAUTH_ANY,
CURLOPT_USERPWD => $userpwd,
];
}
if (($_bearer ?? null) || ($_access_token ?? null)) {
( ! ($_bearer ?? null) && $_access_token) && $_bearer = $_access_token;
$header[] = 'Authorization: Bearer ' . $_bearer;
}
if (($_is_json ?? null)) {
$header[] = 'Content-Type: application/json; charset=utf-8';
}
if ($this->API_SSL_VERIFY && strpos($url, 'https') !== false) {
$options += [
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => ($_CA ?? null) ?: __DIR__ . '/ca.pem',
];
} else {
$options += [
CURLOPT_SSL_VERIFYPEER => false,
];
}
if (($_proxy ?? null)) {
// proxy: 128.0.0.1:8080
$options += [CURLOPT_PROXY => $_proxy];
// auth: USERNAME:PASSWORD
($_proxy_user_pwd ?? null) && $options += [CURLOPT_PROXYUSERPWD => $_proxy_user_pwd];
// type: http, https, socks5 ...
$i = CURLPROXY_HTTPS;
switch (($_proxy_type ?? null)) {
case 'http': $i = CURLPROXY_HTTP;
break;
case 'https': $i = CURLPROXY_HTTPS;
break;
case 'socks4': $i = CURLPROXY_SOCKS4;
break;
case 'socks4a': $i = CURLPROXY_SOCKS4A;
break;
case 'socks5': $i = CURLPROXY_SOCKS5;
break;
case 'socks5_hostname': $i = CURLPROXY_SOCKS5_HOSTNAME;
break;
}
$options += [CURLOPT_PROXYTYPE => $i];
}
// resolve = [
// "example.com:80:192.0.2.1",
// ];
($_resolve ?? null) && $options += [CURLOPT_RESOLVE => $_resolve];
// other
($_timeout ?? null) && $options += [CURLOPT_TIMEOUT => $_timeout];
($_SSLCERT ?? null) && $options += [CURLOPT_SSLCERT => $_SSLCERT];
($_SSLCERTPASSWD ?? null) && $options += [CURLOPT_SSLCERTPASSWD => $_SSLCERTPASSWD];
($_SSLKEY ?? null) && $options += [CURLOPT_SSLKEY => $_SSLKEY];
($_SSLKEYPASSWD ?? null) && $options += [CURLOPT_SSLKEYPASSWD => $_SSLKEYPASSWD];
// redirect
$is_redirect = ($_is_redirect ?? null) || ($_is_followlocation ?? null);
if ($is_redirect) {
$options += [
CURLOPT_FOLLOWLOCATION => true,
];
}
// add header
if ( ! empty($_header)) {
($_is_debug ?? null) && var_dump($_header);
if (empty($header)) {
$header = $_header;
} else {
foreach ($_header as $_i) {
list($_k, $_v) = explode(':', $_i);
$_k = trim($_k);
$is_key = false;
foreach ($header as &$i) {
list($k, $v) = explode(':', $i);
$k = trim($k);
if ($k === $_k) {
$i = $_i;
$is_key = true;
}
}
if ( ! $is_key) {
$header[] = $_i;
}
}
}
}
($_is_debug ?? null) && var_dump($header);
! empty($header) && $options += [CURLOPT_HTTPHEADER => $header];
// debug request header
$options += [
CURLINFO_HEADER_OUT => true,
];
// debug response header
$options += [
CURLOPT_VERBOSE => true,
CURLOPT_HEADER => true,
];
if (($_query ?? null)) {
if (is_array($_query)) {
$q = http_build_query($_query);
} else {
$q = $_query;
}
$url .= '?' . $q;
}
// exec
$ch = curl_init($url);
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
// status
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$http_header = curl_getinfo($ch, CURLINFO_HEADER_OUT);
$error_number = curl_errno($ch);
$error_message = curl_error($ch);
// DEBUG
@$_is_debug && var_dump($url, $options, $http_code, $http_header);
// response
$status = null;
if ($response === false) {
$message = sprintf('[%d] %s', $error_number, $error_message);
$result = [
'status' => $status,
'status_message' => 'Transport error: ' . $message,
];
return $result;
}
// get header, body
$http_header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $http_header_size);
$body = substr($response, $http_header_size);
// DEBUG
@$_is_debug && var_dump($header, $body);
// http code
$is_error = false;
$message = '';
$result = null;
switch ($http_code) {
case 200: $status = true;
break;
case 201: $status = true;
break;
case 301: $message = 'Moved Permanently';
break;
case 302: $message = 'Moved Temporarily';
break;
case 400: $message = 'bad request';
break;
case 401: $message = 'unauthorized';
break;
case 403: $message = 'forbidden';
break;
case 404: $message = 'not found';
break;
default:
if ($http_code >= 500) {
$message = 'server error';
}
break;
}
if ($http_code != 200 && $http_code != 201) {
$result = sprintf('Transport error: [%d] %s', $http_code, $message);
}
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
// DEBUG
($_is_debug ?? null) && var_dump($content_type);
// finish
// curl_close: This function has been DEPRECATED as of PHP 8.5.0
// curl_close($ch);
unset($ch);
// detect content type of response
if (($_is_response_raw ?? null)) {
$result = $body;
} else {
switch (true) {
case ($_is_response_json ?? null) || strpos($content_type, 'application/json') === 0:
$result = @json_decode($body, true);
if ($result ?? false) {
$status = true;
}
// DEBUG
($_is_debug ?? null) && var_dump('is_json', $result);
break;
case ($_is_response_html ?? null) || ($_is_response_xml ?? null) || ($_is_response_form ?? null)
|| strpos($content_type, 'application/xml') === 0
|| strpos($content_type, 'text/xml') === 0
|| strpos($content_type, 'text/html') === 0:
libxml_use_internal_errors(true);
$xml_response = @simplexml_load_string($body);
$error = libxml_get_errors();
// DEBUG
($_is_debug ?? null) && var_dump('is_xml', $result, $error);
if ($error) {
libxml_clear_errors();
$result = [
'status' => null,
'status_message' => 'Response error: invalid data structure',
'error' => $error,
'content' => $body,
];
return $result;
}
if (@$xml_response->getName() == 'error') {
$result = [
'status' => null,
'status_message' => 'Response error: invalid data - ' . (string) $xml_response,
'content' => $body,
];
return $result;
}
if (($_is_response_form ?? null)) {
$form = [];
foreach ($xml_response->xpath('//input') as $item) {
$_item = $item->attributes();
$name = (string) $_item->name;
$value = (string) $_item->value;
$form[$name] = $value;
}
! empty($form) && $xml_response = $form;
// DEBUG
($_is_debug ?? null) && var_dump('is_form', $form);
}
$result = $xml_response;
}
}
if (($_is_http_raw ?? null)) {
$result = [
'status' => $status,
'http_code' => $http_code,
'http_message' => $message,
'body' => $result,
];
return $result;
}
return [$status, $result];
}
public function _response_raw($message = null, $code = 200, $type = null)
public function _response_raw($message = null, $code = 200, $type = null)
{
$is_raw = true;
// code
list($protocol, $code, $status) = $this->_send_http_status($code);
$this->_send_http_type($type);
// message
$message = ($message ?? null) ?: $status;
$this->_send_http_content($message, $is_raw);
}
public function _robot_none()
public function _robot_none()
{
// none = noindex, nofollow
header('X-Robots-Tag: none');
}