Compare commits

..

No commits in common. "master" and "3.2.1" have entirely different histories.

15 changed files with 504 additions and 4689 deletions

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
github: amnuts

6
.gitignore vendored
View file

@ -1,11 +1,11 @@
/status.js
/src/status.js
/.idea
/.module-cache
/node_modules
/vendor
/package-lock.json
/build/*
!/build/template.phps
!/build/build.php
!/build/_frontend
!/build/_languages
/*.js
/vendor

241
README.md
View file

@ -2,33 +2,24 @@
A clean and responsive interface for Zend OPcache information, showing statistics, settings and cached files, and providing a real-time update for the information.
This interface uses ReactJS and Axios and is for modern browsers, and requires a minimum of PHP 7.1.
This interface uses ReactJS and Axios and is for modern browsers and requires a minimum of PHP 7.1.
## License
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=acollington&url=https://github.com/amnuts/opcache-gui&title=opcache-gui&language=&tags=github&category=software)
If you like this software or find it helpful then maybe you'll consider supporting my efforts in some way by [signing up to Flattr and leaving a micro-donation](https://flattr.com/@acollington).
MIT: http://acollington.mit-license.org/
## Sponsoring this work
If you're able and would like to sponsor this work in some way, then that would be super awesome :heart:. You can do that through the [GitHub Sponsorship](https://github.com/sponsors/amnuts) page.
Alternatively, if you'd just like to give me a [shout-out on X (aka, Twitter)](https://twitter.com/acollington) to say you use it, then that'd be awesome, too! (Any one else miss postcardware?)
## Using the opcache-gui
### Installing
### Using the opcache-gui
There are two ways to getting started using this gui:
#### Copy/clone this repo
The easiest way to start using the opcache-gui is to clone this repo, or simply copy/paste/download the `index.php` file to a location which your web server can load. Then point your browser to that location, such as `https://www.example.com/opcache/index.php`.
The easiest way to start using the opcache-gui is to clone this repo, or simply to copy/paste/download the `index.php` file, to a location which your web server can load. Then point your browser to that location, such as `https://www.example.com/opcache/index.php`.
#### Install via composer
You can include the files with [Composer](https://getcomposer.org/) by running the command `composer require amnuts/opcache-gui`.
Once in your `vendor` directory, there are numerous ways in which you can use the interface. For example, if you're using a framework such as Symfony or Laravel, you could load opcache-gui into a `Controller`. Your requirements of setting it up within your framework of choice will vary, so it's not really possible to detail how to do that within this readme... but I have faith in your ability to figure it out!
Once in your `vendor` directory, there are numerous ways in which you can use the interface. For example if you're using a framework such as Symfony or Laravel, you could load opcache-gui into a `Controller`. Your requirements of setting it up within your framework of choice will vary, so it's not really possible to detail how to do that within this readme... but I have faith in your ability to figure it out!
The namespace used for the class is `Amnuts\Opcache`, so once the dependency is in your `autoload.php` you can use the `\Amnuts\Opcache\Service` class. For example, you could do something like:
@ -73,33 +64,27 @@ The default configuration for the interface looks like this:
```php
$options = [
'allow_filelist' => true, // show/hide the files tab
'allow_invalidate' => true, // give a link to invalidate files
'allow_reset' => true, // give option to reset the whole cache
'allow_realtime' => true, // give option to enable/disable real-time updates
'refresh_time' => 5, // how often the data will refresh, in seconds
'size_precision' => 2, // Digits after decimal point
'size_space' => false, // have '1MB' or '1 MB' when showing sizes
'charts' => true, // show gauge chart or just big numbers
'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
'per_page' => 200, // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui', // name of cookie
'cookie_ttl' => 365, // days to store cookie
'datetime_format' => 'D, d M Y H:i:s O', // Show datetime in this format
'highlight' => [
'memory' => true, // show the memory chart/big number
'hits' => true, // show the hit rate chart/big number
'keys' => true, // show the keys used chart/big number
'jit' => true // show the jit buffer chart/big number
],
// json structure of all text strings used, or null for default
'language_pack' => null
'allow_filelist' => true, // show/hide the files tab
'allow_invalidate' => true, // give a link to invalidate files
'allow_reset' => true, // give option to reset the whole cache
'allow_realtime' => true, // give option to enable/disable real-time updates
'refresh_time' => 5, // how often the data will refresh, in seconds
'size_precision' => 2, // Digits after decimal point
'size_space' => false, // have '1MB' or '1 MB' when showing sizes
'charts' => true, // show gauge chart or just big numbers
'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
'per_page' => 200, // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui', // name of cookie
'cookie_ttl' => 365, // days to store cookie
'highlight' => [
'memory' => true, // show the memory chart/big number
'hits' => true, // show the hit rate chart/big number
'keys' => true // show the keys used chart/big number
]
];
```
If you want to change any of the defaults, you can pass in just the ones you want to change if you're happy to keep the rest as-is. Just alter the array at the top of the `index.php` script (or pass in the array differently to the `Service` class).
For example, the following would change only the `allow_reset` and `refresh_time` values but keep everything else as the default:
If you want to change any of the defaults, you can pass in just the ones you want to change if you're happy to keep the rest as-is. Just alter the array at the top of the `index.php` script (or pass in the array differently to the `Service` class). For example, the following would change only the `allow_reset` and `refresh_time` values but keep everything else as the default:
```php
$opcache = (new Service([
@ -108,34 +93,34 @@ $opcache = (new Service([
]))->handle();
```
Or this example to give the tabs a slightly more "piratey" feel:
### Changing the look
```php
$opcache = (new Service([
'language_pack' => <<<EOJSON
{
"Overview": "Crows nest",
"Cached": "Thar Booty",
"Ignored": "The Black Spot",
"Preloaded": "Ready an' waitin', Cap'n",
"Reset cache": "Be gone, yer scurvy dogs!",
"Enable real-time update": "Keep a weathered eye",
"Disable real-time update": "Avert yer eyes, sea dog!"
}
EOJSON
]))->handle();
The interface has been split up to allow you to easily change the colours of the gui, or even the core components, should you wish.
The CSS for the interface is in the `build/_frontend/interface.scss` file. If you want to change the interface itself, update the `build/_frontend/interface.jsx` file - it's basically a set of ReactJS components.
If you update those files, you will want to build the interface again and have the new jsx/css put into use. To do that, run the command `php ./build/build.php` from the repo root (you will need `nodejs` and `npm` installed). Once running, you should see the output:
```
🐢 Installing node modules
🏗️ Building js and css
🚀 Creating single build file
💯 Done!
```
The build script will only need to install the `node_modules` once, so on subsequent builds it should be a fair bit quicker!
### The interface
The build process will create a compiled css file at `build/interface.css` and the javascript of the interface will be in `build/interface.js`. You could probably use both of these within your own frameworks and templating systems, should you wish.
#### Overview
The core PHP template used in the build process, and that acts to pass various bits of data to the ReactJS side of things, is located at `build/template.phps`. If you wanted to update the version of ReactJS used, or how the wrapper html is structured, then this would be the file you'd want to update.
The overview will show you all the core information. From here you'll be able to see what host and platform you're running on, what version of OPcache you're using, when it was last reset, the functions and directives available (with links to the php.net manual), and all the statistics associated with the OPcache (number of hits, memory used, free and wasted memory, and more).
### Overview
![Screenshot of the Overview tab](http://amnuts.com/images/opcache/screenshot/overview-v3.3.0.png)
The overview will show you all the core information. From here you'll be able to see what host and platform you're running on, what version of OPcache you're using, when it was last reset, the functions that are available, all the directives and all the statistics associated with the OPcache (number of hits, memory used, free and wasted memory, etc.)
#### Cached files
![Screenshot of the Overview tab](http://amnuts.com/images/opcache/screenshot/overview-v3.0.0.png)
### Cached files
All the files currently in the cache are listed here with their associated statistics.
@ -147,141 +132,32 @@ If you want to adjust the pagination length you can do so with the `per_page` co
![Screenshot of the Cached files list showing filtered results and pagination](http://amnuts.com/images/opcache/screenshot/cached-v3.png)
#### Ignored files
### Ignored files
If you have set up a list of files which you don't want cache by supplying an `opcache.blacklist_filename` value, then the list of files will be listed within this tab.
If you have not supplied that configuration option in the `php.ini` file then this tab will not be displayed. If you set the `allow_filelist` configuration option to `false` then this tab will not be displayed irrespective of your ini setting.
#### Preloaded files
### Preloaded files
PHP 7.4 introduced the ability to pre-load a set of files on server start by way of the `opcache.preload` setting in your `php.ini` file. If you have set that up then the list of files specifically pre-loaded will be listed within this tab.
As with the ignored file, if you have not supplied the ini setting, or the `allow_filelist` configuration option is `false`, then this tab will not be displayed.
#### Reset the cache
### Reset the cache
You can reset the whole cache as well as force individual files, or groups of files, to become invalidated so that they will be cached again.
Resetting can be disabled with the use of the configuration options `allow_reset` and `allow_invalidate`.
#### Real-time updates
### Real-time updates
The interface can poll every so often to get a fresh look at the opcache. You can change how often this happens with the configuration option `refresh_time`, which is in seconds.
When the real-time updates are active, the interface will automatically update all the values as needed.
Also, if you choose to invalidate any files or reset the cache it will do this without reloading the page, so the search term you've entered, or the page to which you've navigated do not get reset. If the real-time update is not on then the page will reload on any invalidation usage.
### Building it yourself
The interface has been designed around the principle of having just one file that anyone needs in order to get up and running. To fulfil this, there's a template file, language files, jsx and css, which are all used in order to create the interface and they're brought together in the build process.
This build process will allow you to change the language used, how the required 3rd-party javascript libraries are included, the look and feel, or even the core components, should you wish.
#### The build command
To run the build process, run the command `php ./build/build.php` from the repo root (you will need `nodejs` and `npm` already installed). Once running, you should see the output something like:
```
🐢 Installing node modules
🏗️ Building js and css
🚀 Creating single build file
🔗 Using remote js links from 'cloudflare'
💯 Done!
```
The build script will only need to install the `node_modules` once, so on subsequent builds it should be a fair bit quicker!
The build process will create a compiled css file at `build/interface.css` and the javascript of the interface will be in `build/interface.js`. You could probably use both of these within your own frameworks and templating systems, should you wish.
#### The style
The CSS for the interface is in the `build/_frontend/interface.scss` file. Make changes there if you want to change the colours or formatting.
If you make any changes to the scss file then you'll need to run the build script in order to see the changes.
#### The layout
If you want to change the interface itself, update the `build/_frontend/interface.jsx` file - it's basically a set of ReactJS components. This is where you can change the widget layout, how the file list works, pagination, etc.
Run the build script again should you make changes here.
#### The javascript
The wrapper PHP template used in the build process, and that acts to pass various bits of data to the ReactJS side of things, is located at `build/template.phps`. If you wanted to update the version of ReactJS used, or how the wrapper html is structured (such as wanting to pass additional things to the ReactJS side of things), then this would be the file you'd want to update.
The interface requires a few 3rd-party js files to function correctly. You have the option of being able to change where these are fetched (between CloudFare, JSDelivr, and Unpkg), or you can have js js completely local and in-line (for example, you have CSP policies in place and the remote urls are not whitelisted).
In order to change the location of the 3rd-party resources, use the `-r` or `--remote-js` option followed by either `cloudflare`, `jsdelivr`, or `unpkg`. For example, if you wanted to use _jsdelivr_ then you'd run the build command like this: `php ./build/build.php -r jsdelivr`. This defaults to `cloudflare`.
If you wanted to have the js in-line, then you can use the `-j` or `--local-js` flag when building, such as `php ./build/build.php -j`. This will fetch the remote script files and embed the js into main `index.php` file. If you want to build it again with remote files, run the command again without the flag. Fetching the files will take your `-r` option into consideration if you provide it.
#### The language
There's an old saying that goes, "If you know more than one language you're multilingual, if you don't you're British." Not only is that a damning indictment of the British mentality towards other languages, but also goes to explain why the UI has only so far been in English - because I am, for all my sins, British.
However, it is now possible to build the interface with a different language. Currently, thanks to contributors, French and Spanish are also supported. If anyone else wants to contribute additional language packs, please submit a PR!
If the language pack is in the `build/_languages/` directory then you can use that with the `-l` or `--lang` flag. For example, if there is a `fr.json` language pack then you can use `php ./build/build.php -l fr` in order to build with that language.
There have been a few composer scripts added to help with the building. They are, `composer build`, `composer build-french`, and `composer build-spanish`.
If you want to create a language file then `build/_languages/example.json` contains all you need. It's a simple json structure with the key being the English version which matches what's in the UI, and the value is what you're converting it to - which in the example file is just blank. If a value is empty or the index doesn't exist for a translation, then it'll just use the English version. This gives you the ability to replace some or all of the interface strings as you see fit.
To get started with a new language, copy the `example.json` to the language you want that doesn't already exist - for example, `pt-br.json` or `pirate.json`. Then fill in the translations into the values. Once done, rebuild with `php ./build/build.php -l pt-br` or `php ./build/build.php -l pirate`.
When the real-time updates are active the interface will automatically update all the values as needed. Also, if you choose to invalidate any files or reset the cache it will do this without reloading the page, so the search term you've entered, or the page you've navigated to do not get reset. If the real-time update is not on then the page will reload on any invalidation usage.
## Releases
**Version 3.5.5**\
Added Spanish translations thanks to @cvc90 (PR#110)
**Version 3.5.4**\
Better handling of whether JIT is enabled or disabled. Now also shows _why_ it might be disabled even if you have the setting turned on. The interface also disables the graph and memory stats correctly for JIT if it's disabled for any reason.
**Version 3.5.3**\
Worked around some inconsistencies with links in the opcache documentation on php.net.
**Version 3.5.2**\
Removed some warnings for PHP 8.2 by dropping the `namespace` and `use` statements in the bundled `index.php` file.
**Version 3.5.1**\
This is just 3.5.0 but with corrected version tags to make Packagist happy and correct my mistake. :facepalm:
**Version 3.5.0**\
This version changes how the build process includes the javascript.
* The `-j`/`--local-js` flag now embeds the javascript into the `index.php` file rather than having them as a separate files
* The `-r`/`--remote-js` option has been added to allow you to decide where you get the 3rd-party files from (either when fetched locally or when added as remote script links), with `cloudflare`, `jsdelivr`, or `unpkg` being available options
* CloudFlare has been set as the default for the remote js links
* Added PR#94 from @M-Falken
* Added PR#95 from @firassziedan
**Version 3.4.0**\
This version adds a little more info about the files in the cache, and allows a bit more configuration though the config and build script.
* Added new `datetime_format` config option for flexible formatting of date/time values
* Added the cached file's `modified` date/time to the output (when the file was either added or updated)
* You can now build the `index.php` file with the js files local rather than remote urls
* Added PR#83 from @Stevemoretz
* Added the ability to build the interface with a different language. If there's a language you want, and it's not there, please make a PR!
* Added French language pack from @bbalet (PR#91)
**Version 3.3.1**\
Just a few minor tweaks:
* Added more of an explanation to the JIT value
* Replaced date functions with \DateTime and related classes
* Updated README with troubleshooting and sponsorship info (and refined header levels)
**Version 3.3.0**\
Mostly added JIT information for PHP 8:
* Added JIT buffer graph (optionally able to turn it off)
* Added JIT information to the memory usage panel
* Improved the JIT information shown in the directives
* Fixed a long outstanding interface bug that allowed you to see the 'invalidate all' link even if invalidation option was `false`
If you want to enable JIT you have to put in a value for the [opcache.jit_buffer_size](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit-buffer-size) ini setting, else it's disabled by default.
If you're not using PHP 8, the interface will compensate and not show the additional JIT information.
**Version 3.2.1**\
Minor maintenance release to:
* Put back "spaceship operator" so PHP8 doesn't give deprecation warnings (must have been accidentally removed in a previous commit)
@ -360,21 +236,6 @@ Releases of the GUI are available at:
https://github.com/amnuts/opcache-gui/releases/
## Troubleshooting
### Use of PHP-FPM
A number of people have questioned whether the opcache-gui is working on their instance of PHP-FPM, as the files shown don't appear to be everything that's cached, and that's different to what Apache might show.
Essentially, that's expected behaviour. And thanks to a great comment from contributor [Michalng](https://github.com/amnuts/opcache-gui/issues/78#issuecomment-1008015099), this explanation should cover the difference:
> The interface can only show what it knows about the OPcache usage of its own OPcache instance, hence when it's accessed through Apache with mod_php, then it can only see the OPcache usage of that Apache webserver OPcache instance. When it's accessed with classic CGI, it can only see itself being cached as a new PHP and OPcache instance is created, in which case OPcache itself often doesn't make sense.
>
> To be able to monitor and manage the OPcache for all web applications, all need to use the same FastCGI, i.e. PHP-FPM instance.
>
> In case of Apache, one then often needs to actively configure it to not use it's internal mod_php but send PHP handler requests to the shared PHP-FPM server via mod_proxy_fcgi, which also requires using the event MPM. That is generally seen as the preferred setup nowadays, especially for high traffic websites. This is because every single incoming request with MPM prefork + mod_php creates an own child process taking additional time and memory, while with event MPM and dedicated PHP-FPM server a (usually) already waiting handler thread is used on Apache and on PHP end, consuming nearly no additional memory or time for process spawning.
### Making is compatible with PHP 7.0
The script requires PHP 7.1 or above. I'm not tempted to downgrade the code to make it compatible with version 7.0, and hopefully most people would have upgraded by now. But I really do appreciate that sometimes people just don't have the ability to change the version of PHP they use because it's out of their control. So if you're one of the unlucky ones, you can make the following changes to `index.php` (or `Service.php` and run the build script). For the lines:
@ -388,3 +249,7 @@ public function resetCache(?string $file = null): bool
```
It'll just be a case of removing the `?` from each of the params.
# License
MIT: http://acollington.mit-license.org/

View file

@ -67,16 +67,6 @@ class Interface extends React.Component {
return v ? !!v[2] : false;
};
txt = (text, ...args) => {
if (this.props.language !== null && this.props.language.hasOwnProperty(text) && this.props.language[text]) {
text = this.props.language[text];
}
args.forEach((arg, i) => {
text = text.replaceAll(`{${i}}`, arg);
});
return text;
};
render() {
const { opstate, realtimeRefresh, ...otherProps } = this.props;
return (
@ -88,13 +78,9 @@ class Interface extends React.Component {
resetting={this.state.resetting}
realtimeHandler={this.realtimeHandler}
resetHandler={this.resetHandler}
txt={this.txt}
/>
</header>
<Footer
version={this.props.opstate.version.gui}
txt={this.txt}
/>
<Footer version={this.props.opstate.version.gui} />
</>
);
}
@ -105,34 +91,29 @@ function MainNavigation(props) {
return (
<nav className="main-nav">
<Tabs>
<div label={props.txt("Overview")} tabId="overview" tabIndex={1}>
<div label="Overview" tabId="overview" tabIndex={1}>
<OverviewCounts
overview={props.opstate.overview}
highlight={props.highlight}
useCharts={props.useCharts}
txt={props.txt}
/>
<div id="info" className="tab-content-overview-info">
<GeneralInfo
start={props.opstate.overview && props.opstate.overview.readable.start_time || null}
reset={props.opstate.overview && props.opstate.overview.readable.last_restart_time || null}
version={props.opstate.version}
jit={props.opstate.jitState}
txt={props.txt}
/>
<Directives
directives={props.opstate.directives}
txt={props.txt}
/>
<Functions
functions={props.opstate.functions}
txt={props.txt}
/>
</div>
</div>
{
props.allow.filelist &&
<div label={props.txt("Cached")} tabId="cached" tabIndex={2}>
<div label="Cached" tabId="cached" tabIndex={2}>
<CachedFiles
perPageLimit={props.perPageLimit}
allFiles={props.opstate.files}
@ -140,35 +121,32 @@ function MainNavigation(props) {
debounceRate={props.debounceRate}
allow={{fileList: props.allow.filelist, invalidate: props.allow.invalidate}}
realtime={props.realtime}
txt={props.txt}
/>
</div>
}
{
(props.allow.filelist && props.opstate.blacklist.length &&
<div label={props.txt("Ignored")} tabId="ignored" tabIndex={3}>
<div label="Ignored" tabId="ignored" tabIndex={3}>
<IgnoredFiles
perPageLimit={props.perPageLimit}
allFiles={props.opstate.blacklist}
allow={{fileList: props.allow.filelist }}
txt={props.txt}
/>
</div>)
}
{
(props.allow.filelist && props.opstate.preload.length &&
<div label={props.txt("Preloaded")} tabId="preloaded" tabIndex={4}>
<div label="Preloaded" tabId="preloaded" tabIndex={4}>
<PreloadedFiles
perPageLimit={props.perPageLimit}
allFiles={props.opstate.preload}
allow={{fileList: props.allow.filelist }}
txt={props.txt}
/>
</div>)
}
{
props.allow.reset &&
<div label={props.txt("Reset cache")} tabId="resetCache"
<div label="Reset cache" tabId="resetCache"
className={`nav-tab-link-reset${props.resetting ? ' is-resetting pulse' : ''}`}
handler={props.resetHandler}
tabIndex={5}
@ -176,7 +154,7 @@ function MainNavigation(props) {
}
{
props.allow.realtime &&
<div label={props.txt(`${props.realtime ? 'Disable' : 'Enable'} real-time update`)} tabId="toggleRealtime"
<div label={`${props.realtime ? 'Disable' : 'Enable'} real-time update`} tabId="toggleRealtime"
className={`nav-tab-link-realtime${props.realtime ? ' live-update pulse' : ''}`}
handler={props.realtimeHandler}
tabIndex={6}
@ -278,16 +256,15 @@ function OverviewCounts(props) {
if (props.overview === false) {
return (
<p class="file-cache-only">
{props.txt(`You have <i>opcache.file_cache_only</i> turned on. As a result, the memory information is not available. Statistics and file list may also not be returned by <i>opcache_get_statistics()</i>.`)}
You have <i>opcache.file_cache_only</i> turned on. As a result, the memory information is not available. Statistics and file list may also not be returned by <i>opcache_get_statistics()</i>.
</p>
);
}
const graphList = [
{id: 'memoryUsageCanvas', title: props.txt('memory'), show: props.highlight.memory, value: props.overview.used_memory_percentage},
{id: 'hitRateCanvas', title: props.txt('hit rate'), show: props.highlight.hits, value: props.overview.hit_rate_percentage},
{id: 'keyUsageCanvas', title: props.txt('keys'), show: props.highlight.keys, value: props.overview.used_key_percentage},
{id: 'jitUsageCanvas', title: props.txt('jit buffer'), show: props.highlight.jit, value: props.overview.jit_buffer_used_percentage}
{id: 'memoryUsageCanvas', title: 'memory', show: props.highlight.memory, value: props.overview.used_memory_percentage},
{id: 'hitRateCanvas', title: 'hit rate', show: props.highlight.hits, value: props.overview.hit_rate_percentage},
{id: 'keyUsageCanvas', title: 'keys', show: props.highlight.keys, value: props.overview.used_key_percentage}
];
return (
@ -310,10 +287,6 @@ function OverviewCounts(props) {
wasted={props.overview.readable.wasted_memory}
preload={props.overview.readable.preload_memory || null}
wastedPercent={props.overview.wasted_percentage}
jitBuffer={props.overview.readable.jit_buffer_size || null}
jitBufferFree={props.overview.readable.jit_buffer_free || null}
jitBufferFreePercentage={props.overview.jit_buffer_used_percentage || null}
txt={props.txt}
/>
<StatisticsPanel
num_cached_scripts={props.overview.readable.num_cached_scripts}
@ -322,7 +295,6 @@ function OverviewCounts(props) {
blacklist_miss={props.overview.readable.blacklist_miss}
num_cached_keys={props.overview.readable.num_cached_keys}
max_cached_keys={props.overview.readable.max_cached_keys}
txt={props.txt}
/>
{props.overview.readable.interned &&
<InternedStringsPanel
@ -330,7 +302,6 @@ function OverviewCounts(props) {
strings_used_memory={props.overview.readable.interned.strings_used_memory}
strings_free_memory={props.overview.readable.interned.strings_free_memory}
number_of_strings={props.overview.readable.interned.number_of_strings}
txt={props.txt}
/>
}
</div>
@ -342,22 +313,15 @@ function GeneralInfo(props) {
return (
<table className="tables general-info-table">
<thead>
<tr><th colSpan="2">{props.txt('General info')}</th></tr>
<tr><th colSpan="2">General info</th></tr>
</thead>
<tbody>
<tr><td>Zend OPcache</td><td>{props.version.version}</td></tr>
<tr><td>PHP</td><td>{props.version.php}</td></tr>
<tr><td>{props.txt('Host')}</td><td>{props.version.host}</td></tr>
<tr><td>{props.txt('Server Software')}</td><td>{props.version.server}</td></tr>
{ props.start ? <tr><td>{props.txt('Start time')}</td><td>{props.start}</td></tr> : null }
{ props.reset ? <tr><td>{props.txt('Last reset')}</td><td>{props.reset}</td></tr> : null }
<tr>
<td>{props.txt('JIT enabled')}</td>
<td>
{props.txt(props.jit.enabled ? "Yes" : "No")}
{props.jit.reason && (<span dangerouslySetInnerHTML={{__html: ` (${props.jit.reason})` }} />)}
</td>
</tr>
<tr><td>Host</td><td>{props.version.host}</td></tr>
<tr><td>Server Software</td><td>{props.version.server}</td></tr>
{ props.start ? <tr><td>Start time</td><td>{props.start}</td></tr> : null }
{ props.reset ? <tr><td>Last reset</td><td>{props.reset}</td></tr> : null }
</tbody>
</table>
);
@ -369,9 +333,7 @@ function Directives(props) {
return (
<ul className="directive-list">{
directive.v.map((item, key) => {
return Array.isArray(item)
? <li key={"sublist_" + key}>{directiveList({v:item})}</li>
: <li key={key}>{item}</li>
return <li key={key}>{item}</li>
})
}</ul>
);
@ -384,9 +346,9 @@ function Directives(props) {
});
let vShow;
if (directive.v === true || directive.v === false) {
vShow = React.createElement('i', {}, props.txt(directive.v.toString()));
vShow = React.createElement('i', {}, directive.v.toString());
} else if (directive.v === '') {
vShow = React.createElement('i', {}, props.txt('no value'));
vShow = React.createElement('i', {}, 'no value');
} else {
if (Array.isArray(directive.v)) {
vShow = directiveList(directive);
@ -394,25 +356,10 @@ function Directives(props) {
vShow = directive.v;
}
}
let directiveLink = (name) => {
if (name === 'opcache.jit_max_recursive_returns') {
return 'opcache.jit-max-recursive-return';
}
return (
[
'opcache.file_update_protection',
'opcache.huge_code_pages',
'opcache.lockfile_path',
'opcache.opt_debug_level',
].includes(name)
? name
: name.replace(/_/g,'-')
);
}
return (
<tr key={directive.k}>
<td title={props.txt('View {0} manual entry', directive.k)}><a href={'https://php.net/manual/en/opcache.configuration.php#ini.'
+ directiveLink(directive.k)} target="_blank">{dShow}</a></td>
<td title={'View ' + directive.k + ' manual entry'}><a href={'http://php.net/manual/en/opcache.configuration.php#ini.'
+ (directive.k).replace(/_/g,'-')} target="_blank">{dShow}</a></td>
<td>{vShow}</td>
</tr>
);
@ -420,7 +367,7 @@ function Directives(props) {
return (
<table className="tables directives-table">
<thead><tr><th colSpan="2">{props.txt('Directives')}</th></tr></thead>
<thead><tr><th colSpan="2">Directives</th></tr></thead>
<tbody>{directiveNodes}</tbody>
</table>
);
@ -430,10 +377,10 @@ function Functions(props) {
return (
<div id="functions">
<table className="tables">
<thead><tr><th>{props.txt('Available functions')}</th></tr></thead>
<thead><tr><th>Available functions</th></tr></thead>
<tbody>
{props.functions.map(f =>
<tr key={f}><td><a href={"https://php.net/"+f} title={props.txt('View manual page')} target="_blank">{f}</a></td></tr>
<tr key={f}><td><a href={"http://php.net/"+f} title="View manual page" target="_blank">{f}</a></td></tr>
)}
</tbody>
</table>
@ -666,13 +613,11 @@ function MemoryUsagePanel(props) {
<div className="widget-panel">
<h3 className="widget-header">memory usage</h3>
<div className="widget-value widget-info">
<p><b>{props.txt('total memory')}:</b> {props.total}</p>
<p><b>{props.txt('used memory')}:</b> {props.used}</p>
<p><b>{props.txt('free memory')}:</b> {props.free}</p>
{ props.preload && <p><b>{props.txt('preload memory')}:</b> {props.preload}</p> }
<p><b>{props.txt('wasted memory')}:</b> {props.wasted} ({props.wastedPercent}%)</p>
{ props.jitBuffer && <p><b>{props.txt('jit buffer')}:</b> {props.jitBuffer}</p> }
{ props.jitBufferFree && <p><b>{props.txt('jit buffer free')}:</b> {props.jitBufferFree} ({100 - props.jitBufferFreePercentage}%)</p> }
<p><b>total memory:</b> {props.total}</p>
<p><b>used memory:</b> {props.used}</p>
<p><b>free memory:</b> {props.free}</p>
{ props.preload && <p><b>preload memory:</b> {props.preload}</p> }
<p><b>wasted memory:</b> {props.wasted} ({props.wastedPercent}%)</p>
</div>
</div>
);
@ -682,14 +627,14 @@ function MemoryUsagePanel(props) {
function StatisticsPanel(props) {
return (
<div className="widget-panel">
<h3 className="widget-header">{props.txt('opcache statistics')}</h3>
<h3 className="widget-header">opcache statistics</h3>
<div className="widget-value widget-info">
<p><b>{props.txt('number of cached')} files:</b> {props.num_cached_scripts}</p>
<p><b>{props.txt('number of hits')}:</b> {props.hits}</p>
<p><b>{props.txt('number of misses')}:</b> {props.misses}</p>
<p><b>{props.txt('blacklist misses')}:</b> {props.blacklist_miss}</p>
<p><b>{props.txt('number of cached keys')}:</b> {props.num_cached_keys}</p>
<p><b>{props.txt('max cached keys')}:</b> {props.max_cached_keys}</p>
<p><b>number of cached files:</b> {props.num_cached_scripts}</p>
<p><b>number of hits:</b> {props.hits}</p>
<p><b>number of misses:</b> {props.misses}</p>
<p><b>blacklist misses:</b> {props.blacklist_miss}</p>
<p><b>number of cached keys:</b> {props.num_cached_keys}</p>
<p><b>max cached keys:</b> {props.max_cached_keys}</p>
</div>
</div>
);
@ -699,12 +644,12 @@ function StatisticsPanel(props) {
function InternedStringsPanel(props) {
return (
<div className="widget-panel">
<h3 className="widget-header">{props.txt('interned strings usage')}</h3>
<h3 className="widget-header">interned strings usage</h3>
<div className="widget-value widget-info">
<p><b>{props.txt('buffer size')}:</b> {props.buffer_size}</p>
<p><b>{props.txt('used memory')}:</b> {props.strings_used_memory}</p>
<p><b>{props.txt('free memory')}:</b> {props.strings_free_memory}</p>
<p><b>{props.txt('number of strings')}:</b> {props.number_of_strings}</p>
<p><b>buffer size:</b> {props.buffer_size}</p>
<p><b>used memory:</b> {props.strings_used_memory}</p>
<p><b>free memory:</b> {props.strings_free_memory}</p>
<p><b>number of strings:</b> {props.number_of_strings}</p>
</div>
</div>
);
@ -779,7 +724,7 @@ class CachedFiles extends React.Component {
}
if (this.props.allFiles.length === 0) {
return <p>{this.props.txt('No files have been cached or you have <i>opcache.file_cache_only</i> turned on')}</p>;
return <p>No files have been cached or you have <i>opcache.file_cache_only</i> turned on</p>;
}
const { searchTerm, currentPage } = this.state;
@ -799,19 +744,18 @@ class CachedFiles extends React.Component {
);
const allFilesTotal = this.props.allFiles.length;
const showingTotal = filesInSearch.length;
const showing = showingTotal !== allFilesTotal ? ", {1} showing due to filter '{2}'" : "";
return (
<div>
<form action="#">
<label htmlFor="frmFilter">{this.props.txt('Start typing to filter on script path')}</label><br/>
<label htmlFor="frmFilter">Start typing to filter on script path</label><br/>
<input type="text" name="filter" id="frmFilter" className="file-filter" onChange={e => {this.setSearchTerm(e.target.value)}} />
</form>
<h3>{this.props.txt(`{0} files cached${showing}`, allFilesTotal, showingTotal, this.state.searchTerm)}</h3>
<h3>{allFilesTotal} files cached{showingTotal !== allFilesTotal && `, ${showingTotal} showing due to filter '${this.state.searchTerm}'`}</h3>
{ this.props.allow.invalidate && this.state.searchTerm && showingTotal !== allFilesTotal &&
<p><a href={`?invalidate_searched=${encodeURIComponent(this.state.searchTerm)}`} onClick={this.handleInvalidate}>{this.props.txt('Invalidate all matching files')}</a></p>
{ this.state.searchTerm && showingTotal !== allFilesTotal &&
<p><a href={`?invalidate_searched=${encodeURIComponent(this.state.searchTerm)}`} onClick={this.handleInvalidate}>Invalidate all matching files</a></p>
}
<div className="paginate-filter">
@ -821,19 +765,17 @@ class CachedFiles extends React.Component {
pageNeighbours={2}
onPageChanged={this.onPageChanged}
refresh={this.state.refreshPagination}
txt={this.props.txt}
/>}
<nav className="filter" aria-label={this.props.txt('Sort order')}>
<nav className="filter" aria-label="Sort order">
<select name="sortBy" onChange={this.changeSort} value={this.state.sortBy}>
<option value="last_used_timestamp">{this.props.txt('Last used')}</option>
<option value="last_modified">{this.props.txt('Last modified')}</option>
<option value="full_path">{this.props.txt('Path')}</option>
<option value="hits">{this.props.txt('Number of hits')}</option>
<option value="memory_consumption">{this.props.txt('Memory consumption')}</option>
<option value="last_used_timestamp">Last used</option>
<option value="full_path">Path</option>
<option value="hits">Number of hits</option>
<option value="memory_consumption">Memory consumption</option>
</select>
<select name="sortDir" onChange={this.changeSort} value={this.state.sortDir}>
<option value="desc">{this.props.txt('Descending')}</option>
<option value="asc">{this.props.txt('Ascending')}</option>
<option value="desc">Descending</option>
<option value="asc">Ascending</option>
</select>
</nav>
</div>
@ -841,7 +783,7 @@ class CachedFiles extends React.Component {
<table className="tables cached-list-table">
<thead>
<tr>
<th>{this.props.txt('Script')}</th>
<th>Script</th>
</tr>
</thead>
<tbody>
@ -850,7 +792,6 @@ class CachedFiles extends React.Component {
key={file.full_path}
canInvalidate={this.props.allow.invalidate}
realtime={this.props.realtime}
txt={this.props.txt}
{...file}
/>
})}
@ -881,15 +822,14 @@ class CachedFile extends React.Component {
<td>
<span className="file-pathname">{this.props.full_path}</span>
<span className="file-metainfo">
<b>{this.props.txt('hits')}: </b><span>{this.props.readable.hits}, </span>
<b>{this.props.txt('memory')}: </b><span>{this.props.readable.memory_consumption}, </span>
{ this.props.last_modified && <><b>{this.props.txt('last modified')}: </b><span>{this.props.last_modified}, </span></> }
<b>{this.props.txt('last used')}: </b><span>{this.props.last_used}</span>
<b>hits: </b><span>{this.props.readable.hits}, </span>
<b>memory: </b><span>{this.props.readable.memory_consumption}, </span>
<b>last used: </b><span>{this.props.last_used}</span>
</span>
{ !this.props.timestamp && <span className="invalid file-metainfo"> - {this.props.txt('has been invalidated')}</span> }
{ !this.props.timestamp && <span className="invalid file-metainfo"> - has been invalidated</span> }
{ this.props.canInvalidate && <span>,&nbsp;<a className="file-metainfo"
href={'?invalidate=' + this.props.full_path} data-file={this.props.full_path}
onClick={this.handleInvalidate}>{this.props.txt('force file invalidation')}</a></span> }
onClick={this.handleInvalidate}>force file invalidation</a></span> }
</td>
</tr>
);
@ -919,7 +859,7 @@ class IgnoredFiles extends React.Component {
}
if (this.props.allFiles.length === 0) {
return <p>{this.props.txt('No files have been ignored via <i>opcache.blacklist_filename</i>')}</p>;
return <p>No files have been ignored via <i>opcache.blacklist_filename</i></p>;
}
const { currentPage } = this.state;
@ -932,7 +872,7 @@ class IgnoredFiles extends React.Component {
return (
<div>
<h3>{this.props.txt('{0} ignore file locations', allFilesTotal)}</h3>
<h3>{allFilesTotal} ignore file locations</h3>
{this.doPagination && <Pagination
totalRecords={allFilesTotal}
@ -940,11 +880,10 @@ class IgnoredFiles extends React.Component {
pageNeighbours={2}
onPageChanged={this.onPageChanged}
refresh={this.state.refreshPagination}
txt={this.props.txt}
/>}
<table className="tables ignored-list-table">
<thead><tr><th>{this.props.txt('Path')}</th></tr></thead>
<thead><tr><th>Path</th></tr></thead>
<tbody>
{filesInPage.map((file, index) => {
return <tr key={file}><td>{file}</td></tr>
@ -979,7 +918,7 @@ class PreloadedFiles extends React.Component {
}
if (this.props.allFiles.length === 0) {
return <p>{this.props.txt('No files have been preloaded <i>opcache.preload</i>')}</p>;
return <p>No files have been preloaded <i>opcache.preload</i></p>;
}
const { currentPage } = this.state;
@ -992,7 +931,7 @@ class PreloadedFiles extends React.Component {
return (
<div>
<h3>{this.props.txt('{0} preloaded files', allFilesTotal)}</h3>
<h3>{allFilesTotal} preloaded files</h3>
{this.doPagination && <Pagination
totalRecords={allFilesTotal}
@ -1000,11 +939,10 @@ class PreloadedFiles extends React.Component {
pageNeighbours={2}
onPageChanged={this.onPageChanged}
refresh={this.state.refreshPagination}
txt={this.props.txt}
/>}
<table className="tables preload-list-table">
<thead><tr><th>{this.props.txt('Path')}</th></tr></thead>
<thead><tr><th>Path</th></tr></thead>
<tbody>
{filesInPage.map((file, index) => {
return <tr key={file}><td>{file}</td></tr>
@ -1138,15 +1076,15 @@ class Pagination extends React.Component {
return (
<React.Fragment key={index}>
<li className="page-item arrow">
<a className="page-link" href="#" aria-label={this.props.txt('Previous')} onClick={this.handleJumpLeft}>
<a className="page-link" href="#" aria-label="Previous" onClick={this.handleJumpLeft}>
<span aria-hidden="true"></span>
<span className="sr-only">{this.props.txt('Jump back')}</span>
<span className="sr-only">Jump back</span>
</a>
</li>
<li className="page-item arrow">
<a className="page-link" href="#" aria-label={this.props.txt('Previous')} onClick={this.handleMoveLeft}>
<a className="page-link" href="#" aria-label="Previous" onClick={this.handleMoveLeft}>
<span aria-hidden="true"></span>
<span className="sr-only">{this.props.txt('Previous page')}</span>
<span className="sr-only">Previous page</span>
</a>
</li>
</React.Fragment>
@ -1156,15 +1094,15 @@ class Pagination extends React.Component {
return (
<React.Fragment key={index}>
<li className="page-item arrow">
<a className="page-link" href="#" aria-label={this.props.txt('Next')} onClick={this.handleMoveRight}>
<a className="page-link" href="#" aria-label="Next" onClick={this.handleMoveRight}>
<span aria-hidden="true"></span>
<span className="sr-only">{this.props.txt('Next page')}</span>
<span className="sr-only">Next page</span>
</a>
</li>
<li className="page-item arrow">
<a className="page-link" href="#" aria-label={this.props.txt('Next')} onClick={this.handleJumpRight}>
<a className="page-link" href="#" aria-label="Next" onClick={this.handleJumpRight}>
<span aria-hidden="true"></span>
<span className="sr-only">{this.props.txt('Jump forward')}</span>
<span className="sr-only">Jump forward</span>
</a>
</li>
</React.Fragment>
@ -1190,13 +1128,8 @@ function Footer(props) {
<footer className="main-footer">
<a className="github-link" href="https://github.com/amnuts/opcache-gui"
target="_blank"
title={props.txt("opcache-gui (currently version {0}) on GitHub", props.version)}
>https://github.com/amnuts/opcache-gui - {props.txt("version {0}", props.version)}</a>
<a className="sponsor-link" href="https://github.com/sponsors/amnuts"
target="_blank"
title={props.txt("Sponsor this project and author on GitHub")}
>{props.txt("Sponsor this project")}</a>
title="opcache-gui (currently version {props.version}) on GitHub"
>https://github.com/amnuts/opcache-gui - version {props.version}</a>
</footer>
);
}

View file

@ -260,10 +260,6 @@ $footer-border-color: #CCC;
&:last-child {
margin-bottom: 0;
}
ul {
margin-top: 1.5em;
}
}
}
@ -286,8 +282,7 @@ $footer-border-color: #CCC;
.nav-tab-link-reset,
.nav-tab-link-realtime,
.github-link,
.sponsor-link {
.github-link {
background-repeat: no-repeat;
background-color: transparent;
}
@ -297,17 +292,21 @@ $footer-border-color: #CCC;
background-position: 24px 50%;
}
.github-link {
background-position: 5px 50%;
}
.main-footer {
border-top: 1px solid $footer-border-color;
padding: 1em 2em;
}
.github-link,
.sponsor-link {
.github-link {
background-position: 0 50%;
padding: 2em 0 2em 2.3em;
text-decoration: none;
opacity: 0.7;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.19em" height="1em" viewBox="0 0 1664 1408"><path d="M640 960q0 40-12.5 82t-43 76t-72.5 34t-72.5-34t-43-76t-12.5-82t12.5-82t43-76t72.5-34t72.5 34t43 76t12.5 82zm640 0q0 40-12.5 82t-43 76t-72.5 34t-72.5-34t-43-76t-12.5-82t12.5-82t43-76t72.5-34t72.5 34t43 76t12.5 82zm160 0q0-120-69-204t-187-84q-41 0-195 21q-71 11-157 11t-157-11q-152-21-195-21q-118 0-187 84t-69 204q0 88 32 153.5t81 103t122 60t140 29.5t149 7h168q82 0 149-7t140-29.5t122-60t81-103t32-153.5zm224-176q0 207-61 331q-38 77-105.5 133t-141 86t-170 47.5t-171.5 22t-167 4.5q-78 0-142-3t-147.5-12.5t-152.5-30t-137-51.5t-121-81t-86-115Q0 992 0 784q0-237 136-396q-27-82-27-170q0-116 51-218q108 0 190 39.5T539 163q147-35 309-35q148 0 280 32q105-82 187-121t189-39q51 102 51 218q0 87-27 168q136 160 136 398z" fill="#{toRGB($nav-icon-color)}"/></svg>');
font-size: 80%;
&:hover {
@ -315,15 +314,6 @@ $footer-border-color: #CCC;
}
}
.github-link {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.19em" height="1em" viewBox="0 0 1664 1408"><path d="M640 960q0 40-12.5 82t-43 76t-72.5 34t-72.5-34t-43-76t-12.5-82t12.5-82t43-76t72.5-34t72.5 34t43 76t12.5 82zm640 0q0 40-12.5 82t-43 76t-72.5 34t-72.5-34t-43-76t-12.5-82t12.5-82t43-76t72.5-34t72.5 34t43 76t12.5 82zm160 0q0-120-69-204t-187-84q-41 0-195 21q-71 11-157 11t-157-11q-152-21-195-21q-118 0-187 84t-69 204q0 88 32 153.5t81 103t122 60t140 29.5t149 7h168q82 0 149-7t140-29.5t122-60t81-103t32-153.5zm224-176q0 207-61 331q-38 77-105.5 133t-141 86t-170 47.5t-171.5 22t-167 4.5q-78 0-142-3t-147.5-12.5t-152.5-30t-137-51.5t-121-81t-86-115Q0 992 0 784q0-237 136-396q-27-82-27-170q0-116 51-218q108 0 190 39.5T539 163q147-35 309-35q148 0 280 32q105-82 187-121t189-39q51 102 51 218q0 87-27 168q136 160 136 398z" fill="#{toRGB($nav-icon-color)}"/></svg>');
}
.sponsor-link {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewbox="0 0 24 24"><path fill="crimson" d="M12 21.35l-1.45-1.32c-5.15-4.67-8.55-7.75-8.55-11.53 0-3.08 2.42-5.5 5.5-5.5 1.74 0 3.41.81 4.5 2.09 1.09-1.28 2.76-2.09 4.5-2.09 3.08 0 5.5 2.42 5.5 5.5 0 3.78-3.4 6.86-8.55 11.54l-1.45 1.31z"/></svg>');
margin-left: 2em;
}
.file-cache-only {
margin-top: 0;
}

View file

@ -1,113 +0,0 @@
{
"(unsafe) Collect constants": "(inestable) Recoger las constantes",
"++, +=, series of jumps": "++, +=, series de saltos",
"Adjust used stack": "Ajustar pila usada",
"Ascending": "Ascendente",
"Available functions": "Funciones disponibles",
"blacklist misses": "fallos en la blacklist",
"buffer size": "tamaño del búfer",
"Cached": "En cache",
"CALL GRAPH optimization": "Optimización CALL GRAPH",
"CFG based optimization": "Optimización basada en CFG",
"Compile all functions on script load": "Compilar todas las funciones al cargar el script",
"Compile functions on first execution": "Compilar funciones en la primera ejecución",
"Constant conversion and jumps": "Conversión constante y saltos",
"CPU-specific optimization": "Optimización específica de la CPU",
"CSE, STRING construction": "Construcción CSE, STRING",
"Currently unused": "Actualmente sin utilizar",
"DCE (dead code elimination)": "DCE (eliminación del código muerto)",
"Descending": "Descendente",
"DFA based optimization": "Optimización basada en DFA",
"Directives": "Directivas",
"Disable CPU-specific optimization": "Desactivar la optimización específica de la CPU",
"Disable real-time update": "Desactivar la actualización en tiempo real",
"Do not perform register allocation": "No realizar la asignación de registros",
"Enable real-time update": "Activar la actualización en tiempo real",
"Enable use of AVX, if the CPU supports it": "Habilitar el uso de AVX, si la CPU lo soporta",
"false": "falso",
"File list pagination": "Paginación de la lista de archivos",
"force file invalidation": "forzar la invalidación de archivos",
"free memory": "memoria libre",
"General info": "Información general",
"has been invalidated": "se ha invalidado",
"hit rate": "índice de aciertos",
"hits": "hits",
"Host": "Host",
"Ignored": "Ignorado",
"INIT_FCALL_BY_NAME -> DO_FCALL": "INIT_FCALL_BY_NAME -> DO_FCALL",
"Inline functions": "Funciones inline",
"Inline VM handlers": "Controladores de VM en línea",
"interned strings usage": "utilización de cadenas internas",
"Invalidate all matching files": "Invalidar todos los ficheros coincidentes",
"jit buffer free": "búfer jit libre",
"jit buffer": "búfer jit",
"keys": "llaves",
"Last modified": "Última modificación",
"last modified": "última modificaciónn",
"Last reset": "Último reinicio",
"Last used": "Último uso",
"last used": "último uso",
"max cached keys": "llaves máximas en caché",
"Memory consumption": "Consumo de memoria",
"memory usage": "uso de memoria",
"memory": "memoria",
"Merge equal constants": "Fusionar las constantes iguales",
"Minimal JIT (call standard VM handlers)": "JIT mínimo (llame a controladores de VM estándar)",
"never": "nunca",
"Next": "Siguiente",
"No files have been cached or you have <i>opcache.file_cache_only<\/i> turned on": "No se han almacenado archivos en caché o tiene <i>opcache.file_cache_only<\/i> activado",
"No files have been ignored via <i>opcache.blacklist_filename<\/i>": "No se han ignorado archivos a través de <i>opcache.blacklist_filename<\/i>",
"No files have been preloaded <i>opcache.preload<\/i>": "No se han precargado archivos <i>opcache.preload<\/i>",
"No JIT": "No JIT",
"no value": "sin valor",
"NOP removal": "Eliminación de NOP",
"number of cached files": "número de archivos en caché",
"number of cached keys": "número de llaves en caché",
"Number of hits": "Número de hits",
"number of hits": "número de hits",
"number of misses": "número de misses",
"number of strings": "número de cadenas",
"opcache statistics": "estadísticas de opcache",
"Optimization level": "Nivel de optimización",
"Optimize whole script": "Optimizar todo el script",
"Overview": "Visión general",
"Path": "Ruta",
"Perform block-local register allocation": "Asignación de registros a nivel local de bloque",
"Perform global register allocation": "Realizar la asignación global de registros",
"preload memory": "precargar la memoria",
"Preloaded": "Precargado",
"Previous": "Anterior",
"Profile functions on first request and compile the hottest functions afterwards": "Perfile las funciones en la primera solicitud y luego recopile las funciones más populares",
"Profile on the fly and compile hot functions": "Perfilar sobre la marcha y compilar funciones en caliente",
"Register allocation": "Asignación de registros",
"Remove unused variables": "Eliminar variables no utilizadas",
"Reset cache": "Restablecer la caché",
"SCCP (constant propagation)": "SCCP (propagación de constantes)",
"Script": "Script",
"Server Software": "Software de servidor",
"Sort order": "Orden de clasificación",
"Start time": "Hora de inicio",
"Start typing to filter on script path": "Empiece a escribir para filtrar por ruta del script",
"TMP VAR usage": "Utilización de TMP VAR",
"total memory": "memoria total",
"Trigger": "Disparador",
"true": "verdadero",
"Use call graph": "Utilizar el gráfico de llamadas",
"Use tracing JIT. Profile on the fly and compile traces for hot code segments": "Utilice el trazado JIT. Perfila sobre la marcha y compila trazas para segmentos de código calientes.",
"Use type inference": "Utilizar la inferencia de tipos",
"used memory": "memoria utilizada",
"View manual page": "Ver la página del manual",
"View {0} manual entry": "Ver {0} entrada manual",
"wasted memory": "memoria perdida",
"You have <i>opcache.file_cache_only<\/i> turned on. As a result, the memory information is not available. Statistics and file list may also not be returned by <i>opcache_get_statistics()<\/i>.": "Tienes <i>opcache.file_cache_only</i> activado. Como resultado, la información de la memoria no está disponible. Es posible que las estadísticas y la lista de archivos tampoco sean devueltas por <i>opcache_get_statistics()<\/i>.",
"{0} files cached": "{0} archivos almacenados en caché",
"{0} files cached, {1} showing due to filter '{2}'": "{0} archivos almacenados en caché, {1} se visualizan debido al filtro '{2}'",
"{0} ignore file locations": "{0} ignorar ubicaciones de archivos",
"{0} preloaded files": "{0} archivos precargados",
"JIT enabled": "JIT activado",
"disabled due to <i>opcache.jit</i> setting": "ddesactivado debido a la configuración de <i>opcache.jit</i>",
"the <i>opcache.jit_buffer_size</i> must be set to fully enable JIT": "el <i>opcache.jit_buffer_size</i> debe estar configurado para habilitar completamente JIT",
"incompatible with extensions that override <i>zend_execute_ex()</i>, such as <i>xdebug</i>": "incompatible con las extensiones que anulan <i>zend_execute_ex()</i>, como <i>xdebug</i>",
"Yes": "Sí",
"No": "No"
}

View file

@ -1,116 +0,0 @@
{
"(unsafe) Collect constants": "",
"++, +=, series of jumps": "",
"Adjust used stack": "",
"Ascending": "",
"Available functions": "",
"blacklist misses": "",
"buffer size": "",
"Cached": "",
"CALL GRAPH optimization": "",
"CFG based optimization": "",
"Compile all functions on script load": "",
"Compile functions on first execution": "",
"Constant conversion and jumps": "",
"CPU-specific optimization": "",
"CSE, STRING construction": "",
"Currently unused": "",
"DCE (dead code elimination)": "",
"Descending": "",
"DFA based optimization": "",
"Directives": "",
"Disable CPU-specific optimization": "",
"Disable real-time update": "",
"disabled due to <i>opcache.jit</i> setting": "",
"Do not perform register allocation": "",
"Enable real-time update": "",
"Enable use of AVX, if the CPU supports it": "",
"false": "",
"File list pagination": "",
"force file invalidation": "",
"free memory": "",
"General info": "",
"has been invalidated": "",
"hit rate": "",
"hits": "",
"Host": "",
"Ignored": "",
"incompatible with extensions that override <i>zend_execute_ex()</i>, such as <i>xdebug</i>": "",
"INIT_FCALL_BY_NAME -> DO_FCALL": "",
"Inline functions": "",
"Inline VM handlers": "",
"interned strings usage": "",
"Invalidate all matching files": "",
"jit buffer free": "",
"jit buffer": "",
"JIT enabled": "",
"keys": "",
"Last modified": "",
"last modified": "",
"Last reset": "",
"Last used": "",
"last used": "",
"max cached keys": "",
"Memory consumption": "",
"memory usage": "",
"memory": "",
"Merge equal constants": "",
"Minimal JIT (call standard VM handlers)": "",
"never": "",
"Next": "",
"No files have been cached or you have <i>opcache.file_cache_only<\/i> turned on": "",
"No files have been ignored via <i>opcache.blacklist_filename<\/i>": "",
"No files have been preloaded <i>opcache.preload<\/i>": "",
"No JIT": "",
"no value": "",
"No": "",
"NOP removal": "",
"number of cached files": "",
"number of cached keys": "",
"Number of hits": "",
"number of hits": "",
"number of misses": "",
"number of strings": "",
"opcache statistics": "",
"Optimization level": "",
"Optimize whole script": "",
"Overview": "",
"Path": "",
"Perform block-local register allocation": "",
"Perform global register allocation": "",
"preload memory": "",
"Preloaded": "",
"Previous": "",
"Profile functions on first request and compile the hottest functions afterwards": "",
"Profile on the fly and compile hot functions": "",
"Register allocation": "",
"Remove unused variables": "",
"Reset cache": "",
"SCCP (constant propagation)": "",
"Script": "",
"Server Software": "",
"Sort order": "",
"Sponsor this project and author on GitHub": "",
"Sponsor this project": "",
"Start time": "",
"Start typing to filter on script path": "",
"the <i>opcache.jit_buffer_size</i> must be set to fully enable JIT": "",
"TMP VAR usage": "",
"total memory": "",
"Trigger": "",
"true": "",
"Use call graph": "",
"Use tracing JIT. Profile on the fly and compile traces for hot code segments": "",
"Use type inference": "",
"used memory": "",
"version {0}": "",
"View manual page": "",
"View {0} manual entry": "",
"wasted memory": "",
"Yes": "",
"You have <i>opcache.file_cache_only<\/i> turned on. As a result, the memory information is not available. Statistics and file list may also not be returned by <i>opcache_get_statistics()<\/i>.": "",
"{0} files cached": "",
"{0} files cached, {1} showing due to filter '{2}'": "",
"{0} ignore file locations": "",
"{0} preloaded files": ""
}

View file

@ -1,113 +0,0 @@
{
"(unsafe) Collect constants": "(instable) Collecter les constantes",
"++, +=, series of jumps": "++, +=, séries de sauts",
"Adjust used stack": "Ajuster la pile utilisée",
"Ascending": "Croissant",
"Available functions": "Fonctions disponibles",
"blacklist misses": "Ratés mis sur liste noire",
"buffer size": "taille du tampon",
"Cached": "En cache",
"CALL GRAPH optimization": "Optimisation CALL GRAPH",
"CFG based optimization": "Optimisation basée sur CFG",
"Compile all functions on script load": "Compiler toutes les fonctions au chargement du script",
"Compile functions on first execution": "Compiler les fonctions à la première exécution",
"Constant conversion and jumps": "Conversion des constantes et sauts",
"CPU-specific optimization": "Optimisation spécifique au CPU",
"CSE, STRING construction": "Construction CSE, STRING",
"Currently unused": "Actuellement inutilisé",
"DCE (dead code elimination)": "DCE (élimination du code mort)",
"Descending": "Décroissant",
"DFA based optimization": "Optimisation basée sur DFA",
"Directives": "Directives",
"Disable CPU-specific optimization": "Désactiver l'optimisation spécifique au processeur",
"Disable real-time update": "Désactiver la mise à jour en temps réel",
"Do not perform register allocation": "Ne pas effectuer d'allocation de registre",
"Enable real-time update": "Activer la mise à jour en temps réel",
"Enable use of AVX, if the CPU supports it": "Activer l'utilisation d'AVX, si le CPU le prend en charge",
"false": "faux",
"File list pagination": "Pagination de la liste des fichiers",
"force file invalidation": "forcer l'invalidation du fichier",
"free memory": "mémoire libre",
"General info": "Informations générales",
"has been invalidated": "a été invalidé",
"hit rate": "taux de succès",
"hits": "accès",
"Host": "Hôte",
"Ignored": "Ignoré",
"INIT_FCALL_BY_NAME -> DO_FCALL": "INIT_FCALL_BY_NAME -> DO_FCALL",
"Inline functions": "Fonctions inline",
"Inline VM handlers": "Gestionnaires de VM inline",
"interned strings usage": "utilisation des chaînes internées",
"Invalidate all matching files": "Invalider tous les fichiers correspondants",
"jit buffer free": "tampon jit libre",
"jit buffer": "tampon jit",
"keys": "clés",
"Last modified": "Dernière modification",
"last modified": "dernière modification",
"Last reset": "Dernière réinitialisation",
"Last used": "Dernière utilisation",
"last used": "dernière utilisation",
"max cached keys": "max de clés en cache",
"Memory consumption": "Consommation mémoire",
"memory usage": "utilisation de la mémoire",
"memory": "mémoire",
"Merge equal constants": "Fusionner les constantes égales",
"Minimal JIT (call standard VM handlers)": "JIT minimal (appel des gestionnaires de VM standard)",
"never": "jamais",
"Next": "Suivant",
"No files have been cached or you have <i>opcache.file_cache_only<\/i> turned on": "Aucun fichier n'a été mis en cache ou vous avez <i>opcache.file_cache_only<\/i> activé",
"No files have been ignored via <i>opcache.blacklist_filename<\/i>": "Aucun fichier n'a été ignoré via <i>opcache.blacklist_filename<\/i>",
"No files have been preloaded <i>opcache.preload<\/i>": "Aucun fichier n'a été préchargé <i>opcache.preload<\/i>",
"No JIT": "Pas de JIT",
"no value": "pas de valeur",
"NOP removal": "Suppression de NOP",
"number of cached files": "nombre de fichiers en cache",
"number of cached keys": "nombre de clés en cache",
"Number of hits": "Nombre d'accès",
"number of hits": "nombre d'accès",
"number of misses": "nombre de ratés",
"number of strings": "nombre de chaînes",
"opcache statistics": "statistiques d'opcache",
"Optimization level": "Niveau d'optimisation",
"Optimize whole script": "Optimiser tout le script",
"Overview": "Vue d'ensemble",
"Path": "Chemin",
"Perform block-local register allocation": "Effectuer l'allocation de registre local de bloc",
"Perform global register allocation": "Effectuer une allocation de registre globale",
"preload memory": "précharger la mémoire",
"Preloaded": "Préchargé",
"Previous": "Précédent",
"Profile functions on first request and compile the hottest functions afterwards": "Profiling des fonctions à la première demande et compilation des fonctions les plus fréquentes par la suite",
"Profile on the fly and compile hot functions": "Profiling à la volée et compiler les fonctions les plus fréquentes",
"Register allocation": "Allocation de registre",
"Remove unused variables": "Supprimer les variables inutilisées",
"Reset cache": "Réinitialiser le cache",
"SCCP (constant propagation)": "SCCP (propagation des constantes)",
"Script": "Script",
"Server Software": "Logiciel serveur",
"Sort order": "Ordre de tri",
"Start time": "Heure de début",
"Start typing to filter on script path": "Commencez à taper pour filtrer sur le chemin du script",
"TMP VAR usage": "Utilisation TMP VAR",
"total memory": "mémoire totale",
"Trigger": "Déclencheur",
"true": "vrai",
"Use call graph": "Utiliser le graphe des appels",
"Use tracing JIT. Profile on the fly and compile traces for hot code segments": "Utilisez le suivi JIT. Profiling à la volée et compilation des traces pour les segments de code les plus fréquents",
"Use type inference": "Utiliser l'inférence de type",
"used memory": "mémoire utilisée",
"View manual page": "Voir la page du manuel",
"View {0} manual entry": "Voir la page {0} du manuel",
"wasted memory": "mémoire perdue",
"You have <i>opcache.file_cache_only<\/i> turned on. As a result, the memory information is not available. Statistics and file list may also not be returned by <i>opcache_get_statistics()<\/i>.": "Vous avez <i>opcache.file_cache_only<\/i> activé. Par conséquent, les informations sur la mémoire ne sont pas disponibles. Les statistiques et la liste de fichiers peuvent également ne pas être renvoyées par <i>opcache_get_statistics()<\/i>.",
"{0} files cached": "{0} fichiers mis en cache",
"{0} files cached, {1} showing due to filter '{2}'": "{0} fichiers mis en cache, {1} s'affichent en raison du filtre '{2}'",
"{0} ignore file locations": "{0} ignore les fichiers en fonction de l'emplacement",
"{0} preloaded files": "{0} fichiers préchargés",
"JIT enabled": "JIT activé",
"disabled due to <i>opcache.jit</i> setting": "désactivé en raison du paramètre <i>opcache.jit</i>",
"the <i>opcache.jit_buffer_size</i> must be set to fully enable JIT": "le <i>opcache.jit_buffer_size</i> doit être défini pour activer complètement JIT",
"incompatible with extensions that override <i>zend_execute_ex()</i>, such as <i>xdebug</i>": "incompatible avec les extensions qui remplacent <i>zend_execute_ex()</i>, telles que <i>xdebug</i>",
"Yes": "Oui",
"No": "Non"
}

View file

@ -4,53 +4,13 @@
* OPcache GUI - build script
*
* @author Andrew Collington, andy@amnuts.com
* @version 3.5.5
* @version 3.2.1
* @link https://github.com/amnuts/opcache-gui
* @license MIT, https://acollington.mit-license.org/
* @license MIT, http://acollington.mit-license.org/
*/
$remoteJsLocations = [
'cloudflare' => [
'cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js',
'cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js',
'cdnjs.cloudflare.com/ajax/libs/axios/1.3.6/axios.min.js',
],
'jsdelivr' => [
'cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js',
'cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js',
'cdn.jsdelivr.net/npm/axios/dist/axios.min.js',
],
'unpkg' => [
'unpkg.com/react@18/umd/react.production.min.js',
'unpkg.com/react-dom@18/umd/react-dom.production.min.js',
'unpkg.com/axios/dist/axios.min.js',
],
];
$defaultRemoteJsFrom = array_keys($remoteJsLocations)[0];
$options = getopt('jr:l:', ['local-js', 'remote-js', 'lang:']);
$makeJsLocal = (isset($options['j']) || isset($options['local-js']));
$useRemoteJsFrom = $options['r'] ?? $options['remote-js'] ?? $defaultRemoteJsFrom;
$useLanguage = $options['l'] ?? $options['lang'] ?? null;
$languagePack = 'null';
$parentPath = dirname(__DIR__);
if (!isset($remoteJsLocations[$useRemoteJsFrom])) {
$validRemotes = implode(', ', array_keys($remoteJsLocations));
echo "\nThe '{$useRemoteJsFrom}' remote js location is not valid - must be one of {$validRemotes} - defaulting to '{$defaultRemoteJsFrom}'\n\n";
$useRemoteJsFrom = $defaultRemoteJsFrom;
}
if ($useLanguage !== null) {
$useLanguage = preg_replace('/[^a-z_-]/', '', $useLanguage);
$languageFile = __DIR__ . "/_languages/{$useLanguage}.json";
if (!file_exists($languageFile)) {
echo "\nThe '{$useLanguage}' file does not exist - using default English\n\n";
} else {
$languagePack = "<<< EOJSON\n" . file_get_contents($languageFile) . "\nEOJSON";
}
}
if (!file_exists($parentPath . '/node_modules')) {
echo "🐢 Installing node modules\n";
exec('npm install');
@ -65,34 +25,12 @@ echo "🚀 Creating single build file\n";
$template = trim(file_get_contents(__DIR__ . '/template.phps'));
$jsOutput = trim(file_get_contents(__DIR__ . '/interface.js'));
$cssOutput = trim(file_get_contents(__DIR__ . '/interface.css'));
$phpOutput = trim(implode('', array_slice(file($parentPath . '/src/Opcache/Service.php'), 7)));
$phpOutput = trim(join('', array_slice(file($parentPath . '/src/Opcache/Service.php'), 3)));
$output = str_replace(
['{{JS_OUTPUT}}', '{{CSS_OUTPUT}}', '{{PHP_OUTPUT}}', '{{LANGUAGE_PACK}}'],
[$jsOutput, $cssOutput, $phpOutput, $languagePack],
['{{JS_OUTPUT}}', '{{CSS_OUTPUT}}', '{{PHP_OUTPUT}}'],
[$jsOutput, $cssOutput, $phpOutput],
$template
);
if ($makeJsLocal) {
echo "🔗 Making js locally in-line\n";
$jsContents = [];
foreach ($remoteJsLocations[$useRemoteJsFrom] as $jsUrl) {
$jsContents[] = file_get_contents('https://' . $jsUrl);
}
$output = str_replace('{{JS_LIBRARIES}}',
"<script>\n" . implode(";\n\n", $jsContents) . ";\n</script>",
$output
);
} else {
echo "🔗 Using remote js links from '{$useRemoteJsFrom}'\n";
$output = str_replace('{{JS_LIBRARIES}}',
implode("\n ", array_map(static function ($jsUrl) {
return "<script src=\"//{$jsUrl}\"></script>";
}, $remoteJsLocations[$useRemoteJsFrom])),
$output
);
}
file_put_contents($parentPath . '/index.php', $output);
echo "💯 Done!\n";

View file

@ -1,14 +1,16 @@
<?php
namespace Amnuts\Opcache;
/**
* OPcache GUI
*
* A simple but effective single-file GUI for the OPcache PHP extension.
*
* @author Andrew Collington, andy@amnuts.com
* @version 3.5.5
* @version 3.2.1
* @link https://github.com/amnuts/opcache-gui
* @license MIT, https://acollington.mit-license.org/
* @license MIT, http://acollington.mit-license.org/
*/
/*
@ -18,27 +20,23 @@
*/
$options = [
'allow_filelist' => true, // show/hide the files tab
'allow_invalidate' => true, // give a link to invalidate files
'allow_reset' => true, // give option to reset the whole cache
'allow_realtime' => true, // give option to enable/disable real-time updates
'refresh_time' => 5, // how often the data will refresh, in seconds
'size_precision' => 2, // Digits after decimal point
'size_space' => false, // have '1MB' or '1 MB' when showing sizes
'charts' => true, // show gauge chart or just big numbers
'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
'per_page' => 200, // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui', // name of cookie
'cookie_ttl' => 365, // days to store cookie
'datetime_format' => 'D, d M Y H:i:s O', // Show datetime in this format
'allow_filelist' => true, // show/hide the files tab
'allow_invalidate' => true, // give a link to invalidate files
'allow_reset' => true, // give option to reset the whole cache
'allow_realtime' => true, // give option to enable/disable real-time updates
'refresh_time' => 5, // how often the data will refresh, in seconds
'size_precision' => 2, // Digits after decimal point
'size_space' => false, // have '1MB' or '1 MB' when showing sizes
'charts' => true, // show gauge chart or just big numbers
'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
'per_page' => 200, // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui', // name of cookie
'cookie_ttl' => 365, // days to store cookie
'highlight' => [
'memory' => true, // show the memory chart/big number
'hits' => true, // show the hit rate chart/big number
'keys' => true, // show the keys used chart/big number
'jit' => true // show the jit buffer chart/big number
],
// json structure of all text strings used, or null for default
'language_pack' => {{LANGUAGE_PACK}}
'memory' => true, // show the memory chart/big number
'hits' => true, // show the hit rate chart/big number
'keys' => true // show the keys used chart/big number
]
];
/*
@ -63,14 +61,15 @@ $opcache = (new Service($options))->handle();
?>
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="robots" content="noindex, nofollow" />
<title>OPcache statistics on <?= $opcache->getData('version', 'host'); ?></title>
{{JS_LIBRARIES}}
<style>
<script src="//unpkg.com/react/umd/react.production.min.js" crossorigin></script>
<script src="//unpkg.com/react-dom/umd/react-dom.production.min.js" crossorigin></script>
<script src="//unpkg.com/axios/dist/axios.min.js" crossorigin></script>
<style type="text/css">
{{CSS_OUTPUT}}
</style>
</head>
@ -99,8 +98,7 @@ $opcache = (new Service($options))->handle();
highlight: <?= json_encode($opcache->getOption('highlight')); ?>,
debounceRate: <?= $opcache->getOption('debounce_rate'); ?>,
perPageLimit: <?= json_encode($opcache->getOption('per_page')); ?>,
realtimeRefresh: <?= json_encode($opcache->getOption('refresh_time')); ?>,
language: <?= json_encode($opcache->getOption('language_pack')); ?>,
realtimeRefresh: <?= json_encode($opcache->getOption('refresh_time')); ?>
}), document.getElementById('interface'));
</script>

View file

@ -8,7 +8,7 @@
{
"name": "Andrew Collington",
"email": "andy@amnuts.com",
"homepage": "https://blog.amnuts.com/",
"homepage": "http://www.amnuts.com/",
"role": "Developer"
},
{
@ -23,7 +23,7 @@
"require": {
"ext-Zend-OPcache": "*",
"php": ">=7.1.0",
"ext-json": "*"
"ext-json": "*"
},
"autoload": {
"psr-4" : {
@ -31,8 +31,6 @@
}
},
"scripts": {
"build": "php build/build.php",
"build-french": "php build/build.php --lang fr",
"build-spanish": "php build/build.php --lang es"
"build": "php build/build.php"
}
}

623
index.php

File diff suppressed because one or more lines are too long

3354
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,13 @@
{
"name": "opcache-gui",
"description": "A clean and responsive interface for Zend OPcache information, showing statistics, settings and cached files, and providing a real-time update for the information (using jQuery and React).",
"version": "3.5.5",
"version": "3.2.1",
"main": "index.js",
"devDependencies": {
"@babel/cli": "^7.24.7",
"@babel/core": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"node-sass": "^9.0.0"
"@babel/cli": "^7.12.8",
"@babel/core": "^7.12.9",
"@babel/preset-react": "^7.12.7",
"node-sass": "^4.14.1"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",

View file

@ -2,140 +2,64 @@
namespace Amnuts\Opcache;
use DateTimeImmutable;
use DateTimeZone;
use Exception;
class Service
{
public const VERSION = '3.5.5';
const VERSION = '3.2.1';
protected $tz;
protected $data;
protected $options;
protected $optimizationLevels;
protected $jitModes;
protected $jitModeMapping = [
'tracing' => 1254,
'on' => 1254,
'function' => 1205
];
protected $defaults = [
'allow_filelist' => true, // show/hide the files tab
'allow_invalidate' => true, // give a link to invalidate files
'allow_reset' => true, // give option to reset the whole cache
'allow_realtime' => true, // give option to enable/disable real-time updates
'refresh_time' => 5, // how often the data will refresh, in seconds
'size_precision' => 2, // Digits after decimal point
'size_space' => false, // have '1MB' or '1 MB' when showing sizes
'charts' => true, // show gauge chart or just big numbers
'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
'per_page' => 200, // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui', // name of cookie
'cookie_ttl' => 365, // days to store cookie
'datetime_format' => 'D, d M Y H:i:s O', // Show datetime in this format
'allow_filelist' => true, // show/hide the files tab
'allow_invalidate' => true, // give a link to invalidate files
'allow_reset' => true, // give option to reset the whole cache
'allow_realtime' => true, // give option to enable/disable real-time updates
'refresh_time' => 5, // how often the data will refresh, in seconds
'size_precision' => 2, // Digits after decimal point
'size_space' => false, // have '1MB' or '1 MB' when showing sizes
'charts' => true, // show gauge chart or just big numbers
'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
'per_page' => 200, // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui', // name of cookie
'cookie_ttl' => 365, // days to store cookie
'highlight' => [
'memory' => true, // show the memory chart/big number
'hits' => true, // show the hit rate chart/big number
'keys' => true, // show the keys used chart/big number
'jit' => true // show the jit buffer chart/big number
],
'language_pack' => null // json structure of all text strings used, or null for default
'memory' => true, // show the memory chart/big number
'hits' => true, // show the hit rate chart/big number
'keys' => true // show the keys used chart/big number
]
];
/**
* Service constructor.
* @param array $options
* @throws Exception
*/
public function __construct(array $options = [])
{
$this->options = array_merge($this->defaults, $options);
$this->tz = new DateTimeZone(date_default_timezone_get());
if (is_string($this->options['language_pack'])) {
$this->options['language_pack'] = json_decode($this->options['language_pack'], true);
}
$this->optimizationLevels = [
1 << 0 => $this->txt('CSE, STRING construction'),
1 << 1 => $this->txt('Constant conversion and jumps'),
1 << 2 => $this->txt('++, +=, series of jumps'),
1 << 3 => $this->txt('INIT_FCALL_BY_NAME -> DO_FCALL'),
1 << 4 => $this->txt('CFG based optimization'),
1 << 5 => $this->txt('DFA based optimization'),
1 << 6 => $this->txt('CALL GRAPH optimization'),
1 << 7 => $this->txt('SCCP (constant propagation)'),
1 << 8 => $this->txt('TMP VAR usage'),
1 << 9 => $this->txt('NOP removal'),
1 << 10 => $this->txt('Merge equal constants'),
1 << 11 => $this->txt('Adjust used stack'),
1 << 12 => $this->txt('Remove unused variables'),
1 << 13 => $this->txt('DCE (dead code elimination)'),
1 << 14 => $this->txt('(unsafe) Collect constants'),
1 << 15 => $this->txt('Inline functions'),
];
$this->jitModes = [
[
'flag' => $this->txt('CPU-specific optimization'),
'value' => [
$this->txt('Disable CPU-specific optimization'),
$this->txt('Enable use of AVX, if the CPU supports it')
]
],
[
'flag' => $this->txt('Register allocation'),
'value' => [
$this->txt('Do not perform register allocation'),
$this->txt('Perform block-local register allocation'),
$this->txt('Perform global register allocation')
]
],
[
'flag' => $this->txt('Trigger'),
'value' => [
$this->txt('Compile all functions on script load'),
$this->txt('Compile functions on first execution'),
$this->txt('Profile functions on first request and compile the hottest functions afterwards'),
$this->txt('Profile on the fly and compile hot functions'),
$this->txt('Currently unused'),
$this->txt('Use tracing JIT. Profile on the fly and compile traces for hot code segments')
]
],
[
'flag' => $this->txt('Optimization level'),
'value' => [
$this->txt('No JIT'),
$this->txt('Minimal JIT (call standard VM handlers)'),
$this->txt('Inline VM handlers'),
$this->txt('Use type inference'),
$this->txt('Use call graph'),
$this->txt('Optimize whole script')
]
]
1 << 0 => 'CSE, STRING construction',
1 << 1 => 'Constant conversion and jumps',
1 << 2 => '++, +=, series of jumps',
1 << 3 => 'INIT_FCALL_BY_NAME -> DO_FCALL',
1 << 4 => 'CFG based optimization',
1 << 5 => 'DFA based optimization',
1 << 6 => 'CALL GRAPH optimization',
1 << 7 => 'SCCP (constant propagation)',
1 << 8 => 'TMP VAR usage',
1 << 9 => 'NOP removal',
1 << 10 => 'Merge equal constants',
1 << 11 => 'Adjust used stack',
1 << 12 => 'Remove unused variables',
1 << 13 => 'DCE (dead code elimination)',
1 << 14 => '(unsafe) Collect constants',
1 << 15 => 'Inline functions'
];
$this->options = array_merge($this->defaults, $options);
$this->data = $this->compileState();
}
/**
* @return string
*/
public function txt(): string
{
$args = func_get_args();
$text = array_shift($args);
if ((($lang = $this->getOption('language_pack')) !== null) && !empty($lang[$text])) {
$text = $lang[$text];
}
foreach ($args as $i => $arg) {
$text = str_replace('{' . $i . '}', $arg, $text);
}
return $text;
}
/**
* @return $this
* @throws Exception
*/
public function handle(): Service
{
@ -150,11 +74,13 @@ class Service
if (isset($_GET['reset']) && $this->getOption('allow_reset')) {
$response($this->resetCache());
} elseif (isset($_GET['invalidate']) && $this->getOption('allow_invalidate')) {
} else if (isset($_GET['invalidate']) && $this->getOption('allow_invalidate')) {
$response($this->resetCache($_GET['invalidate']));
} elseif (isset($_GET['invalidate_searched']) && $this->getOption('allow_invalidate')) {
} else if (isset($_GET['invalidate_searched']) && $this->getOption('allow_invalidate')) {
$response($this->resetSearched($_GET['invalidate_searched']));
} elseif ($this->isJsonRequest() && $this->getOption('allow_realtime')) {
} else if (isset($_GET['invalidate_searched']) && $this->getOption('allow_invalidate')) {
$response($this->resetSearched($_GET['invalidate_searched']));
} else if ($this->isJsonRequest() && $this->getOption('allow_realtime')) {
echo json_encode($this->getData($_GET['section'] ?? null));
exit;
}
@ -171,8 +97,10 @@ class Service
if ($name === null) {
return $this->options;
}
return $this->options[$name] ?? null;
return (isset($this->options[$name])
? $this->options[$name]
: null
);
}
/**
@ -206,14 +134,13 @@ class Service
/**
* @param string|null $file
* @return bool
* @throws Exception
*/
public function resetCache(?string $file = null): bool
{
$success = false;
if ($file === null) {
$success = opcache_reset();
} elseif (function_exists('opcache_invalidate')) {
} else if (function_exists('opcache_invalidate')) {
$success = opcache_invalidate(urldecode($file), true);
}
if ($success) {
@ -225,7 +152,6 @@ class Service
/**
* @param string $search
* @return bool
* @throws Exception
*/
public function resetSearched(string $search): bool
{
@ -271,7 +197,6 @@ class Service
/**
* @return array
* @throws Exception
*/
protected function compileState(): array
{
@ -284,7 +209,7 @@ class Service
$files = [];
if (!empty($status['scripts']) && $this->getOption('allow_filelist')) {
uasort($status['scripts'], static function ($a, $b) {
uasort($status['scripts'], function ($a, $b) {
return $a['hits'] <=> $b['hits'];
});
foreach ($status['scripts'] as &$file) {
@ -293,15 +218,6 @@ class Service
'hits' => number_format($file['hits']),
'memory_consumption' => $this->size($file['memory_consumption'])
];
$file['last_used'] = (new DateTimeImmutable("@{$file['last_used_timestamp']}"))
->setTimezone($this->tz)
->format($this->getOption('datetime_format'));
$file['last_modified'] = "";
if (!empty($file['timestamp'])) {
$file['last_modified'] = (new DateTimeImmutable("@{$file['timestamp']}"))
->setTimezone($this->tz)
->format($this->getOption('datetime_format'));
}
}
$files = array_values($status['scripts']);
}
@ -333,14 +249,10 @@ class Service
'num_cached_keys' => number_format($status['opcache_statistics']['num_cached_keys']),
'max_cached_keys' => number_format($status['opcache_statistics']['max_cached_keys']),
'interned' => null,
'start_time' => (new DateTimeImmutable("@{$status['opcache_statistics']['start_time']}"))
->setTimezone($this->tz)
->format($this->getOption('datetime_format')),
'last_restart_time' => ($status['opcache_statistics']['last_restart_time'] === 0
? $this->txt('never')
: (new DateTimeImmutable("@{$status['opcache_statistics']['last_restart_time']}"))
->setTimezone($this->tz)
->format($this->getOption('datetime_format'))
'start_time' => date('Y-m-d H:i:s', $status['opcache_statistics']['start_time']),
'last_restart_time' => ($status['opcache_statistics']['last_restart_time'] == 0
? 'never'
: date('Y-m-d H:i:s', $status['opcache_statistics']['last_restart_time'])
)
]
]
@ -366,25 +278,12 @@ class Service
];
}
if ($overview && !empty($status['jit']['enabled'])) {
$overview['jit_buffer_used_percentage'] = ($status['jit']['buffer_size']
? round(100 * (($status['jit']['buffer_size'] - $status['jit']['buffer_free']) / $status['jit']['buffer_size']))
: 0
);
$overview['readable'] = array_merge($overview['readable'], [
'jit_buffer_size' => $this->size($status['jit']['buffer_size']),
'jit_buffer_free' => $this->size($status['jit']['buffer_free'])
]);
} else {
$this->options['highlight']['jit'] = false;
}
$directives = [];
ksort($config['directives']);
foreach ($config['directives'] as $k => $v) {
if (in_array($k, ['opcache.max_file_size', 'opcache.memory_consumption', 'opcache.jit_buffer_size']) && $v) {
if (in_array($k, ['opcache.max_file_size', 'opcache.memory_consumption']) && $v) {
$v = $this->size($v) . " ({$v})";
} elseif ($k === 'opcache.optimization_level') {
} elseif ($k == 'opcache.optimization_level') {
$levels = [];
foreach ($this->optimizationLevels as $level => $info) {
if ($level & $v) {
@ -392,19 +291,6 @@ class Service
}
}
$v = $levels ?: 'none';
} elseif ($k === 'opcache.jit') {
if ($v === '1') {
$v = 'on';
}
if (isset($this->jitModeMapping[$v]) || is_numeric($v)) {
$levels = [];
foreach (str_split((string)($this->jitModeMapping[$v] ?? $v)) as $type => $level) {
$levels[] = "{$level}: {$this->jitModes[$type]['value'][$level]} ({$this->jitModes[$type]['flag']})";
}
$v = [$v, $levels];
} elseif (empty($v) || strtolower($v) === 'off') {
$v = 'Off';
}
}
$directives[] = [
'k' => $k,
@ -415,7 +301,7 @@ class Service
$version = array_merge(
$config['version'],
[
'php' => PHP_VERSION,
'php' => phpversion(),
'server' => $_SERVER['SERVER_SOFTWARE'] ?: '',
'host' => (function_exists('gethostname')
? gethostname()
@ -437,28 +323,7 @@ class Service
'preload' => $preload,
'directives' => $directives,
'blacklist' => $config['blacklist'],
'functions' => get_extension_funcs('Zend OPcache'),
'jitState' => $this->jitState($status, $config['directives']),
'functions' => get_extension_funcs('Zend OPcache')
];
}
protected function jitState(array $status, array $directives): array
{
$state = [
'enabled' => $status['jit']['enabled'],
'reason' => ''
];
if (!$state['enabled']) {
if (empty($directives['opcache.jit']) || $directives['opcache.jit'] === 'disable') {
$state['reason'] = $this->txt('disabled due to <i>opcache.jit</i> setting');
} elseif (!$directives['opcache.jit_buffer_size']) {
$state['reason'] = $this->txt('the <i>opcache.jit_buffer_size</i> must be set to fully enable JIT');
} else {
$state['reason'] = $this->txt('incompatible with extensions that override <i>zend_execute_ex()</i>, such as <i>xdebug</i>');
}
}
return $state;
}
}