chriskacerguis / codeigniter-restserver

A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller.
MIT License
4.89k stars 2.86k forks source link

I got error "Class Example cannot extend from trait Restserver\Libraries\REST_Controller" #1002

Closed ngoctt18 closed 5 years ago

ngoctt18 commented 5 years ago

I copy all the folders inside folder application into my folder application in my project. I also use composer require chriskacerguis/codeigniter-restserver but when i open example page. I got this error.

Untitled

shihasck commented 5 years ago

https://stackoverflow.com/questions/56824024/class-example-cannot-extend-from-trait-restserver-libraries-rest-controller

ipsjolly commented 5 years ago

application\libraries\Format.php

Remove following code on line 2-3

  namespace Restserver\Libraries;
  use Exception;

application\libraries\REST_Controller.php

Replace below code upto line 740

    <?php

    defined('BASEPATH') OR exit('No direct script access allowed');

    /**
     * CodeIgniter Rest Controller
     * A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller.
     *
     * @package         CodeIgniter
     * @subpackage      Libraries
     * @category        Libraries
     * @author          Phil Sturgeon, Chris Kacerguis
     * @license         MIT
     * @link            https://github.com/chriskacerguis/codeigniter-restserver
     * @version         3.0.0
     */
    abstract class REST_Controller extends CI_Controller {

        // Note: Only the widely used HTTP status codes are documented

        // Informational

        const HTTP_CONTINUE = 100;
        const HTTP_SWITCHING_PROTOCOLS = 101;
        const HTTP_PROCESSING = 102;            // RFC2518

        // Success

        /**
         * The request has succeeded
         */
        const HTTP_OK = 200;

        /**
         * The server successfully created a new resource
         */
        const HTTP_CREATED = 201;
        const HTTP_ACCEPTED = 202;
        const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;

        /**
         * The server successfully processed the request, though no content is returned
         */
        const HTTP_NO_CONTENT = 204;
        const HTTP_RESET_CONTENT = 205;
        const HTTP_PARTIAL_CONTENT = 206;
        const HTTP_MULTI_STATUS = 207;          // RFC4918
        const HTTP_ALREADY_REPORTED = 208;      // RFC5842
        const HTTP_IM_USED = 226;               // RFC3229

        // Redirection

        const HTTP_MULTIPLE_CHOICES = 300;
        const HTTP_MOVED_PERMANENTLY = 301;
        const HTTP_FOUND = 302;
        const HTTP_SEE_OTHER = 303;

        /**
         * The resource has not been modified since the last request
         */
        const HTTP_NOT_MODIFIED = 304;
        const HTTP_USE_PROXY = 305;
        const HTTP_RESERVED = 306;
        const HTTP_TEMPORARY_REDIRECT = 307;
        const HTTP_PERMANENTLY_REDIRECT = 308;  // RFC7238

        // Client Error

        /**
         * The request cannot be fulfilled due to multiple errors
         */
        const HTTP_BAD_REQUEST = 400;

        /**
         * The user is unauthorized to access the requested resource
         */
        const HTTP_UNAUTHORIZED = 401;
        const HTTP_PAYMENT_REQUIRED = 402;

        /**
         * The requested resource is unavailable at this present time
         */
        const HTTP_FORBIDDEN = 403;

        /**
         * The requested resource could not be found
         *
         * Note: This is sometimes used to mask if there was an UNAUTHORIZED (401) or
         * FORBIDDEN (403) error, for security reasons
         */
        const HTTP_NOT_FOUND = 404;

        /**
         * The request method is not supported by the following resource
         */
        const HTTP_METHOD_NOT_ALLOWED = 405;

        /**
         * The request was not acceptable
         */
        const HTTP_NOT_ACCEPTABLE = 406;
        const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
        const HTTP_REQUEST_TIMEOUT = 408;

        /**
         * The request could not be completed due to a conflict with the current state
         * of the resource
         */
        const HTTP_CONFLICT = 409;
        const HTTP_GONE = 410;
        const HTTP_LENGTH_REQUIRED = 411;
        const HTTP_PRECONDITION_FAILED = 412;
        const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
        const HTTP_REQUEST_URI_TOO_LONG = 414;
        const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
        const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
        const HTTP_EXPECTATION_FAILED = 417;
        const HTTP_I_AM_A_TEAPOT = 418;                                               // RFC2324
        const HTTP_UNPROCESSABLE_ENTITY = 422;                                        // RFC4918
        const HTTP_LOCKED = 423;                                                      // RFC4918
        const HTTP_FAILED_DEPENDENCY = 424;                                           // RFC4918
        const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425;   // RFC2817
        const HTTP_UPGRADE_REQUIRED = 426;                                            // RFC2817
        const HTTP_PRECONDITION_REQUIRED = 428;                                       // RFC6585
        const HTTP_TOO_MANY_REQUESTS = 429;                                           // RFC6585
        const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;                             // RFC6585

        // Server Error

        /**
         * The server encountered an unexpected error
         *
         * Note: This is a generic error message when no specific message
         * is suitable
         */
        const HTTP_INTERNAL_SERVER_ERROR = 500;

        /**
         * The server does not recognise the request method
         */
        const HTTP_NOT_IMPLEMENTED = 501;
        const HTTP_BAD_GATEWAY = 502;
        const HTTP_SERVICE_UNAVAILABLE = 503;
        const HTTP_GATEWAY_TIMEOUT = 504;
        const HTTP_VERSION_NOT_SUPPORTED = 505;
        const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506;                        // RFC2295
        const HTTP_INSUFFICIENT_STORAGE = 507;                                        // RFC4918
        const HTTP_LOOP_DETECTED = 508;                                               // RFC5842
        const HTTP_NOT_EXTENDED = 510;                                                // RFC2774
        const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;

        /**
         * This defines the rest format
         * Must be overridden it in a controller so that it is set
         *
         * @var string|NULL
         */
        protected $rest_format = NULL;

        /**
         * Defines the list of method properties such as limit, log and level
         *
         * @var array
         */
        protected $methods = [];

        /**
         * List of allowed HTTP methods
         *
         * @var array
         */
        protected $allowed_http_methods = ['get', 'delete', 'post', 'put', 'options', 'patch', 'head'];

        /**
         * Contains details about the request
         * Fields: body, format, method, ssl
         * Note: This is a dynamic object (stdClass)
         *
         * @var object
         */
        protected $request = NULL;

        /**
         * Contains details about the response
         * Fields: format, lang
         * Note: This is a dynamic object (stdClass)
         *
         * @var object
         */
        protected $response = NULL;

        /**
         * Contains details about the REST API
         * Fields: db, ignore_limits, key, level, user_id
         * Note: This is a dynamic object (stdClass)
         *
         * @var object
         */
        protected $rest = NULL;

        /**
         * The arguments for the GET request method
         *
         * @var array
         */
        protected $_get_args = [];

        /**
         * The arguments for the POST request method
         *
         * @var array
         */
        protected $_post_args = [];

        /**
         * The arguments for the PUT request method
         *
         * @var array
         */
        protected $_put_args = [];

        /**
         * The arguments for the DELETE request method
         *
         * @var array
         */
        protected $_delete_args = [];

        /**
         * The arguments for the PATCH request method
         *
         * @var array
         */
        protected $_patch_args = [];

        /**
         * The arguments for the HEAD request method
         *
         * @var array
         */
        protected $_head_args = [];

        /**
         * The arguments for the OPTIONS request method
         *
         * @var array
         */
        protected $_options_args = [];

        /**
         * The arguments for the query parameters
         *
         * @var array
         */
        protected $_query_args = [];

        /**
         * The arguments from GET, POST, PUT, DELETE, PATCH, HEAD and OPTIONS request methods combined
         *
         * @var array
         */
        protected $_args = [];

        /**
         * The insert_id of the log entry (if we have one)
         *
         * @var string
         */
        protected $_insert_id = '';

        /**
         * If the request is allowed based on the API key provided
         *
         * @var bool
         */
        protected $_allow = TRUE;

        /**
         * The LDAP Distinguished Name of the User post authentication
         *
         * @var string
         */
        protected $_user_ldap_dn = '';

        /**
         * The start of the response time from the server
         *
         * @var string
         */
        protected $_start_rtime = '';

        /**
         * The end of the response time from the server
         *
         * @var string
         */
        protected $_end_rtime = '';

        /**
         * List all supported methods, the first will be the default format
         *
         * @var array
         */
        protected $_supported_formats = [
                'json' => 'application/json',
                'array' => 'application/json',
                'csv' => 'application/csv',
                'html' => 'text/html',
                'jsonp' => 'application/javascript',
                'php' => 'text/plain',
                'serialized' => 'application/vnd.php.serialized',
                'xml' => 'application/xml'
            ];

        /**
         * Information about the current API user
         *
         * @var object
         */
        protected $_apiuser;

        /**
         * Whether or not to perform a CORS check and apply CORS headers to the request
         *
         * @var bool
         */
        protected $check_cors = NULL;

        /**
         * Enable XSS flag
         * Determines whether the XSS filter is always active when
         * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered
         * Set automatically based on config setting
         *
         * @var bool
         */
        protected $_enable_xss = FALSE;

        /**
         * HTTP status codes and their respective description
         * Note: Only the widely used HTTP status codes are used
         *
         * @var array
         * @link http://www.restapitutorial.com/httpstatuscodes.html
         */
        protected $http_status_codes = [
            self::HTTP_OK => 'OK',
            self::HTTP_CREATED => 'CREATED',
            self::HTTP_NO_CONTENT => 'NO CONTENT',
            self::HTTP_NOT_MODIFIED => 'NOT MODIFIED',
            self::HTTP_BAD_REQUEST => 'BAD REQUEST',
            self::HTTP_UNAUTHORIZED => 'UNAUTHORIZED',
            self::HTTP_FORBIDDEN => 'FORBIDDEN',
            self::HTTP_NOT_FOUND => 'NOT FOUND',
            self::HTTP_METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED',
            self::HTTP_NOT_ACCEPTABLE => 'NOT ACCEPTABLE',
            self::HTTP_CONFLICT => 'CONFLICT',
            self::HTTP_INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR',
            self::HTTP_NOT_IMPLEMENTED => 'NOT IMPLEMENTED'
        ];

        /**
         * Extend this function to apply additional checking early on in the process
         *
         * @access protected
         * @return void
         */
        protected function early_checks()
        {
        }

        /**
         * Constructor for the REST API
         *
         * @access public
         * @param string $config Configuration filename minus the file extension
         * e.g: my_rest.php is passed as 'my_rest'
         */
        public function __construct($config = 'rest')
        {
            parent::__construct();

            $this->preflight_checks();

            // Set the default value of global xss filtering. Same approach as CodeIgniter 3
            $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE);

            // Don't try to parse template variables like {elapsed_time} and {memory_usage}
            // when output is displayed for not damaging data accidentally
            $this->output->parse_exec_vars = FALSE;

            // Start the timer for how long the request takes
            $this->_start_rtime = microtime(TRUE);

            // Load the rest.php configuration file
            $this->load->config($config);

            // At present the library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter (no citation)
            $this->load->library('format');

            // Determine supported output formats from configuration
            $supported_formats = $this->config->item('rest_supported_formats');

            // Validate the configuration setting output formats
            if (empty($supported_formats))
            {
                $supported_formats = [];
            }

            if ( ! is_array($supported_formats))
            {
                $supported_formats = [$supported_formats];
            }

            // Add silently the default output format if it is missing
            $default_format = $this->_get_default_output_format();
            if (!in_array($default_format, $supported_formats))
            {
                $supported_formats[] = $default_format;
            }

            // Now update $this->_supported_formats
            $this->_supported_formats = array_intersect_key($this->_supported_formats, array_flip($supported_formats));

            // Get the language
            $language = $this->config->item('rest_language');
            if ($language === NULL)
            {
                $language = 'english';
            }

            // Load the language file
            $this->lang->load('rest_controller', $language);

            // Initialise the response, request and rest objects
            $this->request = new stdClass();
            $this->response = new stdClass();
            $this->rest = new stdClass();

            // Check to see if the current IP address is blacklisted
            if ($this->config->item('rest_ip_blacklist_enabled') === TRUE)
            {
                $this->_check_blacklist_auth();
            }

            // Determine whether the connection is HTTPS
            $this->request->ssl = is_https();

            // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS
            $this->request->method = $this->_detect_method();

            // Check for CORS access request
            $check_cors = $this->config->item('check_cors');
            if ($check_cors === TRUE)
            {
                $this->_check_cors();
            }

            // Create an argument container if it doesn't exist e.g. _get_args
            if (isset($this->{'_'.$this->request->method.'_args'}) === FALSE)
            {
                $this->{'_'.$this->request->method.'_args'} = [];
            }

            // Set up the query parameters
            $this->_parse_query();

            // Set up the GET variables
            $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc());

            // Try to find a format for the request (means we have a request body)
            $this->request->format = $this->_detect_input_format();

            // Not all methods have a body attached with them
            $this->request->body = NULL;

            $this->{'_parse_' . $this->request->method}();

            // Now we know all about our request, let's try and parse the body if it exists
            if ($this->request->format && $this->request->body)
            {
                $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array();
                // Assign payload arguments to proper method container
                $this->{'_'.$this->request->method.'_args'} = $this->request->body;
            }

            //get header vars
            $this->_head_args = $this->input->request_headers();

            // Merge both for one mega-args variable
            $this->_args = array_merge(
                $this->_get_args,
                $this->_options_args,
                $this->_patch_args,
                $this->_head_args,
                $this->_put_args,
                $this->_post_args,
                $this->_delete_args,
                $this->{'_'.$this->request->method.'_args'}
            );

            // Which format should the data be returned in?
            $this->response->format = $this->_detect_output_format();

            // Which language should the data be returned in?
            $this->response->lang = $this->_detect_lang();

            // Extend this function to apply additional checking early on in the process
            $this->early_checks();

            // Load DB if its enabled
            if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging')))
            {
                $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE);
            }

            // Use whatever database is in use (isset returns FALSE)
            elseif (property_exists($this, 'db'))
            {
                $this->rest->db = $this->db;
            }

            // Check if there is a specific auth type for the current class/method
            // _auth_override_check could exit so we need $this->rest->db initialized before
            $this->auth_override = $this->_auth_override_check();

            // Checking for keys? GET TO WorK!
            // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none'
            if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE)
            {
                $this->_allow = $this->_detect_api_key();
            }

            // Only allow ajax requests
            if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only'))
            {
                // Display an error response
                $this->response([
                        $this->config->item('rest_status_field_name') => FALSE,
                        $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ajax_only')
                    ], self::HTTP_NOT_ACCEPTABLE);
            }

            // When there is no specific override for the current class/method, use the default auth value set in the config
            if ($this->auth_override === FALSE &&
                (! ($this->config->item('rest_enable_keys') && $this->_allow === TRUE) ||
                ($this->config->item('allow_auth_and_keys') === TRUE && $this->_allow === TRUE)))
            {
                $rest_auth = strtolower($this->config->item('rest_auth'));
                switch ($rest_auth)
                {
                    case 'basic':
                        $this->_prepare_basic_auth();
                        break;
                    case 'digest':
                        $this->_prepare_digest_auth();
                        break;
                    case 'session':
                        $this->_check_php_session();
                        break;
                }
                if ($this->config->item('rest_ip_whitelist_enabled') === TRUE)
                {
                    $this->_check_whitelist_auth();
                }
            }
        }

        /**
         * Deconstructor
         *
         * @author Chris Kacerguis
         * @access public
         * @return void
         */
        public function __destruct()
        {
            // Get the current timestamp
            $this->_end_rtime = microtime(TRUE);

            // Log the loading time to the log table
            if ($this->config->item('rest_enable_logging') === TRUE)
            {
                $this->_log_access_time();
            }
        }

        /**
         * Checks to see if we have everything we need to run this library.
         *
         * @access protected
         * @@throws Exception
         */
        protected function preflight_checks()
        {
            // Check to see if PHP is equal to or greater than 5.4.x
            if (is_php('5.4') === FALSE)
            {
                // CodeIgniter 3 is recommended for v5.4 or above
                throw new Exception('Using PHP v'.PHP_VERSION.', though PHP v5.4 or greater is required');
            }

            // Check to see if this is CI 3.x
            if (explode('.', CI_VERSION, 2)[0] < 3)
            {
                throw new Exception('REST Server requires CodeIgniter 3.x');
            }
        }

        /**
         * Requests are not made to methods directly, the request will be for
         * an "object". This simply maps the object and method to the correct
         * Controller method
         *
         * @access public
         * @param string $object_called
         * @param array $arguments The arguments passed to the controller method
         */
        public function _remap($object_called, $arguments = [])
        {
            // Should we answer if not over SSL?
            if ($this->config->item('force_https') && $this->request->ssl === FALSE)
            {
                $this->response([
                        $this->config->item('rest_status_field_name') => FALSE,
                        $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unsupported')
                    ], self::HTTP_FORBIDDEN);
            }

            // Remove the supported format from the function name e.g. index.json => index
            $object_called = preg_replace('/^(.*)\.(?:'.implode('|', array_keys($this->_supported_formats)).')$/', '$1', $object_called);

            $controller_method = $object_called.'_'.$this->request->method;
            // Does this method exist? If not, try executing an index method
            if (!method_exists($this, $controller_method)) {
                $controller_method = "index_" . $this->request->method;
                array_unshift($arguments, $object_called);
            }

            // Do we want to log this method (if allowed by config)?
            $log_method = ! (isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] === FALSE);

            // Use keys for this method?
            $use_key = ! (isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] === FALSE);

            // They provided a key, but it wasn't valid, so get them out of here
            if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE)
            {
                if ($this->config->item('rest_enable_logging') && $log_method)
                {
                    $this->_log_request();
                }

                // fix cross site to option request error 
                if($this->request->method == 'options') {
                    exit;
                }

                $this->response([
                        $this->config->item('rest_status_field_name') => FALSE,
                        $this->config->item('rest_message_field_name') => sprintf($this->lang->line('text_rest_invalid_api_key'), $this->rest->key)
                    ], self::HTTP_FORBIDDEN);
            }

            // Check to see if this key has access to the requested controller
            if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE)
            {
                if ($this->config->item('rest_enable_logging') && $log_method)
                {
                    $this->_log_request();
                }

                $this->response([
                        $this->config->item('rest_status_field_name') => FALSE,
                        $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_unauthorized')
                    ], self::HTTP_UNAUTHORIZED);
            }

            // Sure it exists, but can they do anything with it?
            if (! method_exists($this, $controller_method))
            {
                $this->response([
                        $this->config->item('rest_status_field_name') => FALSE,
                        $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unknown_method')
                    ], self::HTTP_METHOD_NOT_ALLOWED);
            }

            // Doing key related stuff? Can only do it if they have a key right?
            if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE)
            {
                // Check the limit
                if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE)
                {
                    $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_time_limit')];
                    $this->response($response, self::HTTP_UNAUTHORIZED);
                }

                // If no level is set use 0, they probably aren't using permissions
                $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0;

                // If no level is set, or it is lower than/equal to the key's level
                $authorized = $level <= $this->rest->level;
                // IM TELLIN!
                if ($this->config->item('rest_enable_logging') && $log_method)
                {
                    $this->_log_request($authorized);
                }
                if($authorized === FALSE)
                {
                    // They don't have good enough perms
                    $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_permissions')];
                    $this->response($response, self::HTTP_UNAUTHORIZED);
                }
            }

            //check request limit by ip without login
            elseif ($this->config->item('rest_limits_method') == "IP_ADDRESS" && $this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE)
            {
                $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_address_time_limit')];
                $this->response($response, self::HTTP_UNAUTHORIZED);
            }

            // No key stuff, but record that stuff is happening
            elseif ($this->config->item('rest_enable_logging') && $log_method)
            {
                $this->_log_request($authorized = TRUE);
            }

            // Call the controller method and passed arguments
            try
            {
                call_user_func_array([$this, $controller_method], $arguments);
            }
            catch (Exception $ex)
            {
                // If the method doesn't exist, then the error will be caught and an error response shown
                $_error = &load_class('Exceptions', 'core');
                $_error->show_exception($ex);
            }
        }

................
...............
hamidms commented 5 years ago

https://stackoverflow.com/questions/56824024/class-example-cannot-extend-from-trait-restserver-libraries-rest-controller

Thanks. Working perfectly

oncloudatencion commented 5 years ago

Hi, i do it, but i have a question abou the response.

In the example is the next

$this->response([ 'status' => TRUE, 'message' => 'The user has been added successfully.', 'data' => $insert ], REST_Controller::HTTP_OK);

But when the method insert, i don't have the correct response, i received this Fatal error: Undefined class constant 'HTTP_OK'

How to resolve this?

chriskacerguis commented 5 years ago

Version 3.1 should fix this.

oncloudatencion commented 5 years ago

Thanks, the version 3.1 worked successfully!

I have an other cuestion about the controller.

In my case i construct this

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

use Restserver\Libraries\RestController;

require APPPATH . '/libraries/RestController.php';
require APPPATH . '/libraries/Format.php';

class DemoRestserver extends RestController {
}

If i comment the "require", i receive an error Fatal error: Class 'Restserver\Libraries\RestController' not found.

I load the libraries in the config.php, but doen't work

chriskacerguis commented 4 years ago

Based on what you posted, it doesn't look like you followed the install directions. Please give it another try.

oncloudatencion commented 4 years ago

Based on what you posted, it doesn't look like you followed the install directions. Please give it another try.

Ok man, but i don't find the documentation for this version, can you give me the link please?

Thanks a lot!!