代码研磨 Slim v3 (二)--app->run()

APP->run()代码如下:

/**
 * Run application
 *
 * This method traverses the application middleware stack and then sends the
 * resultant Response object to the HTTP client.
 *
 * @param bool|false $silent
 * @return ResponseInterface
 *
 * @throws Exception
 * @throws MethodNotAllowedException
 * @throws NotFoundException
 */
public function run($silent = false)
{
    $request = $this->container->get(‘request‘);
    $response = $this->container->get(‘response‘);

    $response = $this->process($request, $response);

    if (!$silent) {
        $this->respond($response);
    }
    return $response;
}

 $request = $this->container->get(‘request‘);

get已经分析过了,container实现了ArrayAccess ,get其实就是调用offsetGet()。

这个request到底是个什么东东呢?跟一下。

if (!isset($this[‘request‘])) {
    /**
     * PSR-7 Request object
     *
     * @param Container $c
     *
     * @return ServerRequestInterface
     */
    $this[‘request‘] = function ($c) {
        return Request::createFromEnvironment($c->get(‘environment‘));
    };
}

if (!isset($this[‘environment‘])) {
    /**
     * This service MUST return a shared instance
     * of \Slim\Interfaces\Http\EnvironmentInterface.
     *
     * @return EnvironmentInterface
     */
    $this[‘environment‘] = function () {
        return new Environment($_SERVER);
    };
}

首先看到Request是需要一个environment的,$c->get(‘environment‘)生成的是一个Environment对象。其次Environment 是通过$__SERVER构造的对象。

class Environment extends Collection implements EnvironmentInterface

这里可以看出Enviroment是继承了Collection,所有它的数组形式访问可以实现obj[’name’]。同时Environment中实现了EnvironmentInterface接口的mock方法。

了解这个知识点原因在于下面的$c->get(‘environment’);是一个Environment对象,而且在RequestcreateFromEnvironment($c->get(‘environment‘))会获取Environment成员属性的获取。

在Request::createFromEnvironment()中代码:

/**
 * Create new HTTP request with data extracted from the application
 * Environment object
 *
 * @param  Environment $environment The Slim application Environment
 *
 * @return self
 */
public static function createFromEnvironment(Environment $environment)
{
    $method = $environment[‘REQUEST_METHOD‘];
    $uri = Uri::createFromEnvironment($environment);
    var_dump($uri);
    $headers = Headers::createFromEnvironment($environment);
    $cookies = Cookies::parseHeader($headers->get(‘Cookie‘, []));
    $serverParams = $environment->all();
    $body = new RequestBody();
    $uploadedFiles = UploadedFile::createFromEnvironment($environment);

    $request = new static($method, $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);

    if ($method === ‘POST‘ &&
        in_array($request->getMediaType(), [‘application/x-www-form-urlencoded‘, ‘multipart/form-data‘])
    ) {
        // parsed body must be $_POST
        $request = $request->withParsedBody($_POST);
    }
    return $request;
}

$method = $environment[‘REQUEST_METHOD‘];

就是用数组的方法访问了属性REQUEST_METHOD。

‘SCRIPT_NAME‘ => string ‘/example/index.php‘ (length=18)

很好奇这个$__SERVER到底是个什么东西,对其进行打印,结果如下:

array (size=37)

‘REDIRECT_UNIQUE_ID‘ =>string‘Vqrb26wRQjYAAAOax40AAAAB‘(length=24)
  ‘REDIRECT_STATUS‘ =>string‘200‘(length=3)
  ‘UNIQUE_ID‘ =>string‘Vqrb26wRQjYAAAOax40AAAAB‘(length=24)
  ‘HTTP_HOST‘ =>string‘localhost‘(length=9)
  ‘HTTP_CONNECTION‘ =>string‘keep-alive‘(length=10)
  ‘HTTP_CACHE_CONTROL‘ =>string‘max-age=0‘(length=9)
  ‘HTTP_ACCEPT‘ =>string‘text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8‘(length=74)
  ‘HTTP_UPGRADE_INSECURE_REQUESTS‘ =>string‘1‘(length=1)
  ‘HTTP_USER_AGENT‘ =>string‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36‘(length=121)
  ‘HTTP_ACCEPT_ENCODING‘ =>string‘gzip, deflate, sdch‘(length=19)
  ‘HTTP_ACCEPT_LANGUAGE‘ =>string‘zh-CN,zh;q=0.8‘(length=14)
  ‘HTTP_COOKIE‘ =>string‘CNZZDATA5718743=cnzz_eid%3D686564234-1452136358-%26ntime%3D1452136358‘(length=69)
  ‘PATH‘ =>string‘/usr/bin:/bin:/usr/sbin:/sbin‘(length=29)
  ‘DYLD_LIBRARY_PATH‘ =>string‘/Applications/XAMPP/xamppfiles/lib:/Applications/XAMPP/xamppfiles/lib‘(length=69)
  ‘SERVER_SIGNATURE‘ =>string‘‘(length=0)
  ‘SERVER_SOFTWARE‘ =>string‘Apache/2.4.16 (Unix) OpenSSL/1.0.1p PHP/5.6.12 mod_perl/2.0.8-dev Perl/v5.16.3‘(length=78)
  ‘SERVER_NAME‘ =>string‘localhost‘(length=9)
  ‘SERVER_ADDR‘ =>string‘::1‘(length=3)
  ‘SERVER_PORT‘ =>string‘80‘(length=2)
  ‘REMOTE_ADDR‘ =>string‘::1‘(length=3)
  ‘DOCUMENT_ROOT‘ =>string‘/Applications/XAMPP/xamppfiles/htdocs‘(length=37)
  ‘REQUEST_SCHEME‘ =>string‘http‘(length=4)
  ‘CONTEXT_PREFIX‘ =>string‘‘(length=0)
  ‘CONTEXT_DOCUMENT_ROOT‘ =>string‘/Applications/XAMPP/xamppfiles/htdocs‘(length=37)
  ‘SERVER_ADMIN‘ =>string‘[email protected]‘(length=15)
  ‘SCRIPT_FILENAME‘ =>string‘/Applications/XAMPP/xamppfiles/htdocs/example/index.php‘(length=55)
  ‘REMOTE_PORT‘ =>string‘49719‘(length=5)
  ‘REDIRECT_URL‘ =>string‘/example/forbase‘(length=16)
  ‘GATEWAY_INTERFACE‘ =>string‘CGI/1.1‘(length=7)
  ‘SERVER_PROTOCOL‘ =>string‘HTTP/1.1‘(length=8)
  ‘REQUEST_METHOD‘ =>string‘GET‘(length=3)
  ‘QUERY_STRING‘ =>string‘‘(length=0)
  ‘REQUEST_URI‘ =>string‘/example/forbase‘(length=16)
  ‘SCRIPT_NAME‘ =>string‘/example/index.php‘(length=18)
  ‘PHP_SELF‘ =>string‘/example/index.php‘(length=18)
  ‘REQUEST_TIME_FLOAT‘ =>float1454037979.728

‘REQUEST_TIME‘ =>int1454037979

哈哈,东西还真是个多啊。

Request类中用到Uri类:

/**
 * Create new Uri from environment.
 *
 * @param Environment $env
 *
 * @return self
 */
public static function createFromEnvironment(Environment $env)
{
    // Scheme
    $isSecure = $env->get(‘HTTPS‘);
    $scheme = (empty($isSecure) || $isSecure === ‘off‘) ? ‘http‘ : ‘https‘;

    // Authority: Username and password
    $username = $env->get(‘PHP_AUTH_USER‘, ‘‘);
    $password = $env->get(‘PHP_AUTH_PW‘, ‘‘);

    // Authority: Host
    if ($env->has(‘HTTP_HOST‘)) {
        $host = $env->get(‘HTTP_HOST‘);
    } else {
        $host = $env->get(‘SERVER_NAME‘);
    }

    // Authority: Port
    $port = (int)$env->get(‘SERVER_PORT‘, 80);
    if (preg_match(‘/^(\[[a-fA-F0-9:.]+\])(:\d+)?\z/‘, $host, $matches)) {
        $host = $matches[1];

        if ($matches[2]) {
            $port = (int) substr($matches[2], 1);
        }
    } else {
        $pos = strpos($host, ‘:‘);
        if ($pos !== false) {
            $port = (int) substr($host, $pos + 1);
            $host = strstr($host, ‘:‘, true);
        }
    }

    // Path
    //文件名/脚本名
    $requestScriptName = parse_url($env->get(‘SCRIPT_NAME‘), PHP_URL_PATH);

    //其实是项目目录
    $requestScriptDir = dirname($requestScriptName);

    // parse_url() requires a full URL. As we don‘t extract the domain name or scheme,
    // we use a stand-in.

    //就是uri,主机名后面的字符串
    $requestUri = parse_url(‘http://example.com‘ . $env->get(‘REQUEST_URI‘), PHP_URL_PATH);

    $basePath = ‘‘;
    $virtualPath = $requestUri;

    if (stripos($requestUri, $requestScriptName) === 0) {
        $basePath = $requestScriptName;
    } elseif ($requestScriptDir !== ‘/‘ && stripos($requestUri, $requestScriptDir) === 0) {
        $basePath = $requestScriptDir;
    }

    if ($basePath) {
        //就是最后文件名或接口名
        $virtualPath = ltrim(substr($requestUri, strlen($basePath)), ‘/‘);
    }

    // Query string
    $queryString = $env->get(‘QUERY_STRING‘, ‘‘);

    // Fragment
    $fragment = ‘‘;

    // Build Uri
    $uri = new static($scheme, $host, $port, $virtualPath, $queryString, $fragment, $username, $password);
    if ($basePath) {
        $uri = $uri->withBasePath($basePath);
    }

    return $uri;
}

  

最后会组成一个Uri对象,其中的属性如下:

object(Slim\Http\Uri)[26]
  protected ‘scheme‘ => string ‘http‘ (length=4)
  protected ‘user‘ => string ‘‘ (length=0)
  protected ‘password‘ => string ‘‘ (length=0)
  protected ‘host‘ => string ‘localhost‘ (length=9)
  protected ‘port‘ => int 80
  protected ‘basePath‘ => string ‘/example‘ (length=8)
  protected ‘path‘ => string ‘forbase‘ (length=7)
  protected ‘query‘ => string ‘‘ (length=0)

protected ‘fragment‘ => string ‘‘ (length=0)

 

Request类用到了Headers类:

class Headers extends Collection implements HeadersInterface
/**
 * Create new headers collection with data extracted from
 * the application Environment object
 *
 * @param Environment $environment The Slim application Environment
 *
 * @return self
 */
public static function createFromEnvironment(Environment $environment)
{
    $data = [];
    foreach ($environment as $key => $value) {
        $key = strtoupper($key);
        if (isset(static::$special[$key]) || strpos($key, ‘HTTP_‘) === 0) {
            if ($key !== ‘HTTP_CONTENT_LENGTH‘) {
                $data[$key] =  $value;
            }
        }
    }

    return new static($data);
}
/**
 * Set HTTP header value
 *
 * This method sets a header value. It replaces
 * any values that may already exist for the header name.
 *
 * @param string $key   The case-insensitive header name
 * @param string $value The header value
 */
public function set($key, $value)
{
    if (!is_array($value)) {
        $value = [$value];
    }
    parent::set($this->normalizeKey($key), [
        ‘value‘ => $value,
        ‘originalKey‘ => $key
    ]);
}

/**
 * Get HTTP header value
 *
 * @param  string  $key     The case-insensitive header name
 * @param  mixed   $default The default value if key does not exist
 *
 * @return string[]
 */
public function get($key, $default = null)
{
    if ($this->has($key)) {
        return parent::get($this->normalizeKey($key))[‘value‘];
    }

    return $default;
}

将与HTTP相关的头都包含进来,最后通过static创建一个Header对象,同时在Headers复写Collection的set()和get()方法并实现了HeadersInterface的add()和normalizeKey()方法。

所以结果如下:

object(Slim\Http\Headers)[27]
  protected ‘data‘ => 
    array (size=9)
      ‘host‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_HOST‘ (length=9)
      ‘connection‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_CONNECTION‘ (length=15)
      ‘cache-control‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_CACHE_CONTROL‘ (length=18)
      ‘accept‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_ACCEPT‘ (length=11)
      ‘upgrade-insecure-requests‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_UPGRADE_INSECURE_REQUESTS‘ (length=30)
      ‘user-agent‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_USER_AGENT‘ (length=15)
      ‘accept-encoding‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_ACCEPT_ENCODING‘ (length=20)
      ‘accept-language‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...
          ‘originalKey‘ => string ‘HTTP_ACCEPT_LANGUAGE‘ (length=20)
      ‘cookie‘ => 
        array (size=2)
          ‘value‘ => 
            array (size=1)
              ...

‘originalKey‘ => string ‘HTTP_COOKIE‘ (length=11)

 

Request 类用到了Cookies类

class Cookies implements CookiesInterface
/**
 * Parse HTTP request `Cookie:` header and extract
 * into a PHP associative array.
 *
 * @param  string $header The raw HTTP request `Cookie:` header
 *
 * @return array Associative array of cookie names and values
 *
 * @throws InvalidArgumentException if the cookie data cannot be parsed
 */
public static function parseHeader($header)
{
    if (is_array($header) === true) {
        $header = isset($header[0]) ? $header[0] : ‘‘;
    }

    if (is_string($header) === false) {
        throw new InvalidArgumentException(‘Cannot parse Cookie data. Header value must be a string.‘);
    }

    $header = rtrim($header, "\r\n");
    $pieces = preg_split(‘@\s*[;,]\s*@‘, $header);

    $cookies = [];

    foreach ($pieces as $cookie) {
        $cookie = explode(‘=‘, $cookie, 2);

        if (count($cookie) === 2) {
            $key = urldecode($cookie[0]);
            $value = urldecode($cookie[1]);

            if (!isset($cookies[$key])) {
                $cookies[$key] = $value;
            }
        }
    }
    return $cookies;
}

打印了下当前请求的Cookie,如下:

‘HTTP_COOKIE‘ => string ‘CNZZDATA5718743=cnzz_eid%3D686564234-1452136358-%26ntime%3D1452136358‘ (length=69)

在其被处理后就变成了这个样子:

array (size=1)

‘CNZZDATA5718743‘ => string ‘cnzz_eid=686564234-1452136358-&ntime=1452136358‘ (length=47)

原始COOKIE和parse后的Cookie。其实可以看到就是一个 XX=xx的形式。

解码中%后面是个16进制的值,通过ASCII码就能找到字符了。%3D 的十进制是61,61在ASCII码里是 ‘=‘。

Request 类还用到了RequestBody,

//创建了一个body stream ,stream 指向php://input,这个php://input可以读取原始的POST数据
$body = new RequestBody();

其实是用来获取原始POST数据的。

public function __construct(
        $method,
        UriInterface $uri,
        HeadersInterface $headers,
        array $cookies,
        array $serverParams,
        StreamInterface $body,
        array $uploadedFiles = []
    ) {
        $this->originalMethod = $this->filterMethod($method);
        $this->uri = $uri;
        $this->headers = $headers;
        $this->cookies = $cookies;
        $this->serverParams = $serverParams;
        $this->attributes = new Collection();
        $this->body = $body;
        $this->uploadedFiles = $uploadedFiles;

        if (isset($serverParams[‘SERVER_PROTOCOL‘])) {
            $this->protocolVersion = str_replace(‘HTTP/‘, ‘‘, $serverParams[‘SERVER_PROTOCOL‘]);
        }

        if (!$this->headers->has(‘Host‘) || $this->uri->getHost() !== ‘‘) {
            $this->headers->set(‘Host‘, $this->uri->getHost());
        }

        $this->registerMediaTypeParser(‘application/json‘, function ($input) {
            return json_decode($input, true);
        });

        $this->registerMediaTypeParser(‘application/xml‘, function ($input) {
            $backup = libxml_disable_entity_loader(true);
            $result = simplexml_load_string($input);
            libxml_disable_entity_loader($backup);
            return $result;
        });

        $this->registerMediaTypeParser(‘text/xml‘, function ($input) {
            $backup = libxml_disable_entity_loader(true);
            $result = simplexml_load_string($input);
            libxml_disable_entity_loader($backup);
            return $result;
        });

        $this->registerMediaTypeParser(‘application/x-www-form-urlencoded‘, function ($input) {
            parse_str($input, $data);
            return $data;
        });
    }

这样一个request就妥当了,而且含有method、body等数据。

时间: 2024-10-11 07:32:10

代码研磨 Slim v3 (二)--app->run()的相关文章

【翻】Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏

译者地址:[翻]Android Design Support Library 的 代码实验--几行代码,让你的 APP 变得花俏 原文:Codelab for Android Design Support Library used in I/O Rewind Bangkok session--Make your app fancy with few lines of code 原文项目 demo: Lab-Android-DesignLibrary 双语对照地址: [翻-双语]Android D

Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏

译者地址:[翻]Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏 原文:Codelab for Android Design Support Library used in I/O Rewind Bangkok session----Make your app fancy with few lines of code 原文项目 demo: Lab-Android-DesignLibrary 双语对照地址: [翻-双语]Android

【转】【翻】Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏

转自:http://mrfufufu.github.io/android/2015/07/01/Codelab_Android_Design_Support_Library.html [翻]Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏 Jul 1, 2015 译者地址:[翻]Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏 原文:Codelab for Androi

debian内核代码执行流程(二)

继续上一篇文章<debian内核代码执行流程(一)>未完成部分. acpi_bus_init调用acpi_initialize_objects,经过一系列复杂调用后输出下面信息: [ 0.147393] ACPI: SSDT 7f5e7cc0 0030F (v01 PmRef Cpu0Ist 00003000 INTL 20060912) [ 0.147566] ACPI: Dynamic OEM Table Load: [ 0.147569] ACPI: SSDT (null) 0030F

app.run相关参数与flask配置文件

app.run相关参数 1.指定访问的网站地址的方法: (加入host的参数指定当前机器的ip) app.run(host="192.168.1.109",debug=True) 2.如果机器存在多个网卡或代码放到另外一台机器,让他智能识别 app.run(host="0.0.0.0",debug=True) 3.修改访问的端口号的方法:(加入port的参数指定端口) app.run(host="192.168.1.109",debug=True

Android--新手必备的常用代码片段整理(二)

收集设备信息用于信息统计分析 是否有SD卡 动态隐藏软键盘 动态显示软键盘 动态显示或者是隐藏软键盘 主动回到Home后台运行 获取状态栏高度 获取状态栏高度标题栏ActionBar高度 获取MCCMNC代码 SIM卡运营商国家代码和运营商网络代码 返回移动网络运营商的名字 返回移动终端类型 判断手机连接的网络类型2G3G4G 判断当前手机的网络类型WIFI还是234G 收集设备信息,用于信息统计分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

基于PHP实战ThinkSNS V3二次开发

基于PHP实战ThinkSNS V3二次开发(系统架构.网站定制化开发.问答系统开发) 课程分类:PHP 适合人群:中级 课时数量:25课时 服务类型:C类(普通服务类课程) 用到技术:PHP.ThinkSNS V3二次开发 涉及项目:ThinkSNS V3二次开发.问答系统 咨询QQ:1840215592 课程内容简介: 由于ThinkSNS产品提供了完整的微博及系统功能,并提供了非常友好的二次开发规范.越多越多的企业和开发人员选择使用ThinkSNS构建成熟的SNS企业网站和商业项目.本课程

if __name__ == &quot;__main__&quot;: tf.app.run()

if __name__ == "__main__": means current file is executed under a shell instead of imported as a module. the job of tf.app.run() is to first set the global flags for later usage like from tensorflow.python.platform import flags f = flags.FLAGS a

创建Flask实例对象时的参数和app.run()中的参数

app=Flask(name,static_folder="static",static_url_path="/aaa",template_folder="templates") __name__实际上是有值的,name=main,指的是该模块所在的目录 static_folder = "static",设置静态资源夹的名字是static static_url_path,设置访问静态资源的url前缀,若不存在该项,默认为sta