Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated version #5

Merged
merged 15 commits into from
Dec 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 72 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ Braden Collum - Unsplash (UL) #9HI8UJMSdZA](https://images.unsplash.com/photo-14

Get the best options to keep your application fast as ever, with just one line.

This package generates a PHP 7.4 preloading script from your Opcache statistics automatically. No need to hack your way in.
This package generates a [PHP 7.4 preloading](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.preload) script from your Opcache statistics automatically. No need to hack your way in.

## Installation

Require this using Composer into your project

composer require darkghosthunter/preloader

> This package doesn't enforces `ext-opcache` at install. Just be sure to have it [enabled in your application server](https://www.php.net/manual/en/book.opcache.php).
> This package doesn't requires `ext-opcache` to install. Just be sure to have it [enabled in your application server](https://www.php.net/manual/en/book.opcache.php).

## Usage

Expand Down Expand Up @@ -47,54 +47,69 @@ Then, tell PHP to use this file as a preloader at startup in your `php.ini`.
opcache.preload=/www/app/preload.php
```

Restart your PHP process using Opcache and that's all, you're good.
Restart your PHP process that's using Opcache, and that's all, you're good.

> If you use Preloader when Opcache is disabled or without hits, you will get an Exception.

## How it works

This package will ask Opcache for statistics about what files are the most requested.
This package will ask Opcache for statistics about what files are the most requested. You can [check this article in Medium about that preload](https://medium.com/p/9ede756f292c/).

Since the best statistics are those you get after your application has been running for a while, you can use your own mechanisms (or the ones provided by the class) to compile the list only after certain conditions are met.

![](https://miro.medium.com/max/1365/1*Zp-rR9-dPNn55L8GjSUpJg.png)

Don't worry, you can configure what and how compile the list.

## Configuration

Yuo can configure the Preloader to run when a condition is met, limit the file list, and where to output the compiled preload list.

After the Opcache hits reach a certain number, the Preloader will generate the script, not before.

### `when()` (optional)

If you don't feel like using Opcache hits, you can just use `when()`. The Preloader will proceed when the variable being passed evaluates to `true`, which will be in your hands.

Preloader::make()->when(false); // You will never run, ha ha ha!
```php
Preloader::make()->when(false); // You will never run, ha ha ha!
```

You can also use a Closure (Arrow function or any other callable) that returns `true`.

Preloader::make()->when(fn () => $app->cache()->get('should_run'));

```php
Preloader::make()->when(fn () => $app->cache()->get('should_run'));
```

#### `whenHits()` (optional)

This is the best way to gather good statistics for a good preloading list if you don't know the real load of your application.

Preloader::make()->whenHits(200000): // After a given number of hits.
```php
Preloader::make()->whenHits(200000); // After a given number of hits.
```

The list will be generated when the number of hits set are **above** the reported by Opcache.

> Watch out! If you're using `overwrite()`, the script will be regenerated every time after the number of hits are reached!

#### `whenOneIn()` (optional)

This is another helper for conditioning the generation. The list will be generated one in a given number of chances (the higher is it, the more rare till be).
The list will be generated one in a given number of chances (the higher is it, the less often will run).

Prealoder::make()->whenOneIn(2000); // 1 in 2,000 chances.
```php
Preloader::make()->whenOneIn(2000); // 1 in 2,000 chances.
```

This may come in handy using it with `overwrite()` to constantly recreate the list.

Prealoder::make()->overwrite()->whenOneIn(2000);
```php
Preloader::make()->overwrite()->whenOneIn(2000);
```

### `memory()` (optional, default)

Preloader::make()->memory(32);
```php
Preloader::make()->memory(32);
```

Set your memory limit in **MB**. The default of 32MB is enough for *most* applications. The Preloader will generate a list of files until that memory limit is reached.

Expand All @@ -104,49 +119,78 @@ This takes into account the `memory_consumption` key of each script cached in Op

### `exclude()` (optional)

You can exclude from the list given by Opcache using `exclude()`, which accepts a single file or an array of files.
You can exclude files from the list given by Opcache using `exclude()`, which accepts a single file or an array of files. These are passed to the `glob()` method. These file paths **must be absolute**.

Preloader::make()->exclude(['foo.php', 'bar.php']);
```php
Preloader::make()->exclude([
'/app/foo.php',
'/app/bar.php',
'/app/quz/*.php'
]);
```

These excluded files will be excluded from the list generation, and won't count for memory limits.

These excluded files will be excluded from the list generation, also excluding them from memory limits.
Preloader library files are automatically excluded. You can disable this using `includePreloader()`:

> Preloader library files are automatically excluded.
```php
Preloader::make()->includePreloader()
->exclude([
'/app/foo.php',
'/app/bar.php',
'/app/quz/*.php'
]);
```

### `append()` (optional)

Preloader::make()->append(['foo.php', 'bar.php']);
Of course you can add files using absolute paths manually to the preload script to be generated. Just issue them with `append()`. These are passed to the `glob()` method. These file paths **must be absolute**.

Of course you can add files using absolute paths manually to the preload script to be generated. Just issue them with `append()`.
```php
Preloader::make()->append([
'/app/foo.php',
'/app/bar.php',
'/app/quz/*.php'
]);
```

Prepending files will put them **after** the list generation, so they won't count list or memory limits.
Prepending files will put them **after** the list generation, so they won't count for the list memory limit.

Preloader::make()->top(0.5)->memory(64)->append('foo.bar');
```php
Preloader::make()->memory(64)->append('foo.bar');
```

> Any duplicated file appended will be ignored since the list will remove them automatically before compiling the script.

### `output()` (required)

We need to know where to output the script. It's recommended to do it in the same application folder, since most PHP processes will have access to write in it. If not, you're free to point out where.
We need to know where to output the script. It's recommended to do it in the same application folder, since most PHP processes will have access to write inside the same directory. If not, you're free to point out where.

Preloader::make()->output(__DIR__ . '/../../my-preloader.php');
```php
Preloader::make()->output(__DIR__ . '/../../my-preloader.php');
```

### `overwrite()` (optional)

Sometimes you may have run your preloader script already. To avoid replacing the list with another one, Preloader by default doesn't do nothing when it detects the file already exists.
Sometimes you may have run your preloader script already. To avoid replacing the list with another one, Preloader by default doesn't do nothing when it detects the script file already exists.

To change this behaviour, you can use the `overwrite()` method to instruct Preloader to always rewrite the file.

Preloader::make()->overwrite()->generate();
```php
Preloader::make()->overwrite()->generate();
```

> Watch out using this along conditions like `whenHits()` and `when()`. If the condition are true, the Preloader will overwrite the preload script... over and over and over again!

### `generate()` (required)

Once your Preloader configuration is ready, you can generate the list using `generate()`.

Preloader::make()->generate();
```php
Preloader::make()->generate();
```

This will automatically create a PHP-ready script to preload your application. It will return `true` on success, and `false` when the when the conditions are not met or an existing preload file exists.
This will automatically create a PHP-ready script to preload your application. It will return `true` on success, and `false` when the when the conditions are not met or an existing preload file exists that shouldn't be overwritten.

## Give me an example

Expand Down Expand Up @@ -176,10 +220,6 @@ $weekAfterDeploy = $app->deploymentTimestamp() + (7*24*60*60);
->generate();
```

## Contributing

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

## Security

If you discover any security related issues, please email darkghosthunter@gmail.com instead of using the issue tracker.
Expand Down
8 changes: 3 additions & 5 deletions src/Conditions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace DarkGhostHunter\Preloader;

use DateTime;

trait Conditions
{
/**
Expand All @@ -14,14 +12,14 @@ trait Conditions
protected bool $shouldRun = true;

/**
* Run the Preloader script after Opcache hits reach certain number
* Run the Preloader script when Opcache hits reach certain number
*
* @param int $hits
* @return $this
*/
public function whenHits(int $hits = 200000) : self
{
return $this->when(fn () => $hits > $this->opcache->getHits());
return $this->when($hits > $this->opcache->getHits());
}

/**
Expand All @@ -32,7 +30,7 @@ public function whenHits(int $hits = 200000) : self
*/
public function whenOneIn(int $chances = 100) : self
{
return $this->when(fn () => random_int(1, $chances) === (int)floor($chances/2));
return $this->when(random_int(1, $chances) === (int)floor($chances/2));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/GeneratesScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protected function shouldWrite()
}

/**
* Returns a digestible opcache configuration
* Returns a digestible Opcache configuration
*
* @return array
*/
Expand Down
2 changes: 0 additions & 2 deletions src/LimitsList.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace DarkGhostHunter\Preloader;

use RuntimeException;

trait LimitsList
{
/**
Expand Down
37 changes: 35 additions & 2 deletions src/ManagesFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@

trait ManagesFiles
{
/**
* Include the Preloader files in the file list.
*
* @param bool $include
* @return $this
*/
public function includePreloader(bool $include = true)
{
$this->lister->includePreloader = $include;

return $this;
}

/**
* Append a list of files to the preload list. These won't count for file and memory limits.
*
Expand All @@ -12,7 +25,7 @@ trait ManagesFiles
*/
public function append($files) : self
{
$this->lister->append = (array)$files;
$this->lister->append = $this->listFiles((array)$files);

return $this;
}
Expand All @@ -25,8 +38,28 @@ public function append($files) : self
*/
public function exclude($files) : self
{
$this->lister->exclude = (array)$files;
$this->lister->exclude = $this->listFiles((array)$files);

return $this;
}

/**
* take every file string and pass it to the glob function.
*
* @param array $files
* @return array
*/
protected function listFiles(array $files) : array
{
$paths = [];

// We will cycle trough each "file" and save the resulting array given by glob.
// If the glob returns false, we will trust the developer goodwill and add it
// anyway, since the file may not exists until the app generates something.
foreach ($files as $file) {
$paths[] = glob($file) ?: [$file];
}

return array_merge(...$paths);
}
}
6 changes: 3 additions & 3 deletions src/Preloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Preloader
*
* @const
*/
protected const STUB = __DIR__ . '/preload.php.stub';
protected const STUB_LOCATION = __DIR__ . '/preload.php.stub';

/**
* Determines if the preload file should be rewritten.
Expand Down Expand Up @@ -119,7 +119,7 @@ public function generate()
return false;
}

$this->compiler->contents = file_get_contents(static::STUB);
$this->compiler->contents = file_get_contents(static::STUB_LOCATION);
$this->compiler->opcacheConfig = $this->getOpcacheConfig();
$this->compiler->preloaderConfig = $this->getPreloaderConfig();
$this->compiler->list = $this->lister->build();
Expand All @@ -146,7 +146,7 @@ protected function canGenerate()
}

if (! $this->compiler->autoload) {
throw new LogicException('Cannot proceed without an Composer Autoload.');
throw new LogicException('Cannot proceed without a Composer Autoload.');
}

if (! file_exists($this->compiler->autoload)) {
Expand Down
9 changes: 8 additions & 1 deletion src/PreloaderLister.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ class PreloaderLister
*/
public array $exclude = [];

/**
* If the Preloader package should also be included.
*
* @var bool
*/
public bool $includePreloader = false;

/**
* Opcache class access
*
Expand Down Expand Up @@ -136,7 +143,7 @@ protected function exclude(array $scripts)
*/
protected function excludedPackageFiles()
{
return [
return $this->includePreloader ? [] : [
realpath(__DIR__ . '/Conditions.php'),
realpath(__DIR__ . '/GeneratesScript.php'),
realpath(__DIR__ . '/LimitsList.php'),
Expand Down
7 changes: 6 additions & 1 deletion src/preload.php.stub
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
*
* Add (or update) this line in `php.ini`:
*
* opcache.preload=@output
* opcache.preload=@output
*
*
* --- Config ---
* Generated at: @generated_at
Expand All @@ -25,6 +26,10 @@
* - Overwrite: @preloader_overwrite
* - Files excluded: @preloader_excluded
* - Files appended: @preloader_appended
*
*
* For more information:
* @see https://github.com/darkghosthunter/preloader
*/

require_once '@autoload';
Expand Down
Loading