Skip to content

Commit eda77b6

Browse files
committed
'fix' memory leak issue, namespace functions and composer
1 parent f2a46b7 commit eda77b6

19 files changed

+928
-260
lines changed

README.md

Lines changed: 85 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
# Typed
22

3-
This libaray allows you to create typed variables in PHP 7.4 +
3+
This library allows you to create typed variables in PHP 7.4 +
44

5-
example :
5+
### Composer installation :
6+
```console
7+
$ composer require azjezz/typed
8+
```
9+
10+
### Usage Example :
611

712
```php
813
<?php declare(strict_types=1);
914

10-
$name = &string('saif');
11-
$age = &int(19);
15+
use Typed as t;
16+
17+
$name = &t\string('saif');
18+
$age = &t\int(19);
1219

1320
try {
1421

@@ -18,45 +25,91 @@ try {
1825

1926
} catch(TypeError $error) {
2027

28+
} finally {
29+
t\clean();
2130
}
22-
23-
// based on the passed variable type
24-
$string = &typed('saif');
25-
$traversable = &traversable(new ArrayObject());
26-
$countable = &countable(new ArrayObject());
27-
$func = &func('strtolower');
28-
$object = &object((object) []);
29-
$stdClass = &stdClass((object) []);
30-
$iterable = &iterable([]);
31-
$bool = &bool(false);
32-
$bool = &boolean(true);
33-
$set = &set(['a', 'b', 'b', 'a']); // ['a', 'b']
34-
$vector = &vector(['a' => 1, 'b' => 2]); // [1, 2]
35-
$array = &arr(['a' => 1 , 'b' => 2]); // ['a' => 1, 'b' => 2]
36-
$keyset = &keyset(['a' => 1, 'b' => 2]); // ['a', 'b']
37-
$int = &integer(5);
38-
$int = &int(48);
39-
$string = &string('f');
40-
$float = &float(1.56);
4131
```
4232

43-
### callable => func, array => { arr, keyset, vector, set }
33+
#### callable => `func`, array => { `arr`, `keyset`, `vector`, `set` }
4434
Since `callable` and `array` cant be used for function names, instead i have used `func` for callable and `arr` for array.
4535
`keyset`, `vector` and `set` are just helper to help you create an array of keys ( `keyset = arr(array_keys($value))` ), an array of values ( `vector = arr(array_values($value))` ) and an array of unique values ( `set = arr(array_unique($value))` ).
4636

47-
### typed
48-
`typed()` is a function to help you create a typed variables based on the type of the passed variable, currently it supports `string`, `int`, `float`, `bool`, `object` and `iterable`. it was suggected by @mikesherov on [twitter](https://twitter.com/mikesherov/status/1084512906388144128).
37+
#### `typed`
38+
`typed()` is a function to help you create a typed variables based on the type of the passed variable, currently it supports `string`, `int`, `float`, `bool`, `object` and `iterable`. it was suggested by @mikesherov on [twitter](https://twitter.com/mikesherov/status/1084512906388144128).
4939

50-
---
40+
#### `purge`, `clean`, `delete` and `c`
41+
As mentioned [here](https://twitter.com/drealecs/status/1084658093252849665) and [here](https://www.reddit.com/r/PHP/comments/afq3it/typed_variables_for_php_74/ee1belg), memory leak is a huge issue here, if a specific function created a typed variable, the variable will still exist inside the repository even after execution,
42+
for this i made some helper functions that allow you to remove variables from the repository.
5143

52-
check this [twitter thread](https://twitter.com/dshafik/status/1084248443118219264).
44+
- `Typed\purge()` will delete every reference registered in the repository, you can call this function at the end of every request/response cycle in a web application per example.
45+
- `Typed\clean(string $namespace = 'default')` this function will delete every reference to a typed variable in a specific namespace as shown in the example above, its recommended to use a unique namespace every time you use typed variables and call `clean()` to ensure there's no memory leak.
46+
- `Typed\delete(mixed $value, string $namespace = 'default')` this function will delete the last created reference to typed variable with the given value in a specific namespace. example :
47+
```php
48+
<?php declare(strict_types=1);
5349

54-
---
50+
use Typed as t;
51+
52+
$name = &t\string('azjezz', 'data');
53+
$age = &t\int(19, 'data');
5554

56-
### Compoer installation :
57-
```console
58-
soon
55+
t\delete('azjezz', 'data');
56+
57+
/**
58+
* the reference to the `$name` variable has been deleted.
59+
* therefor we can assign any type to the variable `$name` now.
60+
*/
61+
$name = []; // works
5962
```
63+
- `Typed\c` this function accepted a callable that take no arguments, the callable will be called inside `c` ( container ) and all the assigned typed variables in the time of the call would be deleted afterward, this makes it easier to ensure that function calls don't cause any memory leak. example :
64+
```php
65+
<?php declare(strict_types=1);
66+
67+
use Typed as t;
68+
use function Typed\c;
69+
70+
/**
71+
* all assigned variables inside the callable will be destroyed after execution.
72+
*/
73+
c(function(): void {
74+
$name = &t\string('saif eddin');
75+
$age = &t\int(5);
76+
$arr = &t\arr([
77+
'age' => $age,
78+
'name' => $name
79+
]);
80+
});
81+
82+
```
83+
84+
#### More :
85+
A cool PSR-15 Middleware to delete all typed variables created inside the next middleware/handler
86+
87+
```php
88+
<?php declare(strict_types=1);
89+
90+
use Psr\Http\Server\MiddlewareInterface;
91+
use Psr\Http\Server\RequestHandlerInterface;
92+
use Psr\Http\Message\ServerRequestInterface;
93+
use Psr\Http\Message\ResponseInterface;
94+
use function Typed\c;
95+
96+
class TypedMiddleware implements MiddlewareInterface
97+
{
98+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
99+
{
100+
$response = null;
101+
c(function() use($request, &$response, $handler) {
102+
/**
103+
* if any typed variable is created in the handler, it would be deleted after execution.
104+
*/
105+
$response = $handler->handle($request);
106+
});
107+
return $response;
108+
}
109+
}
110+
111+
```
112+
60113

61114
#### TODO :
62115
- add `nullable*` functions.

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"Typed\\": "src/Typed"
1515
},
1616
"files": [
17-
"src/bootstrap.php"
17+
"src/functions.php"
1818
]
1919
},
2020
"require": {

src/Typed/InvalidArgumentException.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44

55
use InvalidArgumentException as ParentException;
66

7-
class InvalidArgumentException extends ParentException {}
7+
class InvalidArgumentException extends ParentException
8+
{
9+
}

src/Typed/Repository.php

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,114 @@
22

33
namespace Typed;
44

5-
final class Repository
5+
use function array_key_exists;
6+
use function array_keys;
7+
use function array_reverse;
8+
use Typed\Variable\VariableInterface;
9+
10+
final class Repository
611
{
12+
/**
13+
* @var array<string, array<int, VariableInterface>> $variables
14+
*/
715
private static array $variables = [];
8-
private function __construct() {}
9-
public static function add(Variable $var): void
16+
17+
private function __construct()
18+
{
19+
}
20+
21+
/**
22+
* Add a variable to the registry mapped by the given namespace.
23+
*
24+
* @param VariableInterface $var
25+
* @param string $namespace
26+
*/
27+
public static function add(VariableInterface $var, string $namespace): void
28+
{
29+
static::$variables[$namespace][] = $var;
30+
}
31+
32+
/**
33+
* remove reference to all variables in the given namespace.
34+
*
35+
* @param string $namespace
36+
*/
37+
public static function clean(string $namespace): void
38+
{
39+
unset(static::$variables[$namespace]);
40+
}
41+
42+
/**
43+
* Delete all references.
44+
*/
45+
public static function purge(): void
46+
{
47+
static::$variables = [];
48+
}
49+
50+
/**
51+
* Delete all references to variables with the given value.
52+
*
53+
* @param mixed $value value to check.
54+
* @param string|null $namespace
55+
*/
56+
public static function filter($value, string $namespace): void
57+
{
58+
foreach (static::$variables as $ns) {
59+
if ($namespace !== $ns) {
60+
continue;
61+
}
62+
63+
/**
64+
* @var VariableInterface $var
65+
*/
66+
foreach ($ns as $i => $var) {
67+
if ($var->getValue() === $value) {
68+
unset(static::$variables[$ns][$i]);
69+
}
70+
}
71+
}
72+
}
73+
74+
/**
75+
* remove the last reference to variable with the given value in a specific namespace.
76+
*
77+
* @param $value
78+
* @param string $namespace
79+
*/
80+
public static function delete($value, string $namespace): void
81+
{
82+
if (!array_key_exists($namespace, static::$variables)) {
83+
return;
84+
}
85+
86+
$keys = array_reverse(array_keys(static::$variables[$namespace]));
87+
foreach ($keys as $k) {
88+
/**
89+
* @var VariableInterface $var
90+
*/
91+
$var = static::$variables[$namespace][$k];
92+
if ($var->getValue() === $value) {
93+
unset(static::$variables[$namespace][$k]);
94+
return;
95+
}
96+
}
97+
}
98+
99+
/**
100+
* Contain all typed variables in 1 call.
101+
* All typed variables created during the callable execution, will be deleted.
102+
*
103+
* @param callable $call
104+
*/
105+
public static function c(callable $call): void
10106
{
11-
static::$variables[] = $var;
107+
$variables = static::$variables;
108+
static::$variables = [];
109+
try {
110+
$call();
111+
} finally {
112+
static::$variables = $variables;
113+
}
12114
}
13115
}

src/Typed/Variable.php

Lines changed: 0 additions & 109 deletions
This file was deleted.

0 commit comments

Comments
 (0)