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(); +?> + + + + + + + + + <?php echo $dataModel->getPageTitle(); ?> + + + +
+

getPageTitle(); ?>

+ +
+ +
+ + +
+ + getStatusDataRows(); ?> +
+
+
+ +
+ + +
+ + getConfigDataRows(); ?> +
+
+
+ +
+ + +
+ + + + + + + getScriptStatusRows(); ?> +
HitsMemoryPath
+
+
+ +
+ + +
+
+ +
+ +
+
+ + + + +
+ +
+
+
+ +
✖ 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 }}"