diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cfe5909..eb027736 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ The **patch** part changes incrementally at each release.
### Added
* evolinux-base: On debian 10 and later, add noexec on /dev/shm
* php: variable to install the mysqlnd module instead of the default mysql module
+* packweb-apache: Deploy opcache.php to give some insights on PHP's opcache status
### Changed
* elasticsearch: listen on local interface only by default
diff --git a/packweb-apache/files/opcache.php b/packweb-apache/files/opcache.php
new file mode 100644
index 00000000..22b4b2f4
--- /dev/null
+++ b/packweb-apache/files/opcache.php
@@ -0,0 +1,744 @@
+You do not have the Zend OPcache extension loaded, sample data is being shown instead.';
+ require 'data-sample.php';
+}
+
+class OpCacheDataModel
+{
+ private $_configuration;
+ private $_status;
+ private $_d3Scripts = array();
+
+ public function __construct()
+ {
+ $this->_configuration = opcache_get_configuration();
+ $this->_status = opcache_get_status();
+ }
+
+ public function getPageTitle()
+ {
+ return 'PHP ' . phpversion() . " with OpCache {$this->_configuration['version']['version']}";
+ }
+
+ public function getStatusDataRows()
+ {
+ $rows = array();
+ foreach ($this->_status as $key => $value) {
+ if ($key === 'scripts') {
+ continue;
+ }
+
+ if (is_array($value)) {
+ foreach ($value as $k => $v) {
+ if ($v === false) {
+ $value = 'false';
+ }
+ if ($v === true) {
+ $value = 'true';
+ }
+ if ($k === 'used_memory' || $k === 'free_memory' || $k === 'wasted_memory') {
+ $v = $this->_size_for_humans(
+ $v
+ );
+ }
+ if ($k === 'current_wasted_percentage' || $k === 'opcache_hit_rate') {
+ $v = number_format(
+ $v,
+ 2
+ ) . '%';
+ }
+ if ($k === 'blacklist_miss_ratio') {
+ $v = number_format($v, 2) . '%';
+ }
+ if ($k === 'start_time' || $k === 'last_restart_time') {
+ $v = ($v ? date(DATE_RFC822, $v) : 'never');
+ }
+ if (THOUSAND_SEPARATOR === true && is_int($v)) {
+ $v = number_format($v);
+ }
+
+ $rows[] = "
$k | $v |
\n";
+ }
+ continue;
+ }
+ if ($value === false) {
+ $value = 'false';
+ }
+ if ($value === true) {
+ $value = 'true';
+ }
+ $rows[] = "$key | $value |
\n";
+ }
+
+ return implode("\n", $rows);
+ }
+
+ public function getConfigDataRows()
+ {
+ $rows = array();
+ foreach ($this->_configuration['directives'] as $key => $value) {
+ if ($value === false) {
+ $value = 'false';
+ }
+ if ($value === true) {
+ $value = 'true';
+ }
+ if ($key == 'opcache.memory_consumption') {
+ $value = $this->_size_for_humans($value);
+ }
+ $rows[] = "$key | $value |
\n";
+ }
+
+ return implode("\n", $rows);
+ }
+
+ public function getScriptStatusRows()
+ {
+ foreach ($this->_status['scripts'] as $key => $data) {
+ $dirs[dirname($key)][basename($key)] = $data;
+ $this->_arrayPset($this->_d3Scripts, $key, array(
+ 'name' => basename($key),
+ 'size' => $data['memory_consumption'],
+ ));
+ }
+
+ asort($dirs);
+
+ $basename = '';
+ while (true) {
+ if (count($this->_d3Scripts) !=1) break;
+ $basename .= DIRECTORY_SEPARATOR . key($this->_d3Scripts);
+ $this->_d3Scripts = reset($this->_d3Scripts);
+ }
+
+ $this->_d3Scripts = $this->_processPartition($this->_d3Scripts, $basename);
+ $id = 1;
+
+ $rows = array();
+ foreach ($dirs as $dir => $files) {
+ $count = count($files);
+ $file_plural = $count > 1 ? 's' : null;
+ $m = 0;
+ foreach ($files as $file => $data) {
+ $m += $data["memory_consumption"];
+ }
+ $m = $this->_size_for_humans($m);
+
+ if ($count > 1) {
+ $rows[] = '';
+ $rows[] = "{$dir} ({$count} file{$file_plural}, {$m}) | ";
+ $rows[] = '
';
+ }
+
+ foreach ($files as $file => $data) {
+ $rows[] = "";
+ $rows[] = "" . $this->_format_value($data["hits"]) . " | ";
+ $rows[] = "" . $this->_size_for_humans($data["memory_consumption"]) . " | ";
+ $rows[] = $count > 1 ? "{$file} | " : "{$dir}/{$file} | ";
+ $rows[] = '
';
+ }
+
+ ++$id;
+ }
+
+ return implode("\n", $rows);
+ }
+
+ public function getScriptStatusCount()
+ {
+ return count($this->_status["scripts"]);
+ }
+
+ public function getGraphDataSetJson()
+ {
+ $dataset = array();
+ $dataset['memory'] = array(
+ $this->_status['memory_usage']['used_memory'],
+ $this->_status['memory_usage']['free_memory'],
+ $this->_status['memory_usage']['wasted_memory'],
+ );
+
+ $dataset['keys'] = array(
+ $this->_status['opcache_statistics']['num_cached_keys'],
+ $this->_status['opcache_statistics']['max_cached_keys'] - $this->_status['opcache_statistics']['num_cached_keys'],
+ 0
+ );
+
+ $dataset['hits'] = array(
+ $this->_status['opcache_statistics']['misses'],
+ $this->_status['opcache_statistics']['hits'],
+ 0,
+ );
+
+ $dataset['restarts'] = array(
+ $this->_status['opcache_statistics']['oom_restarts'],
+ $this->_status['opcache_statistics']['manual_restarts'],
+ $this->_status['opcache_statistics']['hash_restarts'],
+ );
+
+ if (THOUSAND_SEPARATOR === true) {
+ $dataset['TSEP'] = 1;
+ } else {
+ $dataset['TSEP'] = 0;
+ }
+
+ return json_encode($dataset);
+ }
+
+ public function getHumanUsedMemory()
+ {
+ return $this->_size_for_humans($this->getUsedMemory());
+ }
+
+ public function getHumanFreeMemory()
+ {
+ return $this->_size_for_humans($this->getFreeMemory());
+ }
+
+ public function getHumanWastedMemory()
+ {
+ return $this->_size_for_humans($this->getWastedMemory());
+ }
+
+ public function getUsedMemory()
+ {
+ return $this->_status['memory_usage']['used_memory'];
+ }
+
+ public function getFreeMemory()
+ {
+ return $this->_status['memory_usage']['free_memory'];
+ }
+
+ public function getWastedMemory()
+ {
+ return $this->_status['memory_usage']['wasted_memory'];
+ }
+
+ public function getWastedMemoryPercentage()
+ {
+ return number_format($this->_status['memory_usage']['current_wasted_percentage'], 2);
+ }
+
+ public function getD3Scripts()
+ {
+ return $this->_d3Scripts;
+ }
+
+ private function _processPartition($value, $name = null)
+ {
+ if (array_key_exists('size', $value)) {
+ return $value;
+ }
+
+ $array = array('name' => $name,'children' => array());
+
+ foreach ($value as $k => $v) {
+ $array['children'][] = $this->_processPartition($v, $k);
+ }
+
+ return $array;
+ }
+
+ private function _format_value($value)
+ {
+ if (THOUSAND_SEPARATOR === true) {
+ return number_format($value);
+ } else {
+ return $value;
+ }
+ }
+
+ private function _size_for_humans($bytes)
+ {
+ if ($bytes > 1048576) {
+ return sprintf('%.2f MB', $bytes / 1048576);
+ } else {
+ if ($bytes > 1024) {
+ return sprintf('%.2f kB', $bytes / 1024);
+ } else {
+ return sprintf('%d bytes', $bytes);
+ }
+ }
+ }
+
+ // Borrowed from Laravel
+ private function _arrayPset(&$array, $key, $value)
+ {
+ if (is_null($key)) return $array = $value;
+ $keys = explode(DIRECTORY_SEPARATOR, ltrim($key, DIRECTORY_SEPARATOR));
+ while (count($keys) > 1) {
+ $key = array_shift($keys);
+ if ( ! isset($array[$key]) || ! is_array($array[$key])) {
+ $array[$key] = array();
+ }
+ $array =& $array[$key];
+ }
+ $array[array_shift($keys)] = $value;
+ return $array;
+ }
+
+}
+
+$dataModel = new OpCacheDataModel();
+?>
+
+
+
+
+
+
+
+
+ getPageTitle(); ?>
+
+
+
+
+
getPageTitle(); ?>
+
+
+
+
+
+
+
+
+ getStatusDataRows(); ?>
+
+
+
+
+
+
+
+
+
+ getConfigDataRows(); ?>
+
+
+
+
+
+
+
+
+
+
+ Hits |
+ Memory |
+ Path |
+
+ getScriptStatusRows(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ✖ Close Visualisation
+
+
+
+
+
diff --git a/packweb-apache/tasks/main.yml b/packweb-apache/tasks/main.yml
index f5d0f35e..42c97e5e 100644
--- a/packweb-apache/tasks/main.yml
+++ b/packweb-apache/tasks/main.yml
@@ -17,6 +17,18 @@
line: ' Infos PHP'
regexp: "Infos PHP"
+- name: install opcache.php
+ copy:
+ src: opcache.php
+ dest: /var/www/opcache.php
+ mode: "0644"
+
+- name: enable opcache.php link in default site index
+ lineinfile:
+ dest: /var/www/index.html
+ line: ' Infos OpCache PHP'
+ regexp: "Infos OpCache PHP"
+
- name: Add elements to user account template
file:
path: "/etc/skel/{{ item.path }}"