<?php/*** This file is part of CodeIgniter 4 framework.** (c) CodeIgniter Foundation <admin@codeigniter.com>** For the full copyright and license information, please view* the LICENSE file that was distributed with this source code.*/namespace CodeIgniter;use Closure;use CodeIgniter\Cache\ResponseCache;use CodeIgniter\Debug\Timer;use CodeIgniter\Events\Events;use CodeIgniter\Exceptions\LogicException;use CodeIgniter\Exceptions\PageNotFoundException;use CodeIgniter\Filters\Filters;use CodeIgniter\HTTP\CLIRequest;use CodeIgniter\HTTP\DownloadResponse;use CodeIgniter\HTTP\Exceptions\RedirectException;use CodeIgniter\HTTP\IncomingRequest;use CodeIgniter\HTTP\Method;use CodeIgniter\HTTP\RedirectResponse;use CodeIgniter\HTTP\Request;use CodeIgniter\HTTP\RequestInterface;use CodeIgniter\HTTP\ResponsableInterface;use CodeIgniter\HTTP\ResponseInterface;use CodeIgniter\HTTP\URI;use CodeIgniter\Router\RouteCollectionInterface;use CodeIgniter\Router\Router;use Config\App;use Config\Cache;use Config\Feature;use Config\Kint as KintConfig;use Config\Services;use Exception;use Kint;use Kint\Renderer\CliRenderer;use Kint\Renderer\RichRenderer;use Locale;use Throwable;/*** This class is the core of the framework, and will analyse the* request, route it to a controller, and send back the response.* Of course, there are variations to that flow, but this is the brains.** @see \CodeIgniter\CodeIgniterTest*/class CodeIgniter{/*** The current version of CodeIgniter Framework*/public const CI_VERSION = '4.7.4-dev';/*** App startup time.** @var float|null*/protected $startTime;/*** Total app execution time** @var float*/protected $totalTime;/*** Main application configuration** @var App*/protected $config;/*** Timer instance.** @var Timer*/protected $benchmark;/*** Current request.** @var CLIRequest|IncomingRequest|null*/protected $request;/*** Current response.** @var ResponseInterface|null*/protected $response;/*** Router to use.** @var Router|null*/protected $router;/*** Controller to use.** @var (Closure(mixed...): ResponseInterface|string)|string|null*/protected $controller;/*** Controller method to invoke.** @var string|null*/protected $method;/*** Output handler to use.** @var string|null*/protected $output;/*** Cache expiration time** @var int seconds** @deprecated 4.4.0 Moved to ResponseCache::$ttl. No longer used.*/protected static $cacheTTL = 0;/*** Context* web: Invoked by HTTP request* php-cli: Invoked by CLI via `php public/index.php`** @var 'php-cli'|'web'|null*/protected ?string $context = null;/*** Whether to enable Control Filters.*/protected bool $enableFilters = true;/*** Whether to return Response object or send response.** @deprecated 4.4.0 No longer used.*/protected bool $returnResponse = false;/*** Application output buffering level*/protected int $bufferLevel;/*** Web Page Caching*/protected ResponseCache $pageCache;/*** Constructor.*/public function __construct(App $config){$this->startTime = microtime(true);$this->config = $config;$this->pageCache = Services::responsecache();}/*** Handles some basic app and environment setup.** @return void*/public function initialize(){// Set default locale on the serverLocale::setDefault($this->config->defaultLocale);// Set default timezone on the serverdate_default_timezone_set($this->config->appTimezone);}/*** Reset request-specific state for worker mode.* Clears all request/response data to prepare for the next request.*/public function resetForWorkerMode(): void{$this->request = null;$this->response = null;$this->router = null;$this->controller = null;$this->method = null;$this->output = null;// Reset timing$this->startTime = null;$this->totalTime = 0;$this->resetKintForWorkerMode();}/*** Resets Kint request-specific state for worker mode.*/private function resetKintForWorkerMode(): void{if (! CI_DEBUG || ! class_exists(Kint::class, false)) {return;}$csp = service('csp');if ($csp->enabled()) {RichRenderer::$js_nonce = $csp->getScriptNonce();RichRenderer::$css_nonce = $csp->getStyleNonce();} else {RichRenderer::$js_nonce = null;RichRenderer::$css_nonce = null;}RichRenderer::$needs_pre_render = true;}/*** Initializes Kint** @return void** @deprecated 4.5.0 Moved to Autoloader.*/protected function initializeKint(){if (CI_DEBUG) {$this->autoloadKint();$this->configureKint();} elseif (class_exists(Kint::class)) {// In case that Kint is already loaded via Composer.Kint::$enabled_mode = false;// @codeCoverageIgnore}helper('kint');}/*** @deprecated 4.5.0 Moved to Autoloader.*/private function autoloadKint(): void{// If we have KINT_DIR it means it's already loaded via composerif (! defined('KINT_DIR')) {spl_autoload_register(function ($class): void {$class = explode('\\', $class);if (array_shift($class) !== 'Kint') {return;}$file = SYSTEMPATH . 'ThirdParty/Kint/' . implode('/', $class) . '.php';if (is_file($file)) {require_once $file;}});require_once SYSTEMPATH . 'ThirdParty/Kint/init.php';}}/*** @deprecated 4.5.0 Moved to Autoloader.*/private function configureKint(): void{$config = new KintConfig();Kint::$depth_limit = $config->maxDepth;Kint::$display_called_from = $config->displayCalledFrom;Kint::$expanded = $config->expanded;if (isset($config->plugins) && is_array($config->plugins)) {Kint::$plugins = $config->plugins;}$csp = Services::csp();if ($csp->enabled()) {RichRenderer::$js_nonce = $csp->getScriptNonce();RichRenderer::$css_nonce = $csp->getStyleNonce();}RichRenderer::$theme = $config->richTheme;RichRenderer::$folder = $config->richFolder;if (isset($config->richObjectPlugins) && is_array($config->richObjectPlugins)) {RichRenderer::$value_plugins = $config->richObjectPlugins;}if (isset($config->richTabPlugins) && is_array($config->richTabPlugins)) {RichRenderer::$tab_plugins = $config->richTabPlugins;}CliRenderer::$cli_colors = $config->cliColors;CliRenderer::$force_utf8 = $config->cliForceUTF8;CliRenderer::$detect_width = $config->cliDetectWidth;CliRenderer::$min_terminal_width = $config->cliMinWidth;}/*** Launch the application!** This is "the loop" if you will. The main entry point into the script* that gets the required class instances, fires off the filters,* tries to route the response, loads the controller and generally* makes all the pieces work together.** @param bool $returnResponse Used for testing purposes only.** @return ResponseInterface|null*/public function run(?RouteCollectionInterface $routes = null, bool $returnResponse = false){if ($this->context === null) {throw new LogicException('Context must be set before run() is called. If you are upgrading from 4.1.x, '. 'you need to merge `public/index.php` and `spark` file from `vendor/codeigniter4/framework`.',);}$this->pageCache->setTtl(0);$this->bufferLevel = ob_get_level();$this->startBenchmark();$this->getRequestObject();$this->getResponseObject();Events::trigger('pre_system');$this->benchmark->stop('bootstrap');$this->benchmark->start('required_before_filters');// Start up the filters$filters = Services::filters();// Run required before filters$possibleResponse = $this->runRequiredBeforeFilters($filters);// If a ResponseInterface instance is returned then send it back to the client and stopif ($possibleResponse instanceof ResponseInterface) {$this->response = $possibleResponse;} else {try {$this->response = $this->handleRequest($routes, config(Cache::class), $returnResponse);} catch (ResponsableInterface $e) {$this->outputBufferingEnd();$this->response = $e->getResponse();} catch (PageNotFoundException $e) {$this->response = $this->display404errors($e);} catch (Throwable $e) {$this->outputBufferingEnd();throw $e;}}$this->runRequiredAfterFilters($filters);// Is there a post-system event?Events::trigger('post_system');if ($returnResponse) {return $this->response;}$this->sendResponse();return null;}/*** Run required before filters.*/private function runRequiredBeforeFilters(Filters $filters): ?ResponseInterface{$possibleResponse = $filters->runRequired('before');$this->benchmark->stop('required_before_filters');// If a ResponseInterface instance is returned then send it back to the client and stopif ($possibleResponse instanceof ResponseInterface) {return $possibleResponse;}return null;}/*** Run required after filters.*/private function runRequiredAfterFilters(Filters $filters): void{$filters->setResponse($this->response);// Run required after filters$this->benchmark->start('required_after_filters');$response = $filters->runRequired('after');$this->benchmark->stop('required_after_filters');if ($response instanceof ResponseInterface) {$this->response = $response;}}/*** Invoked via php-cli command?*/private function isPhpCli(): bool{return $this->context === 'php-cli';}/*** Web access?*/private function isWeb(): bool{return $this->context === 'web';}/*** Disables Controller Filters.*/public function disableFilters(): void{$this->enableFilters = false;}/*** Handles the main request logic and fires the controller.** @return ResponseInterface** @throws PageNotFoundException* @throws RedirectException** @deprecated $returnResponse is deprecated.*/protected function handleRequest(?RouteCollectionInterface $routes, Cache $cacheConfig, bool $returnResponse = false){if ($this->request instanceof IncomingRequest && $this->request->getMethod() === 'CLI') {return $this->response->setStatusCode(405)->setBody('Method Not Allowed');}$routeFilters = $this->tryToRouteIt($routes);// $uri is URL-encoded.$uri = $this->request->getPath();if ($this->enableFilters) {/** @var Filters $filters */$filters = service('filters');// If any filters were specified within the routes file,// we need to ensure it's active for the current requestif ($routeFilters !== null) {$filters->enableFilters($routeFilters, 'before');$oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false; // @phpstan-ignore nullCoalesce.propertyif (! $oldFilterOrder) {$routeFilters = array_reverse($routeFilters);}$filters->enableFilters($routeFilters, 'after');}// Run "before" filters$this->benchmark->start('before_filters');$possibleResponse = $filters->run($uri, 'before');$this->benchmark->stop('before_filters');// If a ResponseInterface instance is returned then send it back to the client and stopif ($possibleResponse instanceof ResponseInterface) {$this->outputBufferingEnd();return $possibleResponse;}if ($possibleResponse instanceof IncomingRequest || $possibleResponse instanceof CLIRequest) {$this->request = $possibleResponse;}}$returned = $this->startController();// If startController returned a Response (from an attribute or Closure), use itif ($returned instanceof ResponseInterface) {$this->gatherOutput($cacheConfig, $returned);}// Closure controller has run in startController().elseif (! is_callable($this->controller)) {$controller = $this->createController();if (! method_exists($controller, '_remap') && ! is_callable([$controller, $this->method], false)) {throw PageNotFoundException::forMethodNotFound($this->method);}// Is there a "post_controller_constructor" event?Events::trigger('post_controller_constructor');$returned = $this->runController($controller);} else {$this->benchmark->stop('controller_constructor');$this->benchmark->stop('controller');}// If $returned is a string, then the controller output something,// probably a view, instead of echoing it directly. Send it along// so it can be used with the output.$this->gatherOutput($cacheConfig, $returned);if ($this->enableFilters) {/** @var Filters $filters */$filters = service('filters');$filters->setResponse($this->response);// Run "after" filters$this->benchmark->start('after_filters');$response = $filters->run($uri, 'after');$this->benchmark->stop('after_filters');if ($response instanceof ResponseInterface) {$this->response = $response;}}// Execute controller attributes' after() methods AFTER framework filtersif ((config('Routing')->useControllerAttributes ?? true) === true) { // @phpstan-ignore nullCoalesce.property$this->benchmark->start('route_attributes_after');$this->response = $this->router->executeAfterAttributes($this->request, $this->response);$this->benchmark->stop('route_attributes_after');}// Skip unnecessary processing for special Responses.if (! $this->response instanceof DownloadResponse&& ! $this->response instanceof RedirectResponse) {// Save our current URI as the previous URI in the session// for safer, more accurate use with `previous_url()` helper function.$this->storePreviousURL(current_url(true));}unset($uri);return $this->response;}/*** You can load different configurations depending on your* current environment. Setting the environment also influences* things like logging and error reporting.** This can be set to anything, but default usage is:** development* testing* production** @codeCoverageIgnore** @return void** @deprecated 4.4.0 No longer used. Moved to index.php and spark.*/protected function detectEnvironment(){// Make sure ENVIRONMENT isn't already set by other means.if (! defined('ENVIRONMENT')) {define('ENVIRONMENT', env('CI_ENVIRONMENT', 'production'));}}/*** Load any custom boot files based upon the current environment.** If no boot file exists, we shouldn't continue because something* is wrong. At the very least, they should have error reporting setup.** @return void** @deprecated 4.5.0 Moved to system/bootstrap.php.*/protected function bootstrapEnvironment(){if (is_file(APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php')) {require_once APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';} else {// @codeCoverageIgnoreStartheader('HTTP/1.1 503 Service Unavailable.', true, 503);echo 'The application environment is not set correctly.';exit(EXIT_ERROR); // EXIT_ERROR// @codeCoverageIgnoreEnd}}/*** Start the Benchmark** The timer is used to display total script execution both in the* debug toolbar, and potentially on the displayed page.** @return void*/protected function startBenchmark(){if ($this->startTime === null) {$this->startTime = microtime(true);}$this->benchmark = Services::timer();$this->benchmark->start('total_execution', $this->startTime);$this->benchmark->start('bootstrap');}/*** Sets a Request object to be used for this request.* Used when running certain tests.** @param CLIRequest|IncomingRequest $request** @return $this** @internal Used for testing purposes only.* @testTag*/public function setRequest($request){$this->request = $request;return $this;}/*** Get our Request object, (either IncomingRequest or CLIRequest).** @return void*/protected function getRequestObject(){if ($this->request instanceof Request) {$this->spoofRequestMethod();return;}if ($this->isPhpCli()) {Services::createRequest($this->config, true);} else {Services::createRequest($this->config);}$this->request = service('request');$this->spoofRequestMethod();}/*** Get our Response object, and set some default values, including* the HTTP protocol version and a default successful response.** @return void*/protected function getResponseObject(){$this->response = Services::response($this->config);if ($this->isWeb()) {$this->response->setProtocolVersion($this->request->getProtocolVersion());}// Assume success until proven otherwise.$this->response->setStatusCode(200);}/*** Force Secure Site Access? If the config value 'forceGlobalSecureRequests'* is true, will enforce that all requests to this site are made through* HTTPS. Will redirect the user to the current page with HTTPS, as well* as set the HTTP Strict Transport Security header for those browsers* that support it.** @param int $duration How long the Strict Transport Security* should be enforced for this URL.** @return void** @deprecated 4.5.0 No longer used. Moved to ForceHTTPS filter.*/protected function forceSecureAccess($duration = 31_536_000){if ($this->config->forceGlobalSecureRequests !== true) {return;}force_https($duration, $this->request, $this->response);}/*** Determines if a response has been cached for the given URI.** @return false|ResponseInterface** @throws Exception** @deprecated 4.5.0 PageCache required filter is used. No longer used.* @deprecated 4.4.2 The parameter $config is deprecated. No longer used.*/public function displayCache(Cache $config){$cachedResponse = $this->pageCache->get($this->request, $this->response);if ($cachedResponse instanceof ResponseInterface) {$this->response = $cachedResponse;$this->totalTime = $this->benchmark->getElapsedTime('total_execution');$output = $this->displayPerformanceMetrics($cachedResponse->getBody());$this->response->setBody($output);return $this->response;}return false;}/*** Tells the app that the final output should be cached.** @deprecated 4.4.0 Moved to ResponseCache::setTtl(). No longer used.** @return void*/public static function cache(int $time){static::$cacheTTL = $time;}/*** Caches the full response from the current request. Used for* full-page caching for very high performance.** @return bool** @deprecated 4.4.0 No longer used.*/public function cachePage(Cache $config){$headers = [];foreach ($this->response->headers() as $header) {$headers[$header->getName()] = $header->getValueLine();}return cache()->save($this->generateCacheName($config), serialize(['headers' => $headers, 'output' => $this->output]), static::$cacheTTL);}/*** Returns an array with our basic performance stats collected.*/public function getPerformanceStats(): array{// After filter debug toolbar requires 'total_execution'.$this->totalTime = $this->benchmark->getElapsedTime('total_execution');return ['startTime' => $this->startTime,'totalTime' => $this->totalTime,];}/*** Generates the cache name to use for our full-page caching.** @deprecated 4.4.0 No longer used.*/protected function generateCacheName(Cache $config): string{if ($this->request instanceof CLIRequest) {return md5($this->request->getPath());}$uri = clone $this->request->getUri();$query = $config->cacheQueryString? $uri->getQuery(is_array($config->cacheQueryString) ? ['only' => $config->cacheQueryString] : []): '';return md5((string) $uri->setFragment('')->setQuery($query));}/*** Replaces the elapsed_time and memory_usage tag.** @deprecated 4.5.0 PerformanceMetrics required filter is used. No longer used.*/public function displayPerformanceMetrics(string $output): string{return str_replace(['{elapsed_time}', '{memory_usage}'],[(string) $this->totalTime, number_format(memory_get_peak_usage() / 1024 / 1024, 3)],$output,);}/*** Try to Route It - As it sounds like, works with the router to* match a route against the current URI. If the route is a* "redirect route", will also handle the redirect.** @param RouteCollectionInterface|null $routes A collection interface to use in place* of the config file.** @return list<string>|string|null Route filters, that is, the filters specified in the routes file** @throws RedirectException*/protected function tryToRouteIt(?RouteCollectionInterface $routes = null){$this->benchmark->start('routing');if (! $routes instanceof RouteCollectionInterface) {$routes = service('routes')->loadRoutes();}// $routes is defined in Config/Routes.php$this->router = Services::router($routes, $this->request);// $uri is URL-encoded.$uri = $this->request->getPath();$this->outputBufferingStart();$this->controller = $this->router->handle($uri);$this->method = $this->router->methodName();// If a {locale} segment was matched in the final route,// then we need to set the correct locale on our Request.if ($this->router->hasLocale()) {$this->request->setLocale($this->router->getLocale());}$this->benchmark->stop('routing');return $this->router->getFilters();}/*** Determines the path to use for us to try to route to, based* on the CLI/IncomingRequest path.** @return string** @deprecated 4.5.0 No longer used.*/protected function determinePath(){return $this->request->getPath();}/*** Now that everything has been setup, this method attempts to run the* controller method and make the script go. If it's not able to, will* show the appropriate Page Not Found error.** @return ResponseInterface|string|null*/protected function startController(){$this->benchmark->start('controller');$this->benchmark->start('controller_constructor');// Is it routed to a Closure?if (is_object($this->controller) && ($this->controller::class === 'Closure')) {$controller = $this->controller;return $controller(...$this->router->params());}// No controller specified - we don't know what to do now.if (! isset($this->controller)) {throw PageNotFoundException::forEmptyController();}// Try to autoload the classif (! class_exists($this->controller, true)|| ($this->method[0] === '_' && $this->method !== '__invoke')) {throw PageNotFoundException::forControllerNotFound($this->controller, $this->method);}// Execute route attributes' before() methods// This runs after routing/validation but BEFORE expensive controller instantiationif ((config('Routing')->useControllerAttributes ?? true) === true) { // @phpstan-ignore nullCoalesce.property$this->benchmark->start('route_attributes_before');$attributeResponse = $this->router->executeBeforeAttributes($this->request);$this->benchmark->stop('route_attributes_before');// If attribute returns a Response, short-circuitif ($attributeResponse instanceof ResponseInterface) {$this->benchmark->stop('controller_constructor');$this->benchmark->stop('controller');return $attributeResponse;}// If attribute returns a modified Request, use itif ($attributeResponse instanceof RequestInterface) {$this->request = $attributeResponse;}}return null;}/*** Instantiates the controller class.** @return Controller*/protected function createController(){assert(is_string($this->controller));$class = new $this->controller();$class->initController($this->request, $this->response, Services::logger());$this->benchmark->stop('controller_constructor');return $class;}/*** Runs the controller, allowing for _remap methods to function.** CI4 supports three types of requests:* 1. Web: URI segments become parameters, sent to Controllers via Routes,* output controlled by Headers to browser* 2. PHP CLI: accessed by CLI via php public/index.php, arguments become URI segments,* sent to Controllers via Routes, output varies** @param Controller $class** @return false|ResponseInterface|string|void*/protected function runController($class){// This is a Web request or PHP CLI request$params = $this->router->params();// The controller method param types may not be string.// So cannot set `declare(strict_types=1)` in this file.$output = method_exists($class, '_remap')? $class->_remap($this->method, ...$params): $class->{$this->method}(...$params);$this->benchmark->stop('controller');return $output;}/*** Displays a 404 Page Not Found error. If set, will try to* call the 404Override controller/method that was set in routing config.** @return ResponseInterface|void*/protected function display404errors(PageNotFoundException $e){$this->response->setStatusCode($e->getCode());// Is there a 404 Override available?$override = $this->router->get404Override();if ($override !== null) {$returned = null;if ($override instanceof Closure) {echo $override($e->getMessage());} elseif (is_array($override)) {$this->benchmark->start('controller');$this->benchmark->start('controller_constructor');$this->controller = $override[0];$this->method = $override[1];$controller = $this->createController();$returned = $controller->{$this->method}($e->getMessage());$this->benchmark->stop('controller');}unset($override);$cacheConfig = config(Cache::class);$this->gatherOutput($cacheConfig, $returned);return $this->response;}$this->outputBufferingEnd();// Throws new PageNotFoundException and remove exception message on production.throw PageNotFoundException::forPageNotFound((ENVIRONMENT !== 'production' || ! $this->isWeb()) ? $e->getMessage() : null,);}/*** Gathers the script output from the buffer, replaces some execution* time tag in the output and displays the debug toolbar, if required.** @param Cache|null $cacheConfig Deprecated. No longer used.* @param ResponseInterface|string|null $returned** @deprecated $cacheConfig is deprecated.** @return void*/protected function gatherOutput(?Cache $cacheConfig = null, $returned = null){$this->output = $this->outputBufferingEnd();if ($returned instanceof DownloadResponse) {$this->response = $returned;return;}// If the controller returned a response object,// we need to grab the body from it so it can// be added to anything else that might have been// echoed already.// We also need to save the instance locally// so that any status code changes, etc, take place.if ($returned instanceof ResponseInterface) {$this->response = $returned;$returned = $returned->getBody();}if (is_string($returned)) {$this->output .= $returned;}$this->response->setBody($this->output);}/*** If we have a session object to use, store the current URI* as the previous URI. This is called just prior to sending the* response to the client, and will make it available next request.** This helps provider safer, more reliable previous_url() detection.** @param string|URI $uri** @return void*/public function storePreviousURL($uri){// Ignore CLI requestsif (! $this->isWeb()) {return;}// Ignore AJAX requestsif (method_exists($this->request, 'isAJAX') && $this->request->isAJAX()) {return;}// Ignore unroutable responsesif ($this->response instanceof DownloadResponse || $this->response instanceof RedirectResponse) {return;}// Ignore non-HTML responsesif (! str_contains($this->response->getHeaderLine('Content-Type'), 'text/html')) {return;}// This is mainly needed during testing...if (is_string($uri)) {$uri = new URI($uri);}if (isset($_SESSION)) {session()->set('_ci_previous_url', URI::createURIString($uri->getScheme(),$uri->getAuthority(),$uri->getPath(),$uri->getQuery(),$uri->getFragment(),));}}/*** Modifies the Request Object to use a different method if a POST* variable called _method is found.** @return void*/public function spoofRequestMethod(){// Only works with POSTED formsif ($this->request->getMethod() !== Method::POST) {return;}$method = $this->request->getPost('_method');if ($method === null) {return;}// Only allows PUT, PATCH, DELETEif (in_array($method, [Method::PUT, Method::PATCH, Method::DELETE], true)) {$this->request = $this->request->setMethod($method);}}/*** Sends the output of this request back to the client.* This is what they've been waiting for!** @return void*/protected function sendResponse(){$this->response->send();}/*** Exits the application, setting the exit code for CLI-based applications* that might be watching.** Made into a separate method so that it can be mocked during testing* without actually stopping script execution.** @param int $code** @deprecated 4.4.0 No longer Used. Moved to index.php.** @return void*/protected function callExit($code){exit($code); // @codeCoverageIgnore}/*** Sets the app context.** @param 'php-cli'|'web' $context** @return $this*/public function setContext(string $context){$this->context = $context;return $this;}protected function outputBufferingStart(): void{$this->bufferLevel = ob_get_level();ob_start();}protected function outputBufferingEnd(): string{$buffer = '';while (ob_get_level() > $this->bufferLevel) {$buffer .= ob_get_contents();ob_end_clean();}return $buffer;}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。