Compare commits

...

16 Commits

Author SHA1 Message Date
87fe2ba593 Merge pull request #660 from cytopia/fix-github-settings
Fix username
2020-01-05 01:39:49 +01:00
cd82fa74a7 Fix username 2020-01-05 01:41:25 +01:00
e48c8e96dc Merge pull request #659 from cytopia/release/v1.6.0
Release v1.6.0
2020-01-05 01:37:59 +01:00
f29d552dfa Fixes #378 Allow to mount local ssh dir into PHP container (r/o) 2020-01-04 22:15:50 +01:00
304fc86329 Fixes #615 Add tool: phpmd 2020-01-04 21:07:52 +01:00
d44905bfb5 Use IP address of PHP container for Apache/Nginx upstream to skip DNS lookup 2020-01-04 20:56:30 +01:00
1b9ef528d2 GitHub settings 2020-01-04 17:41:59 +01:00
4f97eae8fe Prepare Release v1.6.0 2020-01-04 17:06:29 +01:00
2e09f7f0f8 Refs #265 Make vhost ssl type configurable 2020-01-04 17:06:08 +01:00
6fa7a84bd3 Refs #642 Make email catch-all configurable 2020-01-04 17:03:42 +01:00
266c5b84a3 Ensure env file has equal spacing everywhere 2020-01-04 17:02:33 +01:00
b75aa9f2ab Merge pull request #658 from cytopia/fix-unbound-var
Fix unbound bash variable in CI tests
2020-01-04 15:44:27 +01:00
99163bd525 Fix unbound bash variable in CI tests 2020-01-04 15:45:54 +01:00
dada924b41 Merge pull request #657 from cytopia/release/v1.5.0
Release v1.5.0
2020-01-04 03:44:09 +01:00
75b6db7676 Refs #653 #643 add integration tests for MySQL/MariaDB/Percona images 2020-01-03 18:24:38 +01:00
f90b53c79a Fixes #654 Add Opcache Control Panel 2020-01-03 15:39:22 +01:00
17 changed files with 768 additions and 11 deletions

View File

@ -13,8 +13,8 @@ error_reporting(-1);
putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
$DEVILBOX_VERSION = 'v1.4.0';
$DEVILBOX_DATE = '2020-01-02';
$DEVILBOX_VERSION = 'v1.6.0';
$DEVILBOX_DATE = '2020-01-04';
$DEVILBOX_API_PAGE = 'devilbox-api/status.json';
//

391
.devilbox/www/htdocs/vendor/ocp.php vendored Normal file
View File

@ -0,0 +1,391 @@
<?php
/*
OCP - Opcache Control Panel (aka Zend Optimizer+ Control Panel for PHP)
Author: _ck_ (with contributions by GK, stasilok)
Version: 0.1.7
Free for any kind of use or modification, I am not responsible for anything, please share your improvements
* revision history
0.1.7 2015-09-01 regex fix for PHP7 phpinfo
0.1.6 2013-04-12 moved meta to footer so graphs can be higher and reduce clutter
0.1.5 2013-04-12 added graphs to visualize cache state, please report any browser/style bugs
0.1.4 2013-04-09 added "recheck" to update files when using large revalidate_freq (or validate_timestamps=Off)
0.1.3 2013-03-30 show host and php version, can bookmark with hashtag ie. #statistics - needs new layout asap
0.1.2 2013-03-25 show optimization levels, number formatting, support for start_time in 7.0.2
0.1.1 2013-03-18 today Zend completely renamed Optimizer+ to OPcache, adjusted OCP to keep working
0.1.0 2013-03-17 added group/sort indicators, replaced "accelerator_" functions with "opcache_"
0.0.6 2013-03-16 transition support as Zend renames product and functions for PHP 5.5 (stasilok)
0.0.5 2013-03-10 added refresh button (GK)
0.0.4 2013-02-18 added file grouping and sorting (click on headers) - code needs cleanup but gets the job done
0.0.2 2013-02-14 first public release
* known problems/limitations:
Unlike APC, the Zend OPcache API
- cannot determine when a file was put into the cache
- cannot change settings on the fly
- cannot protect opcache functions by restricting execution to only specific scripts/paths
* todo:
Extract variables for prefered ordering and better layout instead of just dumping into tables
File list filter
*/
// ini_set('display_errors',1); error_reporting(-1);
if ( count(get_included_files())>1 || php_sapi_name()=='cli' || empty($_SERVER['REMOTE_ADDR']) ) { die; } // weak block against indirect access
$time=time();
define('CACHEPREFIX',function_exists('opcache_reset')?'opcache_':(function_exists('accelerator_reset')?'accelerator_':''));
if ( !empty($_GET['RESET']) ) {
if ( function_exists(CACHEPREFIX.'reset') ) { call_user_func(CACHEPREFIX.'reset'); }
header( 'Location: '.str_replace('?'.$_SERVER['QUERY_STRING'],'',$_SERVER['REQUEST_URI']) );
exit;
}
if ( !empty($_GET['RECHECK']) ) {
if ( function_exists(CACHEPREFIX.'invalidate') ) {
$recheck=trim($_GET['RECHECK']); $files=call_user_func(CACHEPREFIX.'get_status');
if (!empty($files['scripts'])) {
foreach ($files['scripts'] as $file=>$value) {
if ( $recheck==='1' || strpos($file,$recheck)===0 ) call_user_func(CACHEPREFIX.'invalidate',$file);
}
}
header( 'Location: '.str_replace('?'.$_SERVER['QUERY_STRING'],'',$_SERVER['REQUEST_URI']) );
} else { echo 'Sorry, this feature requires Zend Opcache newer than April 8th 2013'; }
exit;
}
?><!DOCTYPE html>
<html>
<head>
<title>OCP - Opcache Control Panel</title>
<meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" />
<style type="text/css">
body {background-color: #fff; color: #000;}
body, td, th, h1, h2 {font-family: sans-serif;}
pre {margin: 0px; font-family: monospace;}
a:link,a:visited {color: #000099; text-decoration: none;}
a:hover {text-decoration: underline;}
table {border-collapse: collapse; width: 600px; }
.center {text-align: center;}
.center table { margin-left: auto; margin-right: auto; text-align: left;}
.center th { text-align: center !important; }
.middle {vertical-align:middle;}
td, th { border: 1px solid #000; font-size: 75%; vertical-align: baseline; padding: 3px; }
h1 {font-size: 150%;}
h2 {font-size: 125%;}
.p {text-align: left;}
.e {background-color: #ccccff; font-weight: bold; color: #000; width:50%; white-space:nowrap;}
.h {background-color: #9999cc; font-weight: bold; color: #000;}
.v {background-color: #cccccc; color: #000;}
.vr {background-color: #cccccc; text-align: right; color: #000; white-space: nowrap;}
.b {font-weight:bold;}
.white, .white a {color:#fff;}
img {float: right; border: 0px;}
hr {width: 600px; background-color: #cccccc; border: 0px; height: 1px; color: #000;}
.meta, .small {font-size: 75%; }
.meta {margin: 2em 0;}
.meta a, th a {padding: 10px; white-space:nowrap; }
.buttons {margin:0 0 1em;}
.buttons a {margin:0 15px; background-color: #9999cc; color:#fff; text-decoration:none; padding:1px; border:1px solid #000; display:inline-block; width:5em; text-align:center;}
#files td.v a {font-weight:bold; color:#9999cc; margin:0 10px 0 5px; text-decoration:none; font-size:120%;}
#files td.v a:hover {font-weight:bold; color:#ee0000;}
.graph {display:inline-block; width:145px; margin:1em 0 1em 1px; border:0; vertical-align:top;}
.graph table {width:100%; height:150px; border:0; padding:0; margin:5px 0 0 0; position:relative;}
.graph td {vertical-align:middle; border:0; padding:0 0 0 5px;}
.graph .bar {width:25px; text-align:right; padding:0 2px; color:#fff;}
.graph .total {width:34px; text-align:center; padding:0 5px 0 0;}
.graph .total div {border:1px dashed #888; border-right:0; height:99%; width:12px; position:absolute; bottom:0; left:17px; z-index:-1;}
.graph .total span {background:#fff; font-weight:bold;}
.graph .actual {text-align:right; font-weight:bold; padding:0 5px 0 0;}
.graph .red {background:#ee0000;}
.graph .green {background:#00cc00;}
.graph .brown {background:#8B4513;}
</style>
<!--[if lt IE 9]><script type="text/javascript" defer="defer">
window.onload=function(){var i,t=document.getElementsByTagName('table');for(i=0;i<t.length;i++){if(t[i].parentNode.className=='graph')t[i].style.height=150-(t[i].clientHeight-150)+'px';}}
</script><![endif]-->
</head>
<body>
<div class="center">
<h1><a href="?">Opcache Control Panel</a></h1>
<div class="buttons">
<a href="?ALL=1">Details</a>
<a href="?FILES=1&GROUP=2&SORT=3">Files</a>
<a href="?RESET=1" onclick="return confirm('RESET cache ?')">Reset</a>
<?php if ( function_exists(CACHEPREFIX.'invalidate') ) { ?>
<a href="?RECHECK=1" onclick="return confirm('Recheck all files in the cache ?')">Recheck</a>
<?php } ?>
<a href="?" onclick="window.location.reload(true); return false">Refresh</a>
</div>
<?php
if ( !function_exists(CACHEPREFIX.'get_status') ) { echo '<h2>Opcache not detected?</h2>'; die; }
if ( !empty($_GET['FILES']) ) { echo '<h2>files cached</h2>'; files_display(); echo '</div></body></html>'; exit; }
if ( !(isset($_REQUEST['GRAPHS']) && !$_REQUEST['GRAPHS']) && CACHEPREFIX=='opcache_') { graphs_display(); if ( !empty($_REQUEST['GRAPHS']) ) { exit; } }
ob_start(); phpinfo(8); $phpinfo = ob_get_contents(); ob_end_clean(); // some info is only available via phpinfo? sadly buffering capture has to be used
if ( !preg_match( '/module\_Zend.(Optimizer\+|OPcache).+?(\<table[^>]*\>.+?\<\/table\>).+?(\<table[^>]*\>.+?\<\/table\>)/is', $phpinfo, $opcache) ) { } // todo
if ( function_exists(CACHEPREFIX.'get_configuration') ) { echo '<h2>general</h2>'; $configuration=call_user_func(CACHEPREFIX.'get_configuration'); }
$host=function_exists('gethostname')?@gethostname():@php_uname('n'); if (empty($host)) { $host=empty($_SERVER['SERVER_NAME'])?$_SERVER['HOST_NAME']:$_SERVER['SERVER_NAME']; }
$version=array('Host'=>$host);
$version['PHP Version']='PHP '.(defined('PHP_VERSION')?PHP_VERSION:'???').' '.(defined('PHP_SAPI')?PHP_SAPI:'').' '.(defined('PHP_OS')?' '.PHP_OS:'');
$version['Opcache Version']=empty($configuration['version']['version'])?'???':$configuration['version'][CACHEPREFIX.'product_name'].' '.$configuration['version']['version'];
print_table($version);
if ( !empty($opcache[2]) ) { echo preg_replace('/\<tr\>\<td class\="e"\>[^>]+\<\/td\>\<td class\="v"\>[0-9\,\. ]+\<\/td\>\<\/tr\>/','',$opcache[2]); }
if ( function_exists(CACHEPREFIX.'get_status') && $status=call_user_func(CACHEPREFIX.'get_status') ) {
$uptime=array();
if ( !empty($status[CACHEPREFIX.'statistics']['start_time']) ) {
$uptime['uptime']=time_since($time,$status[CACHEPREFIX.'statistics']['start_time'],1,'');
}
if ( !empty($status[CACHEPREFIX.'statistics']['last_restart_time']) ) {
$uptime['last_restart']=time_since($time,$status[CACHEPREFIX.'statistics']['last_restart_time']);
}
if (!empty($uptime)) {print_table($uptime);}
if ( !empty($status['cache_full']) ) { $status['memory_usage']['cache_full']=$status['cache_full']; }
echo '<h2 id="memory">memory</h2>';
print_table($status['memory_usage']);
unset($status[CACHEPREFIX.'statistics']['start_time'],$status[CACHEPREFIX.'statistics']['last_restart_time']);
echo '<h2 id="statistics">statistics</h2>';
print_table($status[CACHEPREFIX.'statistics']);
}
if ( empty($_GET['ALL']) ) { meta_display(); exit; }
if ( !empty($configuration['blacklist']) ) { echo '<h2 id="blacklist">blacklist</h2>'; print_table($configuration['blacklist']); }
if ( !empty($opcache[3]) ) { echo '<h2 id="runtime">runtime</h2>'; echo $opcache[3]; }
$name='zend opcache'; $functions=get_extension_funcs($name);
if (!$functions) { $name='zend optimizer+'; $functions=get_extension_funcs($name); }
if ($functions) { echo '<h2 id="functions">functions</h2>'; print_table($functions); } else { $name=''; }
$level=trim(CACHEPREFIX,'_').'.optimization_level';
if (isset($configuration['directives'][$level])) {
echo '<h2 id="optimization">optimization levels</h2>';
$levelset=strrev(base_convert($configuration['directives'][$level], 10, 2));
$levels=array(
1=>'<a href="http://wikipedia.org/wiki/Common_subexpression_elimination">Constants subexpressions elimination</a> (CSE) true, false, null, etc.<br />Optimize series of ADD_STRING / ADD_CHAR<br />Convert CAST(IS_BOOL,x) into BOOL(x)<br />Convert <a href="http://www.php.net/manual/internals2.opcodes.init-fcall-by-name.php">INIT_FCALL_BY_NAME</a> + <a href="http://www.php.net/manual/internals2.opcodes.do-fcall-by-name.php">DO_FCALL_BY_NAME</a> into <a href="http://www.php.net/manual/internals2.opcodes.do-fcall.php">DO_FCALL</a>',
2=>'Convert constant operands to expected types<br />Convert conditional <a href="http://php.net/manual/internals2.opcodes.jmp.php">JMP</a> with constant operands<br />Optimize static <a href="http://php.net/manual/internals2.opcodes.brk.php">BRK</a> and <a href="<a href="http://php.net/manual/internals2.opcodes.cont.php">CONT</a>',
3=>'Convert $a = $a + expr into $a += expr<br />Convert $a++ into ++$a<br />Optimize series of <a href="http://php.net/manual/internals2.opcodes.jmp.php">JMP</a>',
4=>'PRINT and ECHO optimization (<a href="https://github.com/zend-dev/ZendOptimizerPlus/issues/73">defunct</a>)',
5=>'Block Optimization - most expensive pass<br />Performs many different optimization patterns based on <a href="http://wikipedia.org/wiki/Control_flow_graph">control flow graph</a> (CFG)',
9=>'Optimize <a href="http://wikipedia.org/wiki/Register_allocation">register allocation</a> (allows re-usage of temporary variables)',
10=>'Remove NOPs'
);
echo '<table width="600" border="0" cellpadding="3"><tbody><tr class="h"><th>Pass</th><th>Description</th></tr>';
foreach ($levels as $pass=>$description) {
$disabled=substr($levelset,$pass-1,1)!=='1' || $pass==4 ? ' white':'';
echo '<tr><td class="v center middle'.$disabled.'">'.$pass.'</td><td class="v'.$disabled.'">'.$description.'</td></tr>';
}
echo '</table>';
}
if ( isset($_GET['DUMP']) ) {
if ($name) { echo '<h2 id="ini">ini</h2>'; print_table(ini_get_all($name,true)); }
foreach ($configuration as $key=>$value) { echo '<h2>',$key,'</h2>'; print_table($configuration[$key]); }
exit;
}
meta_display();
echo '</div></body></html>';
exit;
function time_since($time,$original,$extended=0,$text='ago') {
$time = $time - $original;
$day = $extended? floor($time/86400) : round($time/86400,0);
$amount=0; $unit='';
if ( $time < 86400) {
if ( $time < 60) { $amount=$time; $unit='second'; }
elseif ( $time < 3600) { $amount=floor($time/60); $unit='minute'; }
else { $amount=floor($time/3600); $unit='hour'; }
}
elseif ( $day < 14) { $amount=$day; $unit='day'; }
elseif ( $day < 56) { $amount=floor($day/7); $unit='week'; }
elseif ( $day < 672) { $amount=floor($day/30); $unit='month'; }
else { $amount=intval(2*($day/365))/2; $unit='year'; }
if ( $amount!=1) {$unit.='s';}
if ($extended && $time>60) { $text=' and '.time_since($time,$time<86400?($time<3600?$amount*60:$amount*3600):$day*86400,0,'').$text; }
return $amount.' '.$unit.' '.$text;
}
function print_table($array,$headers=false) {
if ( empty($array) || !is_array($array) ) {return;}
echo '<table border="0" cellpadding="3" width="600">';
if (!empty($headers)) {
if (!is_array($headers)) {$headers=array_keys(reset($array));}
echo '<tr class="h">';
foreach ($headers as $value) { echo '<th>',$value,'</th>'; }
echo '</tr>';
}
foreach ($array as $key=>$value) {
echo '<tr>';
if ( !is_numeric($key) ) {
$key=ucwords(str_replace('_',' ',$key));
echo '<td class="e">',$key,'</td>';
if ( is_numeric($value) ) {
if ( $value>1048576) { $value=round($value/1048576,1).'M'; }
elseif ( is_float($value) ) { $value=round($value,1); }
}
}
if ( is_array($value) ) {
foreach ($value as $column) {
echo '<td class="v">',$column,'</td>';
}
echo '</tr>';
}
else { echo '<td class="v">',$value,'</td></tr>'; }
}
echo '</table>';
}
function files_display() {
$status=call_user_func(CACHEPREFIX.'get_status');
if ( empty($status['scripts']) ) {return;}
if ( isset($_GET['DUMP']) ) { print_table($status['scripts']); exit;}
$time=time(); $sort=0;
$nogroup=preg_replace('/\&?GROUP\=[\-0-9]+/','',$_SERVER['REQUEST_URI']);
$nosort=preg_replace('/\&?SORT\=[\-0-9]+/','',$_SERVER['REQUEST_URI']);
$group=empty($_GET['GROUP'])?0:intval($_GET['GROUP']); if ( $group<0 || $group>9) { $group=1;}
$groupset=array_fill(0,9,''); $groupset[$group]=' class="b" ';
echo '<div class="meta">
<a ',$groupset[0],'href="',$nogroup,'">ungroup</a> |
<a ',$groupset[1],'href="',$nogroup,'&GROUP=1">1</a> |
<a ',$groupset[2],'href="',$nogroup,'&GROUP=2">2</a> |
<a ',$groupset[3],'href="',$nogroup,'&GROUP=3">3</a> |
<a ',$groupset[4],'href="',$nogroup,'&GROUP=4">4</a> |
<a ',$groupset[5],'href="',$nogroup,'&GROUP=5">5</a>
</div>';
if ( !$group ) { $files =& $status['scripts']; }
else {
$files=array();
foreach ($status['scripts'] as $data) {
if ( preg_match('@^[/]([^/]+[/]){'.$group.'}@',$data['full_path'],$path) ) {
if ( empty($files[$path[0]])) { $files[$path[0]]=array('full_path'=>'','files'=>0,'hits'=>0,'memory_consumption'=>0,'last_used_timestamp'=>'','timestamp'=>''); }
$files[$path[0]]['full_path']=$path[0];
$files[$path[0]]['files']++;
$files[$path[0]]['memory_consumption']+=$data['memory_consumption'];
$files[$path[0]]['hits']+=$data['hits'];
if ( $data['last_used_timestamp']>$files[$path[0]]['last_used_timestamp']) {$files[$path[0]]['last_used_timestamp']=$data['last_used_timestamp'];}
if ( $data['timestamp']>$files[$path[0]]['timestamp']) {$files[$path[0]]['timestamp']=$data['timestamp'];}
}
}
}
if ( !empty($_GET['SORT']) ) {
$keys=array(
'full_path'=>SORT_STRING,
'files'=>SORT_NUMERIC,
'memory_consumption'=>SORT_NUMERIC,
'hits'=>SORT_NUMERIC,
'last_used_timestamp'=>SORT_NUMERIC,
'timestamp'=>SORT_NUMERIC
);
$titles=array('','path',$group?'files':'','size','hits','last used','created');
$offsets=array_keys($keys);
$key=intval($_GET['SORT']);
$direction=$key>0?1:-1;
$key=abs($key)-1;
$key=isset($offsets[$key])&&!($key==1&&empty($group))?$offsets[$key]:reset($offsets);
$sort=array_search($key,$offsets)+1;
$sortflip=range(0,7); $sortflip[$sort]=-$direction*$sort;
if ( $keys[$key]==SORT_STRING) {$direction=-$direction; }
$arrow=array_fill(0,7,''); $arrow[$sort]=$direction>0?' &#x25BC;':' &#x25B2;';
$direction=$direction>0?SORT_DESC:SORT_ASC;
$column=array(); foreach ($files as $data) { $column[]=$data[$key]; }
array_multisort($column, $keys[$key], $direction, $files);
}
echo '<table border="0" cellpadding="3" width="960" id="files">
<tr class="h">';
foreach ($titles as $column=>$title) {
if ($title) echo '<th><a href="',$nosort,'&SORT=',$sortflip[$column],'">',$title,$arrow[$column],'</a></th>';
}
echo ' </tr>';
foreach ($files as $data) {
echo '<tr>
<td class="v" nowrap><a title="recheck" href="?RECHECK=',rawurlencode($data['full_path']),'">x</a>',$data['full_path'],'</td>',
($group?'<td class="vr">'.number_format($data['files']).'</td>':''),
'<td class="vr">',number_format(round($data['memory_consumption']/1024)),'K</td>',
'<td class="vr">',number_format($data['hits']),'</td>',
'<td class="vr">',time_since($time,$data['last_used_timestamp']),'</td>',
'<td class="vr">',empty($data['timestamp'])?'':time_since($time,$data['timestamp']),'</td>
</tr>';
}
echo '</table>';
}
function graphs_display() {
$graphs=array();
$colors=array('green','brown','red');
$primes=array(223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987);
$configuration=call_user_func(CACHEPREFIX.'get_configuration');
$status=call_user_func(CACHEPREFIX.'get_status');
$graphs['memory']['total']=$configuration['directives']['opcache.memory_consumption'];
$graphs['memory']['free']=$status['memory_usage']['free_memory'];
$graphs['memory']['used']=$status['memory_usage']['used_memory'];
$graphs['memory']['wasted']=$status['memory_usage']['wasted_memory'];
$graphs['keys']['total']=$status[CACHEPREFIX.'statistics']['max_cached_keys'];
foreach ($primes as $prime) { if ($prime>=$graphs['keys']['total']) { $graphs['keys']['total']=$prime; break;} }
$graphs['keys']['free']=$graphs['keys']['total']-$status[CACHEPREFIX.'statistics']['num_cached_keys'];
$graphs['keys']['scripts']=$status[CACHEPREFIX.'statistics']['num_cached_scripts'];
$graphs['keys']['wasted']=$status[CACHEPREFIX.'statistics']['num_cached_keys']-$status[CACHEPREFIX.'statistics']['num_cached_scripts'];
$graphs['hits']['total']=0;
$graphs['hits']['hits']=$status[CACHEPREFIX.'statistics']['hits'];
$graphs['hits']['misses']=$status[CACHEPREFIX.'statistics']['misses'];
$graphs['hits']['blacklist']=$status[CACHEPREFIX.'statistics']['blacklist_misses'];
$graphs['hits']['total']=array_sum($graphs['hits']);
$graphs['restarts']['total']=0;
$graphs['restarts']['manual']=$status[CACHEPREFIX.'statistics']['manual_restarts'];
$graphs['restarts']['keys']=$status[CACHEPREFIX.'statistics']['hash_restarts'];
$graphs['restarts']['memory']=$status[CACHEPREFIX.'statistics']['oom_restarts'];
$graphs['restarts']['total']=array_sum($graphs['restarts']);
foreach ( $graphs as $caption=>$graph) {
echo '<div class="graph"><div class="h">',$caption,'</div><table border="0" cellpadding="0" cellspacing="0">';
foreach ($graph as $label=>$value) {
if ($label=='total') { $key=0; $total=$value; $totaldisplay='<td rowspan="3" class="total"><span>'.($total>999999?round($total/1024/1024).'M':($total>9999?round($total/1024).'K':$total)).'</span><div></div></td>'; continue;}
$percent=$total?floor($value*100/$total):''; $percent=!$percent||$percent>99?'':$percent.'%';
echo '<tr>',$totaldisplay,'<td class="actual">', ($value>999999?round($value/1024/1024).'M':($value>9999?round($value/1024).'K':$value)),'</td><td class="bar ',$colors[$key],'" height="',$percent,'">',$percent,'</td><td>',$label,'</td></tr>';
$key++; $totaldisplay='';
}
echo '</table></div>',"\n";
}
}
function meta_display() {
?>
<div class="meta">
<a href="http://files.zend.com/help/Zend-Server-6/content/zendoptimizerplus.html">directives guide</a> |
<a href="http://files.zend.com/help/Zend-Server-6/content/zend_optimizer+_-_php_api.htm">functions guide</a> |
<a href="https://wiki.php.net/rfc/optimizerplus">wiki.php.net</a> |
<a href="http://pecl.php.net/package/ZendOpcache">pecl</a> |
<a href="https://github.com/zend-dev/ZendOptimizerPlus/">Zend source</a> |
<a href="https://gist.github.com/ck-on/4959032/?ocp.php">OCP latest</a>
</div>
<?php
}

View File

@ -112,6 +112,10 @@ class Html
array(
'name' => 'Opcache GUI',
'path' => '/opcache.php'
),
array(
'name' => 'Opcache Control Panel',
'path' => '/vendor/ocp.php'
)
)
)

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
github: [cytopia]
patreon: devilbox

View File

@ -195,6 +195,13 @@ jobs:
make test-smoke-framework-wordpress
if: success() || failure()
- name: "Test Container"
shell: bash
run: |
cd .tests/
make test-smoke-container
if: success() || failure()
# ------------------------------------------------------------
# Finish
# ------------------------------------------------------------

View File

@ -209,6 +209,13 @@ jobs:
make test-smoke-framework-wordpress
if: success() || failure()
- name: "Test Container"
shell: bash
run: |
cd .tests/
make test-smoke-container
if: success() || failure()
# ------------------------------------------------------------
# Finish
# ------------------------------------------------------------

View File

@ -266,6 +266,13 @@ jobs:
make test-smoke-framework-wordpress
if: success() || failure()
- name: "Test Container"
shell: bash
run: |
cd .tests/
make test-smoke-container
if: success() || failure()
# ------------------------------------------------------------
# Finish
# ------------------------------------------------------------

View File

@ -107,6 +107,7 @@ test-smoke-vendors:
$(PWD)/tests/vendor-phpmyadmin.sh
$(PWD)/tests/vendor-phppgadmin.sh
$(PWD)/tests/vendor-phpredmin.sh
$(PWD)/tests/vendor-ocp.sh
###
@ -160,6 +161,13 @@ test-smoke-framework-wordpress:
$(PWD)/tests/framework-wordpress.sh
###
### Container
###
test-smoke-container:
$(PWD)/tests/container-mysql.sh
# -------------------------------------------------------------------------------------------------
# Helper Targets
# -------------------------------------------------------------------------------------------------

103
.tests/tests/container-mysql.sh Executable file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env bash
# NOTE: Parsing curl to tac to circumnvent "failed writing body"
# https://stackoverflow.com/questions/16703647/why-curl-return-and-error-23-failed-writing-body
set -e
set -u
set -o pipefail
SCRIPT_PATH="$( cd "$(dirname "$0")" && pwd -P )"
DVLBOX_PATH="$( cd "${SCRIPT_PATH}/../.." && pwd -P )"
# shellcheck disable=SC1090
. "${SCRIPT_PATH}/../scripts/.lib.sh"
RETRIES=10
DISABLED_VERSIONS=("")
echo
echo "# --------------------------------------------------------------------------------------------------"
echo "# [Container] MySQL"
echo "# --------------------------------------------------------------------------------------------------"
echo
# -------------------------------------------------------------------------------------------------
# Pre-check
# -------------------------------------------------------------------------------------------------
PHP_VERSION="$( get_php_version "${DVLBOX_PATH}" )"
if [[ ${DISABLED_VERSIONS[*]} =~ ${PHP_VERSION} ]]; then
printf "[SKIP] Skipping all checks for PHP %s\\n" "${PHP_VERSION}"
exit 0
fi
# -------------------------------------------------------------------------------------------------
# ENTRYPOINT
# -------------------------------------------------------------------------------------------------
###
### Get required env values
###
MYSQL_ROOT_PASSWORD="$( "${SCRIPT_PATH}/../scripts/env-getvar.sh" "MYSQL_ROOT_PASSWORD" )"
DB_NAME="my_db"
TBL_NAME="my_table"
ROWS=2000 # how many insert statements
GROUPED=1000 # how many grouped inserts: INSERT INTO tbl VALUES ('1') ('2') ('3');
DATALEN=200 # Length of the data per value
# Install pipe viewer
run "docker-compose exec --user root -T php bash -c 'apt update && apt install -y pv'" "${RETRIES}" "${DVLBOX_PATH}"
# Drop database
run "docker-compose exec --user devilbox -T php bash -c 'mysql --host=mysql --user=root --password='\\''${MYSQL_ROOT_PASSWORD}'\\'' -e '\\''DROP DATABASE IF EXISTS ${DB_NAME};'\\'''" "${RETRIES}" "${DVLBOX_PATH}"
# Delete mysql.sql file
run "docker-compose exec --user devilbox -T php bash -c 'rm -f /home/devilbox/mysql.sql'" "${RETRIES}" "${DVLBOX_PATH}"
# Create SQL File
run "docker-compose exec --user devilbox -T php bash -c '
(
echo \"CREATE DATABASE ${DB_NAME} COLLATE '\\''utf8mb4_bin'\\'';\";
echo \"USE ${DB_NAME};\";
echo \"CREATE TABLE ${TBL_NAME} (
id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
dt varchar(256) COLLATE '\\''utf8mb4_bin'\\'' NOT NULL
);\";
>&2 printf \"Creating random mysql.sql file: \";
for i in \$(seq ${ROWS}); do
MY_VAL=\$(openssl rand -hex ${DATALEN});
MY_VAL=\${MY_VAL:$((DATALEN-1))};
echo \"INSERT INTO ${TBL_NAME} (dt) VALUES\";
for num in \$( seq $((GROUPED-1)) ); do
echo \" ('\\''\${i}-\${num} \${MY_VAL}'\\''), \";
done
echo \" ('\\''\${i}-\${GROUPED} \${MY_VAL}'\\''); \";
>&2 printf \".\";
done;
) > /home/devilbox/mysql.sql
'" "${RETRIES}" "${DVLBOX_PATH}"
printf "\\n"
run "docker-compose exec --user devilbox -T php bash -c 'ls -lap /home/devilbox/mysql.sql'" "${RETRIES}" "${DVLBOX_PATH}"
# Import SQL file
run "docker-compose exec --user devilbox -T php bash -c 'pv -f -i 1 -p -t -e /home/devilbox/mysql.sql | mysql --host=mysql --user=root --password='\\''${MYSQL_ROOT_PASSWORD}'\\'''" "${RETRIES}" "${DVLBOX_PATH}"
# Compare inserted rows
COUNT="$( run "docker-compose exec --user devilbox -T php bash -c 'mysql --host=mysql --user=root --password='\\''${MYSQL_ROOT_PASSWORD}'\\'' -e '\\''SELECT COUNT(*) AS cnt FROM ${DB_NAME}.${TBL_NAME};'\\''' | grep -Ei '[0-9]+'" "1" "${DVLBOX_PATH}" )"
COUNT="$( echo "${COUNT}" | grep -Eo '[0-9]+' )"
if [ "${COUNT}" -ne "$(( ROWS * GROUPED ))" ]; then
>&2 echo "Error, Expected rows $(( ROWS * GROUPED )), found rows: ${COUNT}"
exit 1
fi
echo "Success, Expected rows $(( ROWS * GROUPED )), found rows: ${COUNT}"

81
.tests/tests/vendor-ocp.sh Executable file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
# NOTE: Parsing curl to tac to circumnvent "failed writing body"
# https://stackoverflow.com/questions/16703647/why-curl-return-and-error-23-failed-writing-body
set -e
set -u
set -o pipefail
SCRIPT_PATH="$( cd "$(dirname "$0")" && pwd -P )"
DVLBOX_PATH="$( cd "${SCRIPT_PATH}/../.." && pwd -P )"
# shellcheck disable=SC1090
. "${SCRIPT_PATH}/../scripts/.lib.sh"
RETRIES=10
DISABLED_VERSIONS=("")
echo
echo "# --------------------------------------------------------------------------------------------------"
echo "# [Vendor] Opcache Control Panel"
echo "# --------------------------------------------------------------------------------------------------"
echo
# -------------------------------------------------------------------------------------------------
# Pre-check
# -------------------------------------------------------------------------------------------------
PHP_VERSION="$( get_php_version "${DVLBOX_PATH}" )"
if [[ ${DISABLED_VERSIONS[*]} =~ ${PHP_VERSION} ]]; then
printf "[SKIP] Skipping all checks for PHP %s\\n" "${PHP_VERSION}"
exit 0
fi
# -------------------------------------------------------------------------------------------------
# ENTRYPOINT
# -------------------------------------------------------------------------------------------------
###
### Get required env values
###
HOST_PORT_HTTPD="$( "${SCRIPT_PATH}/../scripts/env-getvar.sh" "HOST_PORT_HTTPD" )"
###
### Ensure Opcache Control Panel works
###
URL="/vendor/ocp.php"
printf "[TEST] Fetch %s" "${URL}"
if [ "$( run "\
curl -sS --fail 'http://localhost:${HOST_PORT_HTTPD}${URL}' \
| tac \
| tac \
| grep -Ec 'Used Memory'" \
"${RETRIES}" "" "0" )" != "1" ]; then
printf "\\r[FAIL] Fetch %s\\n" "${URL}"
run "curl -sS 'http://localhost:${HOST_PORT_HTTPD}${URL}' || true"
run "curl -sS -I 'http://localhost:${HOST_PORT_HTTPD}${URL}' || true"
exit 1
else
printf "\\r[OK] Fetch %s\\n" "${URL}"
fi
URL="/vendor/ocp.php?FILES=1&GROUP=2&SORT=3"
printf "[TEST] Fetch %s" "${URL}"
if [ "$( run "\
curl -sS --fail 'http://localhost:${HOST_PORT_HTTPD}${URL}' \
| tac \
| tac \
| grep -Ec 'files cached'" \
"${RETRIES}" "" "0" )" != "1" ]; then
printf "\\r[FAIL] Fetch %s\\n" "${URL}"
run "curl -sS 'http://localhost:${HOST_PORT_HTTPD}${URL}' || true"
run "curl -sS -I 'http://localhost:${HOST_PORT_HTTPD}${URL}' || true"
exit 1
else
printf "\\r[OK] Fetch %s\\n" "${URL}"
fi

View File

@ -365,5 +365,6 @@ script:
retry make test-smoke-autostart &&
retry make test-smoke-framework-cakephp &&
retry make test-smoke-framework-drupal &&
retry make test-smoke-framework-wordpress;
retry make test-smoke-framework-wordpress &&
retry make test-smoke-container;
fi

View File

@ -7,6 +7,24 @@ major versions.
## Unreleased
## Release v1.6.0 (2020-01-04)
#### Changed
- [#642](https://github.com/cytopia/devilbox/issues/642) Make email catch-all configurable
- [#265](https://github.com/cytopia/devilbox/issues/265) Make SSL vhost settings configurable
#### Added
- [#615](https://github.com/cytopia/devilbox/issues/615) Add phpmd
- [#378](https://github.com/cytopia/devilbox/issues/378) Allow to mount local `.ssh/` directory into PHP container (read-only)
## Release v1.5.0 (2020-01-03)
#### Added
- [#654](https://github.com/cytopia/devilbox/issues/654) Added Opcache Control Panel
- Integration tests for MySQL Docker image
## Release v1.4.0 (2020-01-02)
#### Fixed

View File

@ -95,7 +95,7 @@ services:
# PHP
# ------------------------------------------------------------
php:
image: devilbox/php-fpm:${PHP_SERVER}-work-0.98
image: devilbox/php-fpm:${PHP_SERVER}-work-0.100
hostname: php
##
@ -135,7 +135,7 @@ services:
##
## Mail-catching
##
- ENABLE_MAIL=2
- ENABLE_MAIL=${PHP_MAIL_CATCH_ALL:-2}
##
## Enable 127.0.0.1 Port-forwarding
@ -202,6 +202,9 @@ services:
# Certificate Authority public key
- ${DEVILBOX_PATH}/ca:/ca:rw${MOUNT_OPTIONS}
# Users SSH directory (read-only)
- ${HOST_PATH_SSH_DIR}:/home/devilbox/.ssh:ro${MOUNT_OPTIONS}
depends_on:
- bind
@ -245,7 +248,7 @@ services:
- MAIN_VHOST_ENABLE=${DEVILBOX_UI_ENABLE}
- MAIN_VHOST_STATUS_ENABLE=1
- MAIN_VHOST_STATUS_ALIAS=/devilbox-httpd-status
- MAIN_VHOST_SSL_TYPE=both
- MAIN_VHOST_SSL_TYPE=${HTTPD_VHOST_SSL_TYPE:-both}
- MAIN_VHOST_SSL_GEN=1
- MAIN_VHOST_SSL_CN=${DEVILBOX_UI_SSL_CN:-localhost}
@ -256,7 +259,7 @@ services:
- MASS_VHOST_TLD=.${TLD_SUFFIX}
- MASS_VHOST_DOCROOT=${HTTPD_DOCROOT_DIR}
- MASS_VHOST_TPL=${HTTPD_TEMPLATE_DIR}
- MASS_VHOST_SSL_TYPE=both
- MASS_VHOST_SSL_TYPE=${HTTPD_VHOST_SSL_TYPE:-both}
- MASS_VHOST_SSL_GEN=1
##
@ -264,7 +267,7 @@ services:
##
- COMPAT=${PHP_SERVER}
- PHP_FPM_ENABLE=1
- PHP_FPM_SERVER_ADDR=php
- PHP_FPM_SERVER_ADDR=172.16.238.10
- PHP_FPM_SERVER_PORT=9000
- PHP_FPM_TIMEOUT=${HTTPD_TIMEOUT_TO_PHP_FPM:-180}

View File

@ -183,6 +183,12 @@
PHP Code Beautifier and Fixer <img src="https://raw.githubusercontent.com/cytopia/icons/master/11x11/ext-link.png" />
</a>
.. |ext_lnk_tool_phpmd| raw:: html
<a target="_blank" href="https://phpmd.org/">
PHP Mess Detector <img src="https://raw.githubusercontent.com/cytopia/icons/master/11x11/ext-link.png" />
</a>
.. |ext_lnk_tool_phpunit| raw:: html
<a target="_blank" href="https://phpunit.de/">

View File

@ -923,6 +923,23 @@ always be ``/shared/httpd/``.
``docker-compose rm``.
HOST_PATH_SSH_DIR
-----------------
The path on your host OS of the ssh directory to be mounted into the
PHP container into ``/home/devilbox/.ssh``.
.. note::
The path is mounted read-only to ensure you cannot accidentally
delete any ssh keys from inside the php container.
+------------------------------+----------------+----------------+
| Name | Allowed values | Default value |
+==============================+================+================+
| ``HOST_PATH_SSH_DIR`` | valid path | ``~/.ssh`` |
+------------------------------+----------------+----------------+
Docker host ports
=================
@ -1164,6 +1181,36 @@ Example:
# Disable Xdebug, Imagick and Swoole
PHP_MODULES_DISABLE=xdebug,imagick,swoole
PHP_MAIL_CATCH_ALL
^^^^^^^^^^^^^^^^^^
Postfix settings for email catch-all.
When set to ``0`` postfix will be disabled and not started.
When set to ``1`` postfix is normally started and made available. However you still need
to configure it to your needs yourself. For that you can use the autostart scripts
and define a couple of ``postconf -e name=value`` commands.
When set to ``2`` (email catch-all) postfix is started, but no mail will leave the Devilbox. It is automatically
internally routed the the devilbox mail account and you can see each sent mail
in the bundled intranet: https://localhost/mail.php
+-------------------------+--------------------------------------+---------------------------------------------------+
| Name | Allowed values | Default value |
+=========================+======================================+===================================================+
| ``PHP_MAIL_CATCH_ALL`` | ``0``, ``1``, ``2`` | ``2`` |
+-------------------------+--------------------------------------+---------------------------------------------------+
Example:
.. code-block:: bash
:caption: .env
:emphasize-lines: 2
# Enable Postfix without email catch-all
PHP_MAIL_CATCH_ALL=1
Custom variables
^^^^^^^^^^^^^^^^
@ -1205,6 +1252,27 @@ This will then output ``development``.
Web server
----------
HTTPD_SSL_TYPE
^^^^^^^^^^^^^^
SSL (HTTP/HTTPS) settings for automated vhost generation.
By default each project will have two vhosts (one for HTTP and one for HTTPS).
You can control the SSL settings for your projects via the below stated values.
This is internally achieved via the ``-m`` argument of |ext_lnk_project_vhost_gen|
* ``both`` will serve HTTP and HTTPS for all projects
* ``redir`` will always redirect HTTP to HTTPS
* ``ssl`` will only serve HTTPS
* ``plain`` will only serve HTTP
+-----------------------+-----------------------------------------+------------------+
| Name | Allowed values | Default value |
+=======================+=========================================+==================+
| ``HTTPD_SSL_TYPE`` | ``both``, ``redir``, ``ssl``, ``plain`` | ``both`` |
+-----------------------+-----------------------------------------+------------------+
.. _env_httpd_docroot_dir:
HTTPD_DOCROOT_DIR

View File

@ -77,6 +77,8 @@ The PHP container is your workhorse and these are your tools:
+----------------------+---------------------------------------+
| ``phpcbf`` | |ext_lnk_tool_phpcbf| |
+----------------------+---------------------------------------+
| ``phpmd`` | |ext_lnk_tool_phpmd| |
+----------------------+---------------------------------------+
| ``phpunit`` | |ext_lnk_tool_phpunit| |
+----------------------+---------------------------------------+
| ``photon`` | |ext_lnk_tool_photon| |

View File

@ -28,9 +28,10 @@
### Show all executed commands in each
### docker image during docker-compose up?
###
### 1: Yes
### 0: No
DEBUG_COMPOSE_ENTRYPOINT=1
### 0: Quiet
### 1: Verbose
### 2: More verbose
DEBUG_COMPOSE_ENTRYPOINT=2
###
@ -441,6 +442,16 @@ MOUNT_OPTIONS=
HOST_PATH_HTTPD_DATADIR=./data/www
###
### The path on your host OS of the ssh directory to be mounted into the
### PHP container into /home/devilbox/.ssh.
###
### IMPORTANT: The path is mounted read-only to ensure you cannot accidentally
## delete anything inside the php container.
###
HOST_PATH_SSH_DIR=~/.ssh
################################################################################
###
@ -470,6 +481,25 @@ PHP_MODULES_ENABLE=
PHP_MODULES_DISABLE=oci8,PDO_OCI,pdo_sqlsrv,sqlsrv,rdkafka,swoole
###
### Postfix settings for email catch-all
###
### When set to '1' postfix is normally started and made available. However you still need
### to configure it to your needs yourself. For that you can use the autostart scripts
### and define a couple of 'postconf -e name=value' commands.
###
### When set to '2' (email catch-all), no mail will leave the Devilbox. It is automatically
### internally routed the the devilbox mail account and you can see each sent mail
### in the bundled intranet: https://localhost/mail.php
###
### Values:
### 0: Disable postfix (do not start it)
### 1: Enable/Start postfix
### 2: Enable/Start postfix and enable email catch-all
###
PHP_MAIL_CATCH_ALL=2
###
### Configure everything else about PHP in
### * cfg/php-ini-X.X/*.ini
@ -490,6 +520,23 @@ HOST_PORT_HTTPD=80
HOST_PORT_HTTPD_SSL=443
###
### SSL (HTTP/HTTPS) settings for automated vhost generation
###
### By default each project will have two vhosts (one for HTTP and one for HTTPS).
### You can control the SSL settings for your projects via the below stated values.
###
### This is internally achieved via the '-m' argument of https://github.com/devilbox/vhost-gen
###
### Values:
### * both: Serve HTTP and HTTPS for all projects
### * redir: HTTP always redirects to HTTPS
### * ssl: Only serve HTTPS
### * plain: Only serve HTTP
###
HTTPD_VHOST_SSL_TYPE=both
###
### Document Root Subdirectory
###
@ -659,12 +706,14 @@ HOST_PORT_MONGO=27017
###
HOST_PORT_BIND=1053
###
### Add comma separated DNS server from which you want to receive DNS
### You can also add DNS servers from your LAN (if any are available)
###
BIND_DNS_RESOLVER=8.8.8.8,8.8.4.4
###
### Validate DNSSEC
###