yf_api class

module: api

Fileplugins/api/classes/yf_api.class.php
Use_class('api')

Public properties

A site sets them in its config with $PROJECT_CONF['api']['NAME'] = ...;

PropertyDefaultDescription
$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

Public methods 24

None without a leading underscore.

Methods starting with _ hooks and helpers; not routable as page actions

_cache

public 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);
    }

_check_ip

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;
    }

_detect_ip

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;
    }

_error

public function _error()
    public function _error()
    {
        $this->_reject(500);
    }

_forbidden

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 ?: '';
    }

_init

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);
    }

_ip

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;
    }

_ip6_to_v4

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;
    }

_ip_in_range

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);
    }

_is_ip4

public function _is_ip4($ip)
    public function _is_ip4($ip)
    {
        return strpos($ip ?: '', '.') !== false;
    }

_is_ip6

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;
    }

_redirect

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);
    }

_reject

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);
    }

_request

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];
    }

_response_raw

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);
    }

_robot_none

public function _robot_none()
    public function _robot_none()
    {
        // none = noindex, nofollow
        header('X-Robots-Tag: none');
    }