gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

Slim docsの解析; The Request

https://www.slimframework.com/docs/v3/objects/request.html
多分、ここは大物w


とりあえずルーティング設定。
関数とか「設定できる」んだろうけどやる気はないんで、無視w


The Request Method
………まぁ一通り。GET,PUT,POST,DELETEくらいしか使う気ないしなぁ。


で、メソッド指定2種。

You can include a _METHOD parameter in a POST request’s body. The HTTP request must use the application/x-www-form-urlencoded content type.

POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 22

data=value&_METHOD=PUT

You can also override the HTTP request method with a custom X-Http-Method-Override HTTP request header. This works with any HTTP request content type.

POST /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length: 16
X-Http-Method-Override: PUT

{"data":"value"}


ふむ……この辺、お他所はどうだっただろさね??
ちぃと実験コード書いてみるかねぇ。
実験目的としては
・そもそも、ちゃんと「PUT」とかのメソッドでcallした時はどーゆールーティングになるのか
・上述のような「メソッドと上書きメソッド」の時の動き
あたり。


まずは、壮絶に雑なコードを一筆。
public/index.php

$app = new \Slim\App;
$app->get('/', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, get\n");
    return $response;
});
$app->post('/', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, post\n");
    return $response;
});
$app->put('/', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, put\n");
    return $response;
});
$app->delete('/', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, delete\n");
    return $response;
});


んで、おおざっぱにcall。

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:14:49 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 11

Hello, get
Connection closed by foreign host.

よし。


では、PUTで試し。

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
PUT / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:15:49 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 11

Hello, put
Connection closed by foreign host.

ふむりやっぱり無問題。
一応、DELETEも。

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
DELETE / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:16:16 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 14

Hello, delete
Connection closed by foreign host.

OK。


で、これに「糅てて加えて」別フォーマットがあるのか。
ちと修正して。

$app = new \Slim\App;
$app->get('/path', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, get\n" . 'getMethod: ' . $request->getMethod() . "\n" . 'getOriginalMethod: ' . $request->getOriginalMethod() . "\n");
    return $response;
});
$app->post('/path', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, post\n" . 'getMethod: ' . $request->getMethod() . "\n" . 'getOriginalMethod: ' . $request->getOriginalMethod() . "\n");
    return $response;
});
$app->put('/path', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, put\n" . 'getMethod: ' . $request->getMethod() . "\n" . 'getOriginalMethod: ' . $request->getOriginalMethod() . "\n");
    return $response;
});
$app->delete('/path', function(Request $request, Response $response, array $args) {
    $response->getBody()->write("Hello, delete\n" . 'getMethod: ' . $request->getMethod() . "\n" . 'getOriginalMethod: ' . $request->getOriginalMethod() . "\n");
    return $response;
});


サンプルに出ているのを、そのまま。

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 22

data=value&_METHOD=PUT
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:20:52 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 50

Hello, put
getMethod: PUT
getOriginalMethod: POST
Connection closed by foreign host.

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length: 16
X-Http-Method-Override: PUT

{"data":"value"}
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:21:29 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 50

Hello, put
getMethod: PUT
getOriginalMethod: POST
Connection closed by foreign host.

ふむ面白い。
個人的にはX-Http-Method-Overrideヘッダのほうが好きかなぁ、なんとなく。


………ちと、好奇心。

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 22
X-Http-Method-Override: DELETE

data=value&_METHOD=PUT
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:23:29 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 56

Hello, delete
getMethod: DELETE
getOriginalMethod: POST
Connection closed by foreign host.

ヘッダのほうが強いのかしらん?
まぁあんまりない事象だとは思うんだけど、後々用に、軽く実験。


お次はThe Request URI

The PSR 7 Request object’s URI is itself an object that provides the following methods to inspect the HTTP request’s URL parts:

    getScheme()
    getAuthority()
    getUserInfo()
    getHost()
    getPort()
    getPath()
    getBasePath()
    getQuery() (returns the full query string, e.g. a=1&b=2)
    getFragment()
    getBaseUrl()

へぇ。色々あるのか。

You can get the query parameters as an associative array on the Request object using getQueryParams().

これは多分使わないかなぁ。

Base Path
If your Slim application's front-controller lives in a physical subdirectory beneath your document root directory, you can fetch the HTTP request's physical base path (relative to the document root) with the Uri object's getBasePath() method. This will be an empty string if the Slim application is installed in the document root's top-most directory.

あ、解析で出てきたBase Path。
「Slimアプリケーションのフロントコントローラがドキュメントルートディレクトリの下の物理サブディレクトリにある場合、UriオブジェクトのgetBasePath()メソッドを使用して、HTTPリクエストの物理ベースパス(ドキュメントルートからの相対パス)を取得できます。(機械翻訳)」なるほどぉ。
Cookieとかの設定用途とか、で、使いそうだなぁ。
ちと、どこかで必要な時にでも、がっつりやってみよう。


The Request Headers
うんまぁ。

$headers = $request->getHeaders();
foreach ($headers as $name => $values) {
    echo $name . ": " . implode(", ", $values);
}

で、とれるぽ。
あとは

$headerValueArray = $request->getHeader('Accept');
$headerValueString = $request->getHeaderLine('Accept');
if ($request->hasHeader('Accept')) {
    // Do something
}

など。


The Request Body
あ、ちょうど気になってたやつ。

$parsedBody = $request->getParsedBody();


public/index.php

$app->put('/path', function(Request $request, Response $response, array $args) {
    ob_start();
    var_dump( $request->getParsedBody() );
    $s = ob_get_clean();

    $response->getBody()->write("Hello, put\n" . $s . "\n");
    return $response;
});

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
PUT /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length:21

data=value&data2=test
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:45:17 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 91

Hello, put
array(2) {
["data"]=>
string(5) "value"
["data2"]=>
string(4) "test"
}

Connection closed by foreign host.

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
PUT /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length:31

{"data":"value", "data2": 10}
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:44:25 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 82

Hello, put
array(2) {
["data"]=>
string(5) "value"
["data2"]=>
int(10)
}

Connection closed by foreign host.

こっちは綺麗に。


………ふむ。

$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
PUT /path HTTP/1.1
Host: example.com
Content-length:31

{"data":"value", "data2": 10}
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:46:09 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 17

Hello, put
NULL

Connection closed by foreign host.

あぁ。Content-type見てるのか。うん、綺麗だ。


さて………

$body = $request->getBody();

ってのもあるが……なんかこってりしそうだなぁ。

$app->put('/path', function(Request $request, Response $response, array $args) {
    ob_start();
    //var_dump( $request->getParsedBody() );
    var_dump( $request->getBody() );
    $s = ob_get_clean();

    $response->getBody()->write("Hello, put\n" . $s . "\n");
    return $response;
});
$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
PUT /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length:31

{"data":"value", "data2": 10}
HTTP/1.1 200 OK
Host: example.com
Date: Fri, 15 Jun 2018 08:47:56 +0000
Connection: close
X-Powered-By: PHP/7.2.6
Content-Type: text/html; charset=UTF-8
Content-Length: 304

Hello, put
object(Slim\Http\RequestBody)#50 (7) {
  ["stream":protected]=>
  resource(46) of type (stream)
  ["meta":protected]=>
  NULL
  ["readable":protected]=>
  NULL
  ["writable":protected]=>
  NULL
  ["seekable":protected]=>
  NULL
  ["size":protected]=>
  NULL
  ["isPipe":protected]=>
  NULL
}

Connection closed by foreign host.

あ。思ったより穏当。
細かいところはまた今度、実装する時に見てみましょ。


同様に後回ししたいのがUploaded Files。

$files = $request->getUploadedFiles();

で「Each object in the $files array is a instance of \Psr\Http\Message\UploadedFileInterface 以下略」なので、「1ファイルが1インスタンス」って単位だろうから、さほど困ることもなさそうだ。


お次、Request Helpers。
いわゆる「お便利さん」ぽ。


Detect XHR requests………XMLHttpRequestとか、久しく耳にも目にもしておりませぬなぁ。割愛w


Content Type………まぁ欲しい、時も。

$contentType = $request->getContentType();


Media Type………お外から入ってくるこれは全く信用してないw、ので、割愛。


Character Set、Content Length。うんまぁ欲しい時はほしいだろうさね。

$charset = $request->getContentCharset();
$length = $request->getContentLength();


Request Parameter。

To fetch single request parameter value, use methods: getParam(), getQueryParam(), getParsedBodyParam(), getCookieParam(), getServerParam(), counterparts of PSR-7’s plural form get*Params() methods.

「単体の値取得」ならこれ、ですか。入力で一番使いそうな予感。
ざっくりいくんなら getParam() かしらん? とも思うんだが、ざっくり過ぎる気もする。

    public function getParam($key, $default = null)
    {
        $postParams = $this->getParsedBody();
        $getParams = $this->getQueryParams();
        $result = $default;
        if (is_array($postParams) && isset($postParams[$key])) {
            $result = $postParams[$key];
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
            $result = $postParams->$key;
        } elseif (isset($getParams[$key])) {
            $result = $getParams[$key];
        }

        return $result;
    }

あぁ「$_POSTまたは$_GET」的な感じ、なのか。かつ、$_POST的なほうが優先度高め。
これなら、あり、かなぁ。


Route Object

Sometimes in middleware you require the parameter of your route.

む……middlewareのお作法をそもそも理解していない。
add()、が、そうなのかし??
あとで、具体的には「Routing」のところで #route-middleware で項目があるから、その時にさかのぼってみませうか。


Media Type Parsers
ん……「中の動き」っぽいなぁ。一旦省略。


Attributes
あ、よぉ分からん所だった一つ。

$app->add(function ($request, $response, $next) {
    $request = $request->withAttribute('session', $_SESSION); //add the session storage to your request as [READ-ONLY]
    return $next($request, $response);
});
$app->get('/test', function ($request, $response, $args) {
    $session = $request->getAttribute('session'); //get the session from the request

    return $response->write('Yay, ' . $session['name']);
});

あぁ。
MagicWeaponだと「bag」とか言ってるあたりのやつか。「持ちまわる諸々」。
requestは「不変インスタンス」だから、withAttributeは「内部でcloneしている」んだよね、的な。なので「戻り値を受け取って、そっちを$nextに渡してる」。
この辺もmiddlewareかな。


……そういえば、セッション的なのって、どうなってるんだべさね?
その辺も少し意識に入れておきませう。


ってなわけでRequest終了。
やっぱり長かったw