diff --git a/README.md b/README.md index 1a69944..7979afb 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,57 @@ PHP 8 Library for Gemini Protocol ## Usage ``` -composer require yggverse/gemini:dev-main +composer require yggverse/gemini +``` + +## Client + +PHP interface for Gemini protocol queries by TLS socket connection + +### Request + +``` +$request = new \Yggverse\Gemini\Client\Request( + 'gemini://betahowto.duckdns.org:1965/archive' +); +``` + +#### Request::setHost +#### Request::getHost +#### Request::setPort +#### Request::getPort +#### Request::setPath +#### Request::getPath +#### Request::setQuery +#### Request::getQuery +#### Request::getResponse + +Execute requested URL and return raw response + +``` +var_dump( + $request->getResponse() +); +``` + +### Response + +This class provides additional features for the raw response operations + +``` +$response = new \Yggverse\Gemini\Client\Response( + $request->getResponse() +); +``` + +#### Response::getCode +#### Response::getMeta +#### Response::getBody + +``` +var_dump( + $response->getBody() +); ``` ## DokuWiki diff --git a/src/Client/Request.php b/src/Client/Request.php new file mode 100644 index 0000000..400693b --- /dev/null +++ b/src/Client/Request.php @@ -0,0 +1,178 @@ +setHost( + $host + ); + } + + else + { + throw new Exception(); // @TODO + } + + if ($port = parse_url($url, PHP_URL_PORT)) + { + $this->setPort( + $port + ); + } + + else + { + $this->setPort( + 1965 + ); + } + + if ($path = parse_url($url, PHP_URL_PATH)) + { + $this->setPath( + $path + ); + } + + else + { + $this->setPath( + '' + ); + } + + if ($query = parse_url($url, PHP_URL_QUERY)) + { + $this->setQuery( + $query + ); + } + + else + { + $this->setQuery( + '' + ); + } + } + + public function setHost(string $value): void + { + $this->_host = $value; + } + + public function getHost(): string + { + return $this->_host; + } + + public function setPort(int $value): void + { + $this->_port = $value; + } + + public function getPort(): int + { + return $this->_port; + } + + public function setPath(string $value): void + { + $this->_path = $value; + } + + public function getPath(): string + { + return $this->_path; + } + + public function setQuery(string $value): void + { + $this->_query = $value; + } + + public function getQuery(): string + { + return $this->_query; + } + + public function getResponse( + int $timeout = 30, // socket timeout, useful for offline resources + ?int $limit = null, // content length, null for unlimited + int $chunk = 1024, // chunk size, it's better change it later to 1 @TODO + int &$length = 0, // current response length, do not change without special needs + ?int &$code = null, // error code for debug + ?string &$message = null, // error message for debug + string &$response = '' // response init, also returning by this method + ): ?string + { + $connection = stream_socket_client( + sprintf( + 'tls://%s:%d', + $this->_host, + $this->_port + ), + $code, + $message, + $timeout, + STREAM_CLIENT_CONNECT, + stream_context_create( + [ + 'ssl' => + [ + 'verify_peer' => false, + 'verify_peer_name' => false + ] + ] + ) + ); + + if (!is_resource($connection)) + { + return null; + } + + fwrite( + $connection, + sprintf( + "gemini://%s:%d%s%s\r\n", + $this->_host, + $this->_port, + $this->_path, + $this->_query + ) + ); + + while ($part = fgets($connection, $chunk)) + { + $length = $length + mb_strlen( + $part + ); + + if ($limit && $length > $limit) + { + break; + } + + $response .= $part; + } + + fclose( + $connection + ); + + return $response; + } +} \ No newline at end of file diff --git a/src/Client/Response.php b/src/Client/Response.php new file mode 100644 index 0000000..9d8c4bd --- /dev/null +++ b/src/Client/Response.php @@ -0,0 +1,58 @@ +\d{2})\s(?.*)\r\n(?.*)/su', + $data, + $match + ); + + if (isset($match['code'])) + { + $code = (int) $match['code']; + + if ($code >= 10 && $code <= 69) + { + $this->_code = $match['code']; + } + } + + if (isset($match['meta']) && mb_strlen($match['meta']) <= 1024) + { + $this->_meta = (string) $match['meta']; + } + + if (isset($match['body'])) + { + $this->_body = (string) $match['body']; + } + } + + public function getCode(): ?int + { + return $this->_code; + } + + public function getMeta(): ?string + { + return $this->_meta; + } + + public function getBody(): ?string + { + return $this->_body; + } +} \ No newline at end of file