Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ikkez committed Jan 3, 2025
1 parent 703a3ad commit f7f0714
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/.idea/
/vendor/
/composer.lock
/logs/
/.phpunit.result.cache
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,25 @@ NB: Custom tests were mostly taken from [Nyholm/psr7](https://github.com/Nyholm/

Benchmarks done with `devanych/psr-http-benchmark` on php 8.0+.

Test results (Intel Xeon Gold 6140 CPU @ 2.30GHz, 4 cores):
Checkout `benchmark` branch, start docker composer, then run in container:

```
COMPOSER_ROOT_VERSION=1.0 composer update
```

Run benchmarks on php 8.0+:

```
cd benchmark/
php benchmark.php fatfree 50000
```

## Test results (best of 3 on MacBook M2 Pro)

| Runs: 50,000 | Guzzle | HttpSoft | Laminas | Nyholm | Slim | Fatfree |
|----------------------|-----------|-----------|-----------|-----------|-----------|-----------|
| Runs per second | 18599 | 31938 | 22601 | 27999 | 18789 | 35200 |
| Average time per run | 0.0538 ms | 0.0313 ms | 0.0442 ms | 0.0357 ms | 0.0532 ms | 0.0284 ms |
| Total time | 2.6882 s | 1.5655 s | 2.2122 s | 1.7858 s | 2.6611 s | 1.4204 s |
| Runs per second | 14412 | 18608 | 17641 | 20549 | 14444 | 22233 |
| Average time per run | 0.0694 ms | 0.0537 ms | 0.0567 ms | 0.0487 ms | 0.0692 ms | 0.0450 ms |
| Total time | 3.4691 s | 2.6869 s | 2.8342 s | 2.4331 s | 3.4616 s | 2.2488 s |

---
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"type": "library",
"require": {
"php": ">=8.0",
"psr/http-message": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"psr/http-factory": "^1.0",
"php-http/message-factory": "^1.0"
},
Expand All @@ -17,13 +17,13 @@
}
],
"provide": {
"psr/http-message-implementation": "1.0",
"psr/http-message-implementation": "2.*",
"psr/http-factory-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"php-http/psr7-integration-tests": "dev-master",
"http-interop/http-factory-tests": "^0.9.0"
"phpunit/phpunit": "^9.6 || ^10.5 || ^11.5",
"php-http/psr7-integration-tests": "1.*",
"http-interop/http-factory-tests": "^2.2.0"
},
"autoload": {
"psr-4": {
Expand All @@ -36,6 +36,6 @@
}
},
"scripts": {
"test": "phpunit"
"test": "phpunit --display-deprecations"
}
}
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
networks:
internal:
services:
webserver:
container_name: 'f3-psr7'
build:
context: ./docker/bin/php
restart: 'always'
networks:
- internal
volumes:
- ${DOCUMENT_ROOT-./}:/var/www/html:rw
- ${PHP_INI-./docker/config/php/php.ini}:/usr/local/etc/php/php.ini
- ${VHOSTS_DIR-./docker/config/vhosts}:/etc/apache2/sites-enabled
- ${LOG_DIR-./logs/apache2}:/var/log/apache2
environment:
XDEBUG_MODE: debug
XDEBUG_CONFIG: client_host=host.docker.internal
ports:
- "80:80"
external_links:
- webserver:f3-psr7.localhost
59 changes: 59 additions & 0 deletions docker/bin/php/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
FROM php:8.2-apache

ARG DEBIAN_FRONTEND=noninteractive

# Update
RUN apt-get -y update --fix-missing && \
apt-get upgrade -y && \
apt-get --no-install-recommends install -y apt-utils && \
rm -rf /var/lib/apt/lists/*

# Install tools and libaries
RUN apt-get -y update && \
apt-get -y --no-install-recommends install --fix-missing \
wget \
dialog \
locales \
zlib1g-dev \
libzip-dev \
libicu-dev && \
apt-get -y --no-install-recommends install --fix-missing apt-utils \
build-essential \
git \
curl \
libonig-dev && \
apt-get -y --no-install-recommends install --fix-missing libcurl4 \
libcurl4-openssl-dev \
zip \
unzip \
openssl && \
rm -rf /var/lib/apt/lists/* && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Set the locales
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen

ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8

RUN pecl channel-update pecl.php.net

# Other PHP extensions
RUN docker-php-ext-install -j$(nproc) intl
RUN docker-php-ext-install zip

# Install XDebug
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
COPY docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

# Enable apache modules
RUN a2enmod rewrite headers ssl

# Cleanup
RUN rm -rf /usr/src/*

RUN usermod -u 1000 www-data
RUN chown -R www-data:www-data /var/www/html
4 changes: 4 additions & 0 deletions docker/bin/php/docker-php-ext-xdebug.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
zend_extension=xdebug.so
xdebug.mode = off
xdebug.log_level=0
xdebug.output_dir=/var/www/html/logs
2 changes: 2 additions & 0 deletions docker/config/php/php.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
memory_limit = 32M
short_open_tag = 0
8 changes: 8 additions & 0 deletions docker/config/vhosts/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot "/var/www/html"
ServerName localhost
<Directory "/var/www/html/">
AllowOverride all
</Directory>
</VirtualHost>
2 changes: 1 addition & 1 deletion src/Http/Factory/Psr17Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function createStream(string $content = ''): StreamInterface {
}

public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface {
if (($resource = @\fopen($filename, $mode)) === false) {
if (!$filename || ($resource = @\fopen($filename, $mode)) === false) {
throw new \RuntimeException('Unable to to open file');
}
return new Stream($resource);
Expand Down
6 changes: 3 additions & 3 deletions src/Http/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

class Message implements MessageInterface {

protected ?string $version = '1.1';
protected string $version = '1.1';
protected array $headers = [];
protected ?StreamInterface $body = NULL;

public function getProtocolVersion(): ?string {
public function getProtocolVersion(): string {
return $this->version;
}

Expand Down Expand Up @@ -106,7 +106,7 @@ public function withoutHeader($name): static {
return $new;
}

public function getBody(): ?StreamInterface {
public function getBody(): StreamInterface {
return $this->body;
}

Expand Down
8 changes: 4 additions & 4 deletions src/Http/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public function rewind(): void {

public function isWritable(): bool {
if ($this->writable === NULL) {
$mode=$this->getMetadata('mode');
$this->writable = str_contains($mode,'w') || str_contains($mode,'+') || str_contains($mode,'x') || str_contains($mode,'c') || str_contains($mode,'a');
$this->writable = ($mode=$this->getMetadata('mode'))
&& (\str_contains($mode,'w') || \str_contains($mode,'+') || \str_contains($mode,'x') || \str_contains($mode,'c') || \str_contains($mode,'a'));
}
return $this->writable;
}
Expand All @@ -115,8 +115,8 @@ public function write($string): int {

public function isReadable(): bool {
if ($this->readable === NULL) {
$mode=$this->getMetadata('mode');
$this->readable = \str_contains($mode,'r') || \str_contains($mode,'+');
$this->readable = ($mode=$this->getMetadata('mode'))
&& (\str_contains($mode,'r') || \str_contains($mode,'+'));
}
return $this->readable;
}
Expand Down
16 changes: 11 additions & 5 deletions src/Http/Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function getAuthority(): string {
}

public function getUserInfo(): string {
return $this->user.($this->pass!==''?':'.$this->pass:'');
return ($this->user).($this->pass!==''?':'.($this->pass):'');
}

public function getHost(): string {
Expand All @@ -64,7 +64,10 @@ public function getPort(): ?int {
} === $this->port ? null : $this->port;
}

public function getPath(): string {
public function getPath(bool $trim=true): string {
if ($trim && $this->host && \str_starts_with($this->path, '/')) {
return '/'.\ltrim($this->path, '/');
}
return $this->path;
}

Expand All @@ -86,8 +89,11 @@ public function withScheme($scheme): Uri {

public function withUserInfo($user, $password = NULL): Uri {
$new = clone $this;
$new->user = $user;
$pattern = '/[^%a-zA-Z0-9_\-.~!$&\'()*+,;=]+|%(?![A-Fa-f0-9]{2})/';
$new->user = \preg_match($pattern, $user) ? \rawurlencode($user) : $user;
$new->pass = (string) $password;
if (\preg_match($pattern, $new->pass))
$new->pass = \rawurlencode($new->pass);
return $new;
}

Expand Down Expand Up @@ -131,10 +137,10 @@ public function withFragment($fragment): Uri {
return $new;
}

public function __toString() {
public function __toString(): string {
return (($s=$this->getScheme()) !== '' ? $s.':' : '').
(($a=$this->getAuthority()) !== '' ? '//'.$a : '').
(($p=$this->getPath()) !=='' ? (
(($p=$this->getPath(false)) !=='' ? (
(!($abs=\str_starts_with($p, '/')) && $a !== '' ? '/' : '').
($abs && $a === '' ? '/'.\ltrim($p, '/') : $p)
) : '').
Expand Down
2 changes: 1 addition & 1 deletion tests/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public function testSameInstanceWhenRemovingMissingHeader()
$this->assertSame($r, $r->withoutHeader('foo'));
}

public function trimmedHeaderValues()
public static function trimmedHeaderValues(): array
{
return [
[(new Response())->withHeaders(['OWS' => " \t \tFoo\t \t "])],
Expand Down
6 changes: 3 additions & 3 deletions tests/UploadedFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function tearDown(): void
}
}

public function invalidErrorStatuses()
public static function invalidErrorStatuses(): array
{
return [
'null' => [null],
Expand Down Expand Up @@ -87,7 +87,7 @@ public function testSuccessful()
$this->assertEquals($stream->__toString(), file_get_contents($to));
}

public function invalidMovePaths()
public static function invalidMovePaths(): array
{
return [
'null' => [null],
Expand Down Expand Up @@ -144,7 +144,7 @@ public function testCannotRetrieveStreamAfterMove()
$upload->getStream();
}

public function nonOkErrorStatus()
public static function nonOkErrorStatus(): array
{
return [
'UPLOAD_ERR_INI_SIZE' => [UPLOAD_ERR_INI_SIZE],
Expand Down
8 changes: 4 additions & 4 deletions tests/UriTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function testValidUrisStayValid($input)
$this->assertSame($input, (string) $uri);
}

public function getValidUris()
public static function getValidUris(): array
{
return [
['urn:path-rootless'],
Expand Down Expand Up @@ -97,7 +97,7 @@ public function testInvalidUrisThrowException($invalidUri)
new Uri($invalidUri);
}

public function getInvalidUris()
public static function getInvalidUris(): array
{
return [
// parse_url() requires the host component which makes sense for http(s)
Expand Down Expand Up @@ -216,7 +216,7 @@ public function testCanConstructFalseyUriParts()
$this->assertSame('0://0:0@0/0?0#0', (string) $uri);
}

public function getResolveTestCases()
public static function getResolveTestCases(): array
{
return [
[self::RFC3986_BASE, 'g:h', 'g:h'],
Expand Down Expand Up @@ -366,7 +366,7 @@ public function testAuthorityWithUserInfoButWithoutHost()
$this->assertSame('', $uri->getAuthority());
}

public function uriComponentsEncodingProvider()
public static function uriComponentsEncodingProvider(): array
{
$unreserved = 'a-zA-Z0-9.-_~!$&\'()*+,;=:@';

Expand Down

0 comments on commit f7f0714

Please sign in to comment.