
在上一篇文章中,我们创建了插件的基本结构。现在是时候为每个小部件实现渲染类了。
回想一下,所有小部件提供程序都必须实现 Provider 接口。它们还必须位于名为 widget 的文件夹内,并位于名称空间 AX\StatBoard\Widget 下。如果我们想添加一种新的指标,只需创建一个相应的类,并创建一个对象并使用 add_provider 方法将其添加到 Widget 类中。
RAM 使用情况小部件
我们想要显示的第一条信息是当前正在使用的 RAM 量以及当前空闲的 RAM 量。
在本例中,free -m 是我们的朋友 - 它告诉我们 RAM 使用情况。 -m 开关是以兆字节为单位输出结果。
[vagrant@vagrant-centos64 ~]$ free -m
total used free shared buffers cached
Mem: 589 366 223 0 9 57
-/+ buffers/cache: 299 290
Swap: 0 0 0
我们将类命名为 Ram。相应的文件将为 widget/ram.php。我们在这里只是编写基础并实现 get_title 。
<?php
namespace AX\StatBoard\Widget;
class Ram implements Provider {
function __construct() {
}
public function get_title() {
return "Ram Usage";
}
?>
接下来,我们将实现 get_metric 来实际运行必要的 shell 命令并提取信息。稍后我将解释 get_metric 的更多细节。
<?php
function get_metric() {
$df = `free -m | grep -E "(Mem|Swap)" | awk '{print $1, $2, $3, $4}'`;
$df = explode("\n", $df);
if ( is_array( $df ) && 2 <= count( $df ) ) {
$df = array_map( function ( $line ) {
if ( empty( $line ) ) {
return;
}
$segment = preg_split( '/\s+/', $line );
return array(
'type' => trim( $segment[0]," :" ),
'total' => (int)$segment[1],
'used' => (int)$segment[2],
'free' => (int)$segment[3],
);
}, $df );
return $df;
}
return false;
}
?>
我们执行命令 free -m | grep -E "内存|交换" | awk '{print $1, $2, $3, $4}' .它的输出看起来与此类似。
[vagrant@vagrant-centos64 ~]$ free -m | grep -E "Mem|Swap" | awk '{print $1, $2, $3, $4}'
Mem: 589 541 47
Swap: 255 0 255
[vagrant@vagrant-centos64 ~]$
我们通过将行拆分为数组来使用 PHP 解析每一位信息。我们使用 array_map 循环遍历数组的所有元素,对于每一行,我们用空格分隔,然后返回一个包含元素的关联数组:
-
type:第一个字段 -
total:第二个字段 -
used:第三个字段 -
free:第四个字段
现在,是 get_content 的时候了。
public function get_content() {
$metric = $this->get_metric();
$data = array(
array('Type', 'Used(MB)', 'Free(MB)')
);
foreach ($metric as $item) {
if (empty($item)) {
continue;
}
if ($item['type'] !== 'Mem' && $item['type'] !== 'Swap') {
continue;
}
if ( 0 == ($item['free'] + $item['used'])) {
continue;
}
$data[] = array(
$item['type'],$item['used'], $item['free']
);
}
$data = json_encode($data);
echo <<<EOD
<div id="widget_ram_usage"></div>
<script type="text/javascript">
google.setOnLoadCallback(function () {
var data = google.visualization.arrayToDataTable({$data});
var options = {
isStacked: true
};
var chart = new google.visualization.ColumnChart(document.getElementById('widget_ram_usage'));
chart.draw(data, options);
})
</script>
EOD;
}
我们使用堆积条形图来显示 RAM 使用情况。
首先,我们调用 get_metric() 来获取必要的数据。然后,我们只需循环它并格式化它以匹配 Google 图表数据要求。最后,我们使用 json_encode 将它们转换为 JavaScript 对象表示法(或 JSON)。然后,我们输出一个 HTML 代码,其中包含 div 元素来保存图表对象。
最后,我们调用相应的 Google Chart API 将图表渲染到 div 元素中。

安装的软件
我们将介绍的第二个小部件是显示已安装软件的小部件。它是一个小部件,旨在显示我们在服务器上有哪些常见软件包以及哪个版本。
例如,我们是否安装了 NodeJS,是否安装了 Ruby?我们使用的是哪个版本的 PHP?等等。
让我们使用以下初始内容创建 widget/software.php:
<?php
namespace AX\StatBoard\Widget;
class Software implements Provider {
function __construct() {
}
public function get_title() {
return "Installed Software";
}
function get_metric() {
$cmds = array();
$package = array(
'php' => '-v',
'node' => '-v',
'mysql' => '-V',
'vim' => '--version',
'python' => '-V',
'ruby' => '-v',
'java' => '-version',
'curl' => '-V');
foreach ( $package as $cmd=>$version_query ) {
if ( NULL == $cmds[$cmd] = shell_exec( "which $cmd" ) ) {
$cmds[ $cmd ] = 'Not installed';
continue;
}
$version = shell_exec( "$cmd $version_query" );
$version = explode( "\n", $version );
if ( is_array( $version ) ) {
$version = array_shift( $version );
}
$cmds[ $cmd ] .= '<br>' . $version;
}
return $cmds;
}
因此,与往常一样,我们有 get_title ,它只返回一个简单的字符串。对于 get_metric(),我们想知道是否安装了特定的软件。如果有,则获取其版本信息。
为此,我们使用显示软件版本的开关创建一个命令数组。以PHP为例,php -v 显示版本信息,mysql --version 显示MySQL信息。
如果命令返回且错误或未找到命令,则 shell_exec 将返回 false。此时,我们可以判断该软件没有安装;否则,我们可以解析结果以显示版本信息。然后,我们逐行分割结果,并检索第一行作为版本信息。这是因为我们需要的信息只能在第一行找到。
对于其他应用程序,某些命令过于冗长,包含许多信息。一旦我们有了数据,就可以创建 get_content 方法了。
public function get_content() {
$cmds = $this->get_metric();
$content = '';
foreach ( $cmds as $cmd => $info ) {
$content .= "<p><strong>$cmd</strong> $info</p>";
}
echo $content;
}
我们只是展示此类数据的基本表格。这是显示时的仪表板:

磁盘使用情况
现在我们将解决磁盘使用问题。我们将处理此任务的类命名为 Disk。让我们先制作基本骨架。
<?php
namespace AX\StatBoard\Widget;
class Disk implements Provider {
function __construct() {
}
public function get_title() {
return "Disk Usage";
}
}
与往常一样,我们必须实现 Provider 接口。我们在这里为我们的小部件设置一个标题。接下来是我们课程的核心:获取磁盘使用情况的方法。
<?php
function get_metric() {
$df = `df -h`;
$df = explode("\n", $df);
if (is_array($df) && count($df)>=2) {
array_shift($df); //Get rid the first line
$df = array_map(function ($line) {
if (empty($line)) {
return NULL;
}
$segment=preg_split('/\s+/', $line);
return array(
'filesystem' => $segment[0],
'size' => $segment[1],
'used' => $segment[2],
'available' => $segment[3],
'use_percent' => $segment[4],
);
}, $df);
return $df;
}
return false;
}
在本系列的第一部分中,我们对 df 命令有了一些了解,因此理解以下命令应该很容易:
回想一下 df 输出:
[vagrant@vagrant-centos64 ~]$ df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 7.3G 1.4G 5.6G 20% / tmpfs 295M 0 295M 0% /dev/shm /vagrant 60G 55G 4.9G 92% /vagrant /data/GeoIP 60G 55G 4.9G 92% /data/GeoIP /var/webapps 60G 55G 4.9G 92% /var/webapps /var/www/html 60G 55G 4.9G 92% /var/www/html
我们将其逐行拆分,将其变成数组。我们循环遍历每一行,用空格分割整行,再次将其转换为数组。然后,我们只需将值映射为更友好、人类可读的关联数组。当我们有了这些数据后,我们可以将其放入 get_content.
public function get_content() {
$metric = $this->get_metric();
$data = array(
array( 'Disk', 'Space' )
);
$disk_container = array();
$data_partition = array(
array('Filesystem', 'Free(GB)', 'Used(GB)')
);
foreach ( $metric as $disk ) {
$size = intval( $disk['size'] );
if ( 'M' == substr( $disk['size'], -1 ) ) {
$size = round( $size / 1024, 2 );
}
$used = intval( $disk['used'] );
if ('M' == substr( $disk['used'], -1 ) ) {
$used = round( $used / 1024, 2 );
}
if ( empty( $size ) ) {
continue;
}
$data[] = array( $disk['filesystem'], $size );
$data_partition[] = array($disk['filesystem'], $size - $used, $used);
}
}
我们遍历度量数组并尝试将 MB 空间转换为 GB 空间。我们构建一个数组来匹配图表数据格式要求。数据数组应如下所示:
[ ['File System', 'Free', 'Used', ['/dev/sda1', 10, 24], ['/dev/sda2', 28, 19]]
一旦我们有了数据,我们就开始渲染图表。我们将制作两个图表:
- 第一个图表显示了每个已安装文件系统的总空间。对于此数据,我们将使用饼图。
- 第二个图表用于显示每个已安装文件系统的磁盘使用情况。为此,我们将使用条形图。
为此,我们将方法修改为以下内容:
public function get_content() {
$metric = $this->get_metric();
$data = array(
array('Disk', 'Space')
);
$disk_container = array();
$data_partition = array(
array('Filesystem', 'Free(GB)', 'Used(GB)')
);
foreach ($metric as $disk) {
$size = intval($disk['size']);
if ('M' == substr($disk['size'], -1)) {
$size = round($size / 1024, 2);
}
$used = intval($disk['used']);
if ('M' == substr($disk['used'], -1)) {
$used = round($used / 1024, 2);
}
if (empty($size)) {
continue;
}
$data[] = array($disk['filesystem'], $size);
$data_partition[] = array($disk['filesystem'], $size - $used, $used);
}
$data = json_encode($data);
$data_partition = json_encode($data_partition);
echo <<<EOD
<div id="widget_disk_usage"></div>
<div id="widget_disk_partion"></div>
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(function () {
var data = google.visualization.arrayToDataTable({$data});
var options = {
is3D: true,
};
var chart = new google.visualization.PieChart(document.getElementById('widget_disk_usage'));
chart.draw(data, options);
var data2 = google.visualization.arrayToDataTable({$data_partition});
var options2 = {
isStacked: true
};
var chart2 = new google.visualization.ColumnChart(document.getElementById('widget_disk_partion'));
chart2.draw(data2, options2);
})
</script>
EOD;
}
我们创建了两个 div 元素来包含信息
<div id="widget_disk_usage"></div>
<div id="widget_disk_partion"></div>
然后,使用图表 API 的绘制方法将图表渲染在这些元素内。这里最令人困惑的事情可能是我们图表的数据格式。
结果如下:

服务器信息
这个小部件向我们显示信息:Linux 内核、CPU 架构、正常运行时间、IP 地址。我们这里不需要图表,一个简单的数据表就可以完成这项工作。调用该类是Server。这是 widget/server.php
的第一个内容
<?php
namespace AX\StatBoard\Widget;
use DateTime;
class Server implements Provider {
function __construct() {
}
public function get_title() {
return "Server Info";
}
/**
* Return server info: OS, Kernel, Uptime, and hostname
* @return array with 3 metric:
* * hostname
* * os
* * uptime
*/
function get_metric() {
$server = array();
$server['hostname'] = `hostname`;
$server['os'] = `uname -sr`;
$server['core'] = `grep -c ^processor /proc/cpuinfo`;
$total_uptime_sec = time() - `cut -d. -f1 /proc/uptime`;
$now = new DateTime("now");
$server['uptime'] = $now->diff(new DateTime("@$total_uptime_sec"))->format('%a days, %h hours, %i minutes and %s seconds');
// Get the external ip with ifconfig.me, a website that show you ip address in plaintext
// when sending request with curl header
$server['ip'] = `curl ifconfig.me`;
$server['ram'] = `free -m | grep Mem | awk '{print $2}'`;
$server['cpu'] =`cat /proc/cpuinfo | grep "model name" | awk '{print $4,$5,$6,$7}'`;
return $server;
}
}
至此,您应该熟悉 get_title()。我们只是返回这个小部件的标题。
首先,我们使用语句 use DateTime 因为我们位于名称空间 AX\StatBoard\Widget 中,并且 DateTime 类来自全局命名空间。每次我们想要使用 DateTime 时,我们都必须输入 \DateTime。因此,我们使用命名空间导入来使 DateTime 名称在我们当前的命名空间中可用。
将其视为符号链接。在 get_metric 中,我们运行 shell 命令来获取结果并将其分配回来。
主机名
显示您的服务器主机名。
名称-sr
显示Linux内核信息:
[vagrant@vagrant-centos64 ~]$ uname -sr Linux 2.6.32-358.23.2.el6.x86_64
grep -c ^处理器/proc/cpuinfo
-c 开关打印输入字符串中匹配行的计数。 /proc/cpuinfo 包含处理器信息。我们 grep 它并计算文字处理器的出现次数。这是我的 32 核结果。
$ grep -c ^processor /proc/cpuinfo 32
剪切-d。 -f1 /proc/uptime
此命令显示服务器已启动并运行的秒数。我们将秒数转换为“x 天 y 小时 z 分钟”的格式,以使其更加用户友好。
使用 DateTime::diff 我们可以轻松实现这一点。我们创建一个带有当前时间戳的 DateTime 对象,另一个带有时间戳的对象是当前时间戳减去正常运行时间的秒数。然后使用 format 方法将其格式化为人类友好的字符串。
这是我的结果,正常运行时间为 26194091 秒。
$ cut -d. -f1 /proc/uptime 26194091
卷曲 ifconfig.me
ifconfig.me 是一项在浏览器内直接访问时显示您的 IP 地址的服务。如果您使用 curl 向其发送请求,它将以单个字符串形式返回您的 IP 地址。
[vagrant@vagrant-centos64 ~]$ curl ifconfig.me 76.102.253.237
CPU型号
如上所述,/proc/cpuinfo存储了CPU信息。我们可以从中提取CPU型号。例如:
[vagrant@vagrant-centos64 ~]$ cat /proc/cpuinfo | grep "model name" | awk '{print $4,$5,$6,$7}'
Intel(R) Core(TM) i5-4250U CPU
一旦我们在数组中获得了所有可用数据,我们就会返回它并向 get_content 方法提供这些数据。这是 get_content,仅显示数据:
public function get_content() {
$server = $this->get_metric();
echo <<<EOD
<strong>Ip Address</strong> {$server['ip']}<br>
<strong>CPU</strong> {$server['cpu']}<br>
<strong>Number of Core</strong> {$server['core']}<br>
<strong>Ram</strong> {$server['ram']}<br>
<strong>Hostname</strong> {$server['hostname']}<br>
<strong>OS</strong> {$server['os']}<br>
<strong>Uptime</strong> {$server['uptime']}<br>
EOD;
}
这是仪表板上的小部件。

处理器
监控我们的处理器是其中之一我们可以展示的最重要的东西。我们想知道某个特定进程正在使用多少 CPU 和/或消耗了多少内存。我们将我们的类称为 Process,首先从 get_title 和 get_metric 开始。我将在代码后面解释 get_metric 的更多细节:
<?php
namespace AX\StatBoard\Widget;
class Process implements Provider {
public function get_title() {
return "Processes";
}
/**
* Return server info: OS, Kernel, Uptime, and hostname
* @return array with 3 metric:
* * hostname
* * os
* * uptime
*/
function get_metric() {
$processes = array();
$output = `ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\[' | sort -k 1 -r | head -30 | awk '{print $4,$3,$1,$2,$7,$6,$5}'`;
$output = explode("\n", $output);
if (!is_array($output) || count($output)<2) {
return false;
}
array_shift($output);
foreach ($output as $line) {
//$line = preg_split('/\s+/', $line);
$line = explode(' ', $line);
if (count($line)<6) {
continue;
}
//var_dump($line);
//echo count($line);
if (empty($processes[$line[6]])) {
$processes[$line[6]] = array_combine(array('user', 'pid', '%cpu', '%mem','start','time', 'command'), $line);
} else {
$processes[$line[6]]['%cpu'] += $line[2];
$processes[$line[6]]['%mem'] += $line[3];
}
}
return $processes;
}
}
显示进程正在运行的命令是 ps。它通过开关 -e 提供了广泛的信息,因为它允许我们查看每个进程。对于我们的小部件,我们只需要提取 COU、内存、PID、用户、参数、时间和启动。
我们可以结合 -o 表示用户定义的格式,例如: ps -eo pcpu,pmem,pid,user,args,time,start。如果您尝试该命令,您将得到一些奇怪的过程,例如:
[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start %CPU %MEM PID USER COMMAND TIME STARTED 0.0 0.2 1 root /sbin/init 00:00:00 06:50:39 0.0 0.0 2 root [kthreadd] 00:00:00 06:50:39 0.0 0.0 3 root [migration/0] 00:00:00 06:50:39
注意 [kthread]、[migration/0]。基本上,这意味着该命令无法在文件系统中找到。它可能是一些内部系统进程或内核线程,我们可能永远不想关心它。因此,我们应该使用 grep 来消除这些进程。 grep 有 -v 开关使我们能够反转匹配。它返回的结果不包含我们传递给它的字符串。
[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\[' %CPU %MEM PID USER COMMAND TIME STARTED 0.0 0.2 1 root /sbin/init 00:00:00 06:50:39 0.0 0.1 292 root /sbin/udevd -d 00:00:00 06:50:41 0.0 0.1 811 root /sbin/dhclient -H vagrant-c 00:00:00 06:50:48 0.0 0.2 948 root /sbin/rsyslogd -i /var/run/ 00:00:00 06:50:50 0.0 0.1 966 rpc rpcbind 00:00:00 06:50:50 0.0 0.2 984 rpcuser rpc.statd 00:00:00 06:50:50 0.0 0.0 1011 root rpc.idmapd 00:00:00 06:50:51 0.0 0.2 1073 root /usr/sbin/VBoxService 00:00:00 06:50:51
为了使数据看起来不错,我们应该按内存或CPU对进程进行排序。在我们的教程中,我们按 %MEM 排序。我们可以使用Linux的sort命令来做到这一点。 %MEM 是第二列。
就像索引为零的数组一样,第二个元素通过索引键 1 访问。我们可以使用 sort -k 1。它按从最低到最高的顺序排序。我们实际上关心的是首先消耗大量内存的进程。为此,我们应该使用 sort -k 1 -r 反转顺序。一旦我们得到结果,我们可能只需要前30个过程。当然,这取决于您,因为您可以选择包含所有内容,但我想保持简短。 30 听起来是一个合理的数字。
最后,我们使用 awk 来格式化输出。这是我们的命令和示例输出:
[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\[' | sort -k 1 | head -30 | awk '{print $4,$3,$1,$2,$7,$6,$5}'
root 1151 0.0 0.0 00:00:00 -d /sbin/udevd
root 1152 0.0 0.0 00:00:00 -d /sbin/udevd
root 292 0.0 0.0 00:00:00 -d /sbin/udevd
root 811 0.0 0.0 vagrant-c -H /sbin/dhclient
root 1 0.0 0.1 06:50:39 00:00:00 /sbin/init
root 2153 0.0 0.1 -q -1 /sbin/dhclient
root 3642 0.0 0.1 00:00:00 -s /usr/sbin/anacron
vagrant 3808 0.0 0.1 pcpu,pmem,pid,user,a -eo ps
vagrant 3810 0.0 0.1 1 -k sort
vagrant 3811 0.0 0.1 00:00:00 -30 head
vagrant 3812 0.0 0.1 $4,$3,$1,$2,$7,$ {print awk
root 948 0.0 0.1 /var/run/ -i /sbin/rsyslogd
rpc 966 0.0 0.1 06:50:50 00:00:00 rpcbind
root 1073 0.0 0.2 06:50:51 00:00:00 /usr/sbin/VBoxService
root 1105 0.0 0.2 06:50:51 00:00:00 /usr/sbin/sshd
root 1121 0.0 0.2 06:50:52 00:00:00 crond
rpcuser 984 0.0 0.2 06:50:50 00:00:00 rpc.statd
496 1088 0.0 0.3 -p -d memcached
vagrant 3544 0.0 0.3 00:00:00 vagrant@pts/0 sshd:
vagrant 3545 0.0 0.3 06:59:27 00:00:00 -bash
root 1113 0.0 1.7 06:50:52 00:00:00 /usr/sbin/httpd
apache 1157 0.0 4.2 06:50:53 00:00:01 /usr/sbin/httpd
apache 3438 0.0 4.2 06:55:39 00:00:01 /usr/sbin/httpd
一旦我们得到结果,我们将它分成一个数组并使用 foreach 循环它。我们将同名进程分组到一个元素中,并将 CPU 百分比和内存添加到其中。
<?php
//...
// inside get_content
foreach ( $output as $line ) {
//$line = preg_split( '/\s+/', $line );
$line = explode( ' ', $line );
if ( 6 > count( $line ) ) {
continue;
}
if ( empty( $processes[ $line[6] ] ) ) {
$processes[ $line[6]] = array_combine( array( 'user', 'pid', '%cpu', '%mem','start','time', 'command' ), $line );
} else {
$processes[ $line[6] ]['%cpu'] += $line[2];
$processes[ $line[6] ]['%mem'] += $line[3];
}
}
//...
我们使用 array_combine 从两个数组创建一个关联数组:一个用于键,一个用于值。
剩下的就是实现 get_content 方法。只是提醒我们必须实现三个方法: get_title, get_metric 和 get_content.对于这个过程,我们只想展示一个简单的表格。
我们的 get_content 方法很简单。
public function get_content() {
$processes = $this->get_metric();
$html = '<table class="wp-list-table widefat"><thead><tr>
<th>User</th>
<th>Pid</th>
<th>%CPU</th>
<th>%Mem</th>
<th>Command</th>
</tr></thead><tbody>';
foreach ($processes as $process) {
$html .= "<tr>
<td>{$process['user']}</td>
<td>{$process['pid']}</td>
<td>{$process['%cpu']}</td>
<td>{$process['%mem']}</td>
<td>{$process['command']}</td>
</tr>";
}
$html .= '</tbody></table>';
echo $html;
}
这是我们得到的结果:

平均负载
Linux 有一个命令可以显示过去一分钟、五分钟和 15 分钟内 CPU 和 IO 的平均负载。让我们把它压缩成一个小部件。称之为 Cpuload,并创建我们的 widget/cpuload.php
<?php
namespace AX\StatBoard\Widget;
class Cpuload implements Provider {
function __construct() {
}
public function get_title() {
return "CPU Load";
}
function get_metric() { $number_of_core = intval(`/bin/grep -c processor /proc/cpuinfo`); $loadAvg = `cat /proc/loadavg | /usr/bin/awk '{print $1,$2,$3}'`; $loadAvg = explode(' ', $loadAvg); if ($loadAvg <3) { return false; } $loadTimes = array('1 min', '5 mins', '15 mins'); return array_map( function ($loadtime, $value, $number_of_core) { return array($loadtime, round($value * 100 / $number_of_core, 2), $value); }, $loadTimes, $loadAvg, array_fill(0, 3, $number_of_core) ); }
}
首先,我们通过读取 /proc/cpuinfo 来统计 CPU 核心数,并统计包含“processor”一词的行数。我们在服务器信息
部分对此进行了介绍。
在Linux中,/proc/loadavg保存平均负载信息。前三列分别是1分钟、5分钟和15分钟的负载。 awk 在这里用于过滤掉我们需要的字段。
➜ ~ cat /proc/loadavg
0.01 0.04 0.05 1/217 16089
➜ ~ cat /proc/loadavg | awk '{print $1, $2, $3}'
0.01 0.04 0.05
将上面的结果用空格分割,构建一个包含三个元素的数组。计算所有核心的平均负载。因此,为了获得结果,我们使用 array_map 循环遍历 $loadAvg 数组,并除以我们拥有的核心数。请注意,我们创建了 2 个与 $loadAvg 长度相同的额外数组,一个用于键,另一个用于保存核心数,以便将所有这些一一传递给 array_map。
get_content 的时间:
public function get_content() {
$metrics = $this->get_metric();
if ( ! $metrics ) {
return false;
}
// see https://google-developers.appspot.com/chart/interactive/docs/gallery/barchart#Data_Format for more detai of format
$data = array( array( 'Duration', '% Load' ) );
foreach ( $metrics as $key=>$metric ) {
array_push( $data, array( $metric[0], $metric[1] ) );
}
$data = json_encode( $data );
echo <<<EOD
<div id="avg_load"></div>
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable($data);
var options = {
hAxis: {
titleTextStyle: {color: 'red'},
minValue:0,
maxValue:100
}
};
var chart = new google.visualization.BarChart(document.getElementById('avg_load'));
chart.draw(data, options);
}
</script>
EOD;
}
我们使用条形图并从数组创建一个数据数组,然后使用 json_encode 将其转换为与条形图数据格式匹配的 JavaScript 表示法数组。
例如:
[ ["Duration","% Load"], ["1 min",20], ["5 mins",11], ["15 mins",3]]
这是渲染图表时的结果:

以太网接口
我们要处理的下一个小部件是以太网接口。某些服务器可以有多个以太网接口,并为其分配不同的 IP 地址。
看到这些信息非常有用。我们将这个类称为 Ethernet,从 widget/ethernet.php 的基本内容开始。
<?php
/**
* Adopt from https://github.com/afaqurk/linux-dash/blob/master/sh/ip.php
*
*/
namespace AX\StatBoard\Widget;
class Ethernet implements Provider {
function __construct() {
}
public function get_title() {
return "Ethernet";
}
function get_metric() {
$ethernet = array();
$output = shell_exec("ip -oneline link show | awk '{print $2}' | sed 's/://'");
if (!$output) { // It didn't work with "ip" , so we do it with ifconfig
$output = shell_exec(
'ifconfig | /bin/grep -B1 "inet addr" | /usr/bin/awk \'' .
'{ if ( $1 == "inet" ) { print $2 }' .
'else if ( $2 == "Link" ) { printf "%s:",$1 } }\' | /usr/bin/awk' .
' -F: \'{ print $1","$3 }\''
);
$output = trim($output, " \n");
$output = `ifconfig | grep "Link encap" | awk '{ print $1 }'`;
$interfaces = explode("\n", $output);
$output = `ifconfiga | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'`;
$addreses = explode("\n", $output);
$output = trim($output, " \n");
return array_combine($interfaces, $addreses);
}
$output = trim($output, " \n");
$interfaces = explode("\n", $output);
$addreses = array();
foreach ($interfaces as $interface) {
$output = shell_exec("ip -oneline -family inet addr show $interface | awk '{print $4}' | cut -d'/' -f1");
$addreses[] = $output;
}
return array_combine($interfaces, $addreses);
}
}
所以小部件的标题将是以太网。对于 get_metric,我们将尝试获取所有以太网接口名称,然后获取每个接口的 IP 地址,并将设备名称和 IP 地址结合起来返回。
我们需要处理两种情况:如果服务器使用 ifconfig 或服务器使用 ip 实用程序。较新的服务器很可能有 ip 而不是 ifconfig; 因此,我们应该首先运行 ip 来获取以太网设备。
$output = shell_exec("ip -oneline link show | awk '{print $2}' | sed 's/://'");
使用 IP 实用程序
带有 ip 命令和 -oneline 将仅在一行中显示输出,其中 link 和 show 将列出所有设备。我们使用 awk 获取第二列,其中包含设备名称;但是它包含 : 字符。我们使用 sed 将 : 替换为空字符串。
这是我们在命令行上运行它们时的结果:
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether 08:00:27:08:c2:e4 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether 08:00:27:eb:11:e4 brd ff:ff:ff:ff:ff:ff
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show | awk '{print $2}'
lo:
eth0:
eth1:
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show | awk '{print $2}' | sed 's/://'
lo
eth0
eth1
[vagrant@vagrant-centos64 sbin]$
如果 shell_exec 成功运行,我们将继续使用 IP 实用程序。上面的输出被逐行分割成一个数组。
$output = trim($output, " \n");
$interfaces = explode("\n", $output);
然后我们循环遍历该数组,并再次使用 ip 命令 ip -oneline -family inet addr show device_name 来获取分配给设备的 IP 地址。
$addreses = array();
foreach ($interfaces as $interface) {
$output = shell_exec("ip -oneline -family inet addr show $interface | awk '{print $4}' | cut -d'/' -f1");
$addreses[] = $output;
}
IP 地址出现在第四列中,因此 awk 用于打印该值。然后我们使用 cut 将命令按 / 分割,并使用 swich -f1 获取第一个元素。
查看输出,看看当我们在命令行上运行它们时,它们是如何工作的:
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1
3: eth1 inet 192.168.1.111/24 brd 192.168.1.255 scope global eth1
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 | awk '{print $4}'
192.168.1.111/24
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 | awk '{print $4}' | cut -d'/' -f1
192.168.1.111
[vagrant@vagrant-centos64 sbin]$
当我们在接口数组中获取设备名称并在地址数组中获取 IP 地址时,我们创建一个关联数组,其中接口名称作为键,IP 地址作为值。
return array_combine($interfaces, $addreses);
使用 ifconfig 的服务器
在 ifconfig 的情况下,ip 的 shell_exec 将返回 false。在这种情况下,我们改为运行 ifconfig。
我们上面介绍的逻辑完全相同 - 它只是获取信息的不同实用程序。
if (!$output) { // It didn't work with "ip" , so we do it with ifconfig
$output = `ifconfig | grep "Link encap" | awk '{ print $1 }'`;
$interfaces = explode("\n", $output);
$output = `ifconfig | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'`;
$addreses = explode("\n", $output);
$output = trim($output, " \n");
return array_combine($interfaces, $addreses);
}
让我们在终端中运行上面的命令,以便我们了解发生了什么。
请注意,ifconfig 直接在输出中显示 IP 地址,因此我们可以直接获取它们,而不是在设备数组上运行循环并获取每个设备的 IP 地址。
[vagrant@vagrant-centos64 sbin]$ ifconfig
eth0 Link encap:Ethernet HWaddr 08:00:27:08:C2:E4
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe08:c2e4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4230 errors:0 dropped:0 overruns:0 frame:0
TX packets:2575 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:444488 (434.0 KiB) TX bytes:2288676 (2.1 MiB)
eth1 Link encap:Ethernet HWaddr 08:00:27:EB:11:E4
inet addr:192.168.1.111 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:feeb:11e4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4470 errors:0 dropped:0 overruns:0 frame:0
TX packets:2449 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1689803 (1.6 MiB) TX bytes:271675 (265.3 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:264 errors:0 dropped:0 overruns:0 frame:0
TX packets:264 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:15840 (15.4 KiB) TX bytes:15840 (15.4 KiB)
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "Link encap"
eth0 Link encap:Ethernet HWaddr 08:00:27:08:C2:E4
eth1 Link encap:Ethernet HWaddr 08:00:27:EB:11:E4
lo Link encap:Local Loopback
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "Link encap" | awk '{ print $1 }'
eth0
eth1
lo
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr"
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet addr:192.168.1.111 Bcast:192.168.1.255 Mask:255.255.255.0
inet addr:127.0.0.1 Mask:255.0.0.0
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr" | awk '{ print $2 }'
addr:10.0.2.15
addr:192.168.1.111
addr:127.0.0.1
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'
10.0.2.15
192.168.1.111
127.0.0.1
[vagrant@vagrant-centos64 sbin]$
一旦我们有了数据,将它们放入 get_content 中就很容易了,因为我们在这里只显示了一个简单的表格。
public function get_content() {
$interfaces = $this->get_metric();
$html = '<table class="wp-list-table widefat"><thead><tr>
<th>Interface</th>
<th>IP</th>
</tr></thead><tbody>';
foreach ( $interfaces as $interface => $ip ) {
$html .= "<tr>
<td>{$interface}</td>
<td>{$ip}</td>
</tr>";
}
$html .= '</tbody></table>';
echo $html;
}
以下是它向管理员显示的方式:

网络流量
网络流量或网络 IO 显示通过计算机网络传输包的状态。该信息是从 netstat 中提取的。
[vagrant@vagrant-centos64 sbin]$ netstat -i Kernel Interface table Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg eth0 1500 0 4828 0 0 0 2933 0 0 0 BMRU eth1 1500 0 4806 0 0 0 2679 0 0 0 BMRU lo 16436 0 276 0 0 0 276 0 0 0 LRU
让我们在文件 widget/networkio.php 中获取我们的基本类 Networkio
<?php
/**
* Adopt from https://github.com/afaqurk/linux-dash/blob/master/sh/ip.php
*
*/
namespace AX\StatBoard\Widget;
class Networkio implements Provider {
function __construct() {
}
public function get_title() {
return "Network IO";
}
function get_metric() { $ethernet = array(); $output = `netstat -i | grep -v -E '(Iface|Interface)' | awk '{print $1","$4","$8}'`; $lines = explode("\n", $output); foreach ($lines as $line) { $line = explode(',', $line); if (count($line)<3) { continue; } $ethernet[] = array($line[0], intval($line[1]), intval($line[2])); } return $ethernet; }}
我将在本文后面解释它们。现在,让我们尝试评估我们在上面代码中使用的命令。
我将尝试运行多个命令,以便在我们将结果传递到另一个管道后您可以理解其中的差异。
[vagrant@vagrant-centos64 sbin]$ netstat -i
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 0 5727 0 0 0 3400 0 0 0 BMRU
eth1 1500 0 5004 0 0 0 2797 0 0 0 BMRU
lo 16436 0 292 0 0 0 292 0 0 0 LRU
[vagrant@vagrant-centos64 sbin]$ netstat -i | grep -v -E '(Iface|Interface)'
eth0 1500 0 5736 0 0 0 3405 0 0 0 BMRU
eth1 1500 0 5004 0 0 0 2797 0 0 0 BMRU
lo 16436 0 292 0 0 0 292 0 0 0 LRU
[vagrant@vagrant-centos64 sbin]$ netstat -i | grep -v -E '(Iface|Interface)' | awk '{print $1","$4","$8}'
eth0,5760,3420
eth1,5004,2797
lo,292,292
[vagrant@vagrant-centos64 sbin]$
netstat 返回很多东西,我们使用 grep 来排除包含单词 Iface 或 Kernel 的行(前两行),我们只关心最后三行是我们的以太网设备及其包传输。 awk 用于打印出第一、四、八列的数据,分别表示接口名称、RX-OK、TX-OK。
在我们的get_metric中,我们将结果逐行拆分到一个数组中。因为每一行都包含用逗号分隔的数据,所以它们被再次分割成一个数组。
我们确保只接受具有三个元素的数组。我们还尝试将数字字符串转换为整数值。数据将依次输入 get_content。
public function get_content() {
$interfaces = $this->get_metric();
$data = array_merge(array(array('Interface', 'Receive(package)', 'Transfer(package)')), $interfaces);
$data = json_encode($data);
echo <<<EOD
<div id="nio_chart"></div>
<script type="text/javascript">
google.setOnLoadCallback(function () {
var data = google.visualization.arrayToDataTable({$data});
var options = {
};
var chart = new google.visualization.ColumnChart(document.getElementById('nio_chart'));
chart.draw(data, options);
})
</script>
EOD;
}
我们之前使用了条形图,并尝试将指标数据数组格式化为条形图数据格式,然后进行渲染。
数据数组的每一行代表一个组条形图,其中包含条形图名称及其相应的值。在我们的例子中,每一行都是接口名称以及 RX 栏和 TX 栏。
这是我们得到的:

输入/输出统计
现在,我们处理 io stat。 IO 表示输入/输出。我们将了解每秒执行多少次读/写操作。我们还处理 io_wait。 IO等待是CPU空闲等待从硬盘读取结果的时间。
比如你正在读取MySQL数据,CPU会空闲下来等待结果。 io wait 按1秒或1000毫秒计算。如果您的代码需要 100 毫秒从硬盘读取数据,则 io_wait 为 100/1000 = 10%。 IO 等待越少,系统性能越好。
为了继续执行此操作,请确保系统上有 sysstat 软件包。
- 对于 Arch Linux,使用 pacman -S sysstat 安装
- 对于 Debian/Ubuntu,您可以使用
apt-get install sysstat获取它们 - 对于 Fedora/Centos,您可以使用
yum install sysstat - 对于其他发行版,:请使用您的发行版包管理器进行安装
安装完成后,让我们评估一下我们将使用的一些命令。首先是第一件事:
[vagrant@vagrant-centos64 sbin]$ iostat
Linux 2.6.32-358.23.2.el6.x86_64 (vagrant-centos64.vagrantup.com) 04/27/2014 _x86_64_ (1 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
0.05 0.00 0.25 0.04 0.00 99.66
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 0.18 7.62 1.04 157826 21584
第四行包含IO状态数据。我们对第四列中的 iowait 值感兴趣。从第七行开始的数据包含硬盘驱动器每秒的读/写块。
如果您有许多硬盘连接到服务器,您将拥有多个设备:sdb、sdc 等。
数据是块的数量,而不是实际大小(以兆字节为单位)。我们必须找到块大小来计算总大小。
块大小存储在/sys/block/sda/queue/physical_block_size中。
[vagrant@vagrant-centos64 ~]$ cat /sys/block/sda/queue/physical_block_size 512 [vagrant@vagrant-centos64 ~]$
所以我的sda的块大小是512。我们将每秒读取的块数与块大小相乘,得到读/写数据的实际大小。
有了上面的基本知识,让我们在 widget/iostat.php 中创建我们的类 Iostat 。
<?php
namespace AX\StatBoard\Widget;
class Iostat implements Provider {
function __construct() {
}
public function get_title() {
return "Disk IO";
}
/**
* Make sure we install package sysstat
* yum install sysstat
* or apt-get install sysstat
*
* Return IO Stat information. CPU waiting time, disk read/write
*
*/
function get_metric() {
$metric = array();
$output = `iostat`;
$number_of_core = intval(`/bin/grep -c processor /proc/cpuinfo`);
$lines = explode("\n", $output);
//We should have more than 4 lines
if (!is_array($lines) || sizeof($lines)<4) {
return false;
}
$avg_cpu = preg_split("/\s+/", $lines[3]);
$metric['cpu'] = array(
'user' => floatval($avg_cpu[0]) * $number_of_core,
'system' => floatval($avg_cpu[2]) * $number_of_core,
'io_wait' => floatval($avg_cpu[3]) * $number_of_core,
'other' => 100 - ($avg_cpu[0] + $avg_cpu[2] + $avg_cpu[3])
);
if (sizeof($lines) >=7) {
for ($i=6,$l = sizeof($lines);$i<$l; $i++) {
$line = preg_split("/\s+/", $lines[$i]);
if (!is_array($line) || sizeof($line)<5) {
continue;
}
// Calculate block size
$block_size = shell_exec("cat /sys/block/{$lines[1]}/queue/physical_block_size");
$metric['disk'][$line[0]] = array(
'read' => floatval($line[2]) * $block_size / 1024,
'write' => floatval($line[3]) * $block_size / 1024,
);
}
}
return $metric;
}
}
我们只是尝试将我们的理论应用到 PHP 代码中。获取 iostat 的输出,将其转换为数组,每一行都是一个元素。
第四行被空格分割并映射到关联数组中。第七行之后的所有行都放入另一个关联数组中,其中键是设备名称(sda,sdb,sdc,..),如果以兆字节为单位读取和写入,则值是一个数组。
一旦我们获得了指标,将其输入 get_content 并渲染我们的图表。
public function get_content() {
$metric = $this->get_metric();
$disk_io = array(
array('Disk', 'Read(MB)', 'Write(MB)'),
);
foreach ($metric['disk'] as $disk=>$stat) {
$disk_io[] = array($disk, $stat['read'], $stat['write']);
}
$disk_io = json_encode($disk_io);
$cpu_io = json_encode(array(
array('CPU Time', 'Percent'),
array('IO Wait', $metric['cpu']['io_wait']),
));
echo <<<EOD
<div id="widget_disk_io"></div>
<div id="widget_cpu_io_wait"></div>
<script type="text/javascript">
google.load('visualization', '1', {packages:['gauge']});
google.setOnLoadCallback(function () {
var data = google.visualization.arrayToDataTable({$cpu_io});
var goptions = {
redFrom: 80, redTo: 100,
yellowFrom:50, yellowTo: 80,
minorTicks: 5
};
var chart = new google.visualization.Gauge(document.getElementById('widget_cpu_io_wait'));
chart.draw(data, goptions);
var data2 = google.visualization.arrayToDataTable({$disk_io});
var chart2 = new google.visualization.ColumnChart(document.getElementById('widget_disk_io'));
chart2.draw(data2, {});
})
</script>
EOD;
}
对于磁盘 IO 读写,我们使用了条形图。对于 IO 等待,我们使用仪表图来使其看起来更漂亮。我们认为 80-100 的 IOwait 是一个严重事件,并用红色突出显示。

以上就是将Transforming a WordPress Server Dashboard into a Widget的详细内容,更多请关注知企PROSAAS其它相关文章!
<!--
-->

智能AI问答
PHP中文网智能助手能迅速回答你的编程问题,提供实时的代码和解决方案,帮助你解决各种难题。不仅如此,它还能提供编程资源和学习指导,帮助你快速提升编程技能。无论你是初学者还是专业人士,AI智能助手都能成为你的可靠助手,助力你在编程领域取得更大的成就。
我要提问
<!-- -->
相关标签:
php JavaScript ruby mysql 架构 json html print 关联数组 sort foreach 命名空间 format cURL 字符串 循环 接口 堆 线程 对象 事件 linux ubuntu centos debian WordPress
来源:知企PROSAAS

收藏
![]()
点赞
上一篇:WooCommerce 初学者:掌握订单创建第 3 部分
下一篇:Envato WordPress 工具包:精明的 WordPress 开发人员必备的工具箱

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
-
快手的研究成果SAMP在EMNLP2023国际人工智能顶会上获得认可2023-12-15 18:35:51
-
iPhone 15 Pro在DXOMARK排行榜上稳居前置摄像头性能之巅2023-12-15 18:27:21
-
【IT之家开箱】轻薄有型还带点艺术感:华硕灵耀14 2024 AI超轻薄本图赏2023-12-15 18:09:30
-
关注人工智能引发的三大人类社会问题2023-12-15 18:03:51
-
轻启“芯”纪元 | 首台酷睿Ultra AI 超轻薄本华硕灵耀14 2024重磅发布2023-12-15 17:54:37
-
大众ID.2首次曝光:注重驾驶便捷性的纯电动车型内饰揭秘2023-12-15 17:43:49
-
揭示摩托罗拉新机“XT2363-4”在GeekBench跑分方面的重大发现2023-12-15 16:19:32
-
现代起亚发布创新记忆合金轮胎,助您冬季驾驶更加安全2023-12-15 15:21:30
-
三星Galaxy S24系列手机即将支持卫星通信功能2023-12-15 15:15:39
-
如何将AI应用到云管理和运营中2023-12-15 14:49:49
最新问题
从来源“https://example.com”访问即使我允许 https://example.com/ 也被阻止
我有一个使用React、Node.js和Socket.io制作的应用程序我将Node后端部署到heroku,前端部署到Netlify我知道CORS错误与服务器有关,但无论我添加什么...
P粉805922437来自于2023-12-14 16:39:45
0
2
201
Vue - 高级 Cropper(未捕获类型错误:this.$refs.cropper.getResult 不是函数)
我有一个VueAdvancedCropper的裁剪功能,如下所示:crop(){const{canvas}=this.$refs.cropper.getResult();if(ca...
P粉054616867来自于2023-12-14 15:01:09
0
2
197
传递给 Illuminate\Database\Grammar::parameterize() 的参数 1 必须是数组类型,给定字符串,在
foreach($request->perbaikan_laporanas$key=>$value){Perbaikan_laporan::create(["...
P粉366946380来自于2023-12-14 09:50:33
0
1
276
Laravel 紧凑()
我有一个有趣的问题,关于PHP中的紧凑和Laravel中的紧凑。以PHP中的紧凑示例为例:$banana="yellow";$apple="red&q...
P粉697408921来自于2023-12-13 23:50:03
0
1
193
Laravel Livewire 在传输到运行 nginx 的实时服务器后无法工作
当我将项目传输到生产服务器时遇到问题,livewire停止工作。服务器正在运行nginx。当我在组件内触发wire:click事件(单击按钮)时,我在响应中收到错误:Symfony...
P粉043470158来自于2023-12-13 22:47:38
0
1
188
如何使用 Laravel 的服务提供程序进行外部 API 使用(利用基于用户的凭据)
所以我正在开发一个Laravel管理应用程序,它使用外部API,我们称之为PlatformAPI。平台的工作方式是,我的应用程序的用户在平台上有一个帐户。我的Laravel应用程序...
P粉465675962来自于2023-12-13 21:03:48
0
1
151
如何避免运行此命令 'sudo amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2'?
我正在关注本教程:https://aws.amazon.com/getting-started/hands-on/deploy-wordpress-with-amazon-rds/...
P粉938936304来自于2023-12-13 19:01:43
0
1
162
Google Analytics Data API v1:执行 runReport 并仅检索 JSON 数据
目前我正在尝试使用GoogleAnalyticsDataAPIv1从GA4获取json数据。但是,返回的响应不是纯json数据,相反,如果我只是使用PHP打印它,它会给我{}。但是...
P粉356361722来自于2023-12-13 17:37:47
0
1
158
Chrome浏览器中的Laravel中的Dump and Die函数返回了无法加载响应数据的错误:没有找到给定标识符的资源
{dd($request->all());die;}为什么dd不返回结果,只显示chrome浏览器中无法加载响应数据:没有带有给定标识符的资源?
P粉752826008来自于2023-12-13 16:33:09
0
1
253
如果产品未设置,则在保存时添加属性:WooCommerce
保存产品时,我想检查该产品是否具有特定属性。就我而言,pa_region。如果没有,我想将属性集和属性术语添加到产品中。如果属性pa_region已设置,我不想更新/更改它。我看到...
P粉555682718来自于2023-12-13 15:49:42
0
1
223
相关专题
更多>
-

php文件怎么打开 -

php怎么取出数组的前几个元素 -

php反序列化失败怎么办 -

php怎么连接mssql数据库 -

php连接mssql数据库的方法 -

html怎么上传 -

PHP出现乱码怎么解决 -

php怎么在浏览器运行
<!-- -->
热门推荐
-
wordpress收费么 -
WordPress 中functions.php 文件的 5 个实际应用 -
在 WordPress 中使用 Dashicons:综合指南 -
重置 WordPress 网站的指南 -
使用免费插件在WordPress中创建联系表单 -
从 WordPress 网站提取帖子的指南 -
重温 Squire:高效的 HTML5 富文本编辑器 -
创建 WordPress 登陆页面:分步指南 -
在 WordPress 中进行 A/B 测试的指南 -
修改WordPress中的特色图像大小
热门教程
更多>
相关推荐
热门推荐
最新课程
-

phpStudy极速入门视频教程
511219次学习
收藏 -

独孤九贱(1)_HTML5视频教程
590441次学习
收藏 -

独孤九贱(5)_ThinkPHP5视频教程
1175672次学习
收藏
-

最新ThinkPHP 5.1全球首发视频教程(60天成就PHP大牛线上培训班课)
1354766次学习
收藏 -

独孤九贱(4)_PHP视频教程
1175383次学习
收藏 -

PHP实战天龙八部之仿爱奇艺电影网站
729404次学习
收藏 -

PHP入门视频教程之一周学会PHP
1420652次学习
收藏 -

ThinkPHP5实战之[教学管理系统]
690956次学习
收藏
-

swoole进程树解析
36次学习
收藏 -

laravel和swoole连接池操作数据库的细节分析
350次学习
收藏 -

微信小程序开发--组件篇--十天技能课堂
2185次学习
收藏 -

ThinkPHP6.x 微实战--十天技能课堂
1755次学习
收藏 -

Vue.js 微实战--十天技能课堂
996次学习
收藏
$('.wzrthreeTab>div').click(function(e){
$('.wzrthreeTab>div').removeClass('check')
$(this).addClass('check')
$('.wzrthreelist>ul').css('display','none')
$('.'+e.currentTarget.dataset.id).show()
})
(window.slotbydup = window.slotbydup || []).push({
id: "u6938610",
container: "_u9zo4aprzs",
async: true
});
最新下载
更多>
$(document).ready(function(){
var sjyx_banSwiper = new Swiper(".sjyx_banSwiperwz",{
speed:1000,
autoplay:{
delay:3500,
disableOnInteraction: false,
},
pagination:{
el:'.sjyx_banSwiperwz .swiper-pagination',
clickable :false,
},
loop:true
})
})
网站特效
网站源码
网站素材
前端模板
-
[焦点幻灯] js左侧分类菜单图片轮播代码 -
[焦点幻灯] jQuery百叶窗风格图片切换代码 -
[焦点幻灯] jQuery仿京东首页广告图片切换 -
[焦点幻灯] jQuery无缝轮播插件 -
[焦点幻灯] jQuery自适应三屏滚动代码 -
[焦点幻灯] jQuery仿今日头条图片轮播代码 -
[焦点幻灯] jQuery爆炸切换效果焦点图 -
[焦点幻灯] jQuery仿魅族大图轮播幻灯片特效
-
[Bootstrap模板] 有机果蔬供应商网页模板 Bootstrap5 -
[后端模板] Bootstrap3多功能数据信息后台管理响应式网页模板-Novus -
[Bootstrap模板] 房产资源服务平台网页模板 Bootstrap5 -
[Bootstrap模板] 简约简历资料网页模板 Bootstrap4 -
[Bootstrap模板] bootstrap响应式宽屏图书教育网站模板-DREAMLIFE -
[后端模板] MAC风格响应式蓝色企业CMS后台管理系统模版 -
[后端模板] 响应式渐变大气后台管理系统网站模板-usinessbox -
[Bootstrap模板] 响应式蔬菜水果商店网站模板-Organio
-
[网站素材] 九个不同动作和表情的柠檬矢量素材(EPS) -
[网站素材] 蓝色的世界地图矢量素材(EPS+PNG) -
[网站素材] 九个手绘黑白风格的山脉矢量素材(EPS+PNG) -
[网站素材] 八个不同种族和肤色的人物头像矢量素材(AI+EPS+PNG) -
[网站素材] 15个水墨笔刷集合矢量素材(EPS+PNG) -
[网站素材] 逼真的银色 MacBook Pro 电脑模型矢量素材(EPS+PNG) -
[网站素材] 可爱的 happy birthday to you 字母矢量素材(AI+EPS+PNG) -
[网站素材] 带着 VR 眼睛访问网络体验科技的人们矢量素材(EPS)
-
[前端模板] 现代主题咖啡店宣传网站模板 -
[前端模板] HTML5游艇租赁服务宣传网站模板 -
[前端模板] 虚拟货币交易服务公司网站模板 -
[前端模板] HTML5远程教育服务机构网站模板 -
[前端模板] 数字解决方案增长机构网站模板 -
[前端模板] IT业务服务公司网站模板 -
[前端模板] 桌椅办公家具公司HTML5模板 -
[前端模板] 响应式汽车维修保养服务网站模板
$('.wzrfourlTab>div').click(function(e){
$('.wzrfourlTab>div').removeClass('check')
$(this).addClass('check')
$('.wzrfourList>ul').css('display','none')
$('.'+e.currentTarget.dataset.id).show()
})
-
关于我们
免责申明
意见反馈
讲师合作
广告合作
技术文章 - 知企PROSAAS:公益在线php培训,帮助PHP学习者快速成长!
-
关注服务号
微信扫码
关注PHP中文网服务号
技术交流群
QQ扫码
加入技术交流群
app下载

扫描下载App

- PHP中文网订阅号
- 每天精选资源文章推送

- PHP中文网APP
- 随时随地碎片化学习

- PHP中文网抖音号
- 发现有趣的
Copyright 2014-2023 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?c0e685c8743351838d2a7db1c49abd56";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
layui.use(['element', 'carousel'], function () {var element = layui.element;$ = layui.jquery;var carousel = layui.carousel;carousel.render({elem: '#test1', width: '100%', height: '330px', arrow: 'always'});$.getScript('/static/js/jquery.lazyload.min.js', function () {$("img").lazyload({placeholder: "/static/images/load.jpg", effect: "fadeIn", threshold: 200, skip_invisible: false});});});
$(function() {
//直播倒计时
$(".liveitem").each(function(){
timer(this);
})
function timer(obj){
var intDiff = $(obj).data("countdown");
window.setInterval(function(){
var day=0,
hour=0,
minute=0,
second=0;//时间默认值
if(intDiff > 0){
day = Math.floor(intDiff / (60 * 60 * 24));
hour = Math.floor(intDiff / (60 * 60)) - (day * 24);
minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60);
second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);
}else{
$(obj).find(".phpZbktBg").remove();
return;
}
if (hour <= 9) hour = '0' + hour;
if (minute <= 9) minute = '0' + minute;
if (second <= 9) second = '0' + second;
$(obj).find('.day_show').html(day+"");
$(obj).find('.hour_show').html(''+hour+'');
$(obj).find('.minute_show').html(''+minute+'');
$(obj).find('.second_show').html(''+second+'');
intDiff--;
}, 1000);
}
});
article_status = 164;
var _hmt = _hmt || [];(function(){var hm = document.createElement("script");hm.src="//hm.baidu.com/hm.js?c0e685c8743351838d2a7db1c49abd56";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm, s);})();(function(){var bp = document.createElement('script');var curProtocol = window.location.protocol.split(':')[0];if(curProtocol === 'https'){bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';}else{bp.src = 'http://push.zhanzhang.baidu.com/push.js';};var s = document.getElementsByTagName("script")[0];s.parentNode.insertBefore(bp, s);})();
var topadshow = $.cookie('phpcndatatopadshows');if(!topadshow&&1==2){$('.topimages').show();var topobj = $('.topimages').find('.time');var topobj_day = $('.topimages .time').find('.day');var topobj_hours = $('.topimages .time').find('.hours');var topobj_minutes = $('.topimages .time').find('.minutes');var topobj_second = $('.topimages .time').find('.second');var topday = parseInt(topobj_day.html());var tophours = parseInt(topobj_hours.html());var topminutes = parseInt(topobj_minutes.html());var topsecond = parseInt(topobj_second.html());setInterval(function(){if(topsecond > 0){topsecond = topsecond-1;}else{if(topminutes > 0){topminutes = topminutes-1;topsecond = 59;}else{if(tophours > 0){tophours = tophours-1;topminutes = 59;topsecond = 59;}else{if(topday > 0){topday = topday -1;tophours = 23;topminutes = 59;topsecond = 59;}else{topobj.html("
活动已结束
");}}}}topobj_second.html(topsecond);topobj_minutes.html(topminutes);topobj_hours.html(tophours);topobj_day.html(topday);},1000);}$('.topimages .layui-icon-close').click(function(){$.cookie('phpcndatatopadshows',1,{expires:7});$('.topimages').hide();});
.content img{max-width:80%;}
.copy-button {
padding: 5px 10px;
background-color: #666;
border: none;
color: #FFF;
font-size: 12px;
cursor: pointer;
border-radius: 5px;
position: relative;
top: 33px;
right: 5px;
z-index: 99;
float: right;
}
.copy-button:hover {
background-color: #fc3930;
}
$(document).ready(function(){
$('#gongzhonghao').hover(function(){
$('#gzh').show();
},function(){
$('#gzh').hide();
})
})
layui.use(['jquery','layer'], function(){
$('.test-iframe-handle').click(function(){
layer.open({
type: 2,
area: ['1300px', '750px'],
content: 'https://www.php.cn/help/ask?q=%E5%B0%86Transforming+a+WordPress+Server+Dashboard+into+a+Widget',
fixed: true, // 不固定
//maxmin: true,
shadeClose: true,
title:"智能小助手",
btnAlign: 'c',
yes: function(index, layero){
// 获取 iframe 的窗口对象
var iframeWin = window[layero.find('iframe')[0]['name']];
var elemMark = iframeWin.$('#mark'); // 获得 iframe 中某个输入框元素
var value = elemMark.val();
if($.trim(value) === '') return elemMark.focus();
// 显示获得的值
layer.msg('获得 iframe 中的输入框标记值:'+ value);
}
});
})
var is_login = "0";
var show = 0;
var ceng = getCookie('ceng');
//文章下拉弹出登录
// if(is_login == 0 && !ceng)
// {
// window.onscroll = function(){
// var t = document.documentElement.scrollTop || document.body.scrollTop;
// var top_div = document.getElementById( "top_div" );
// if( t >= 2500 && show == 0) {
// show = 1
// setCookie('ceng',1,1);
// $(document).trigger("api.loginpopbox");
// }
// }
// }
//未登录复制显示登录按钮
if(is_login == 0)
{
$(".code").hover(function(){
$(this).find('.contentsignin').show();
},function(){
$(this).find('.contentsignin').hide();
});
//不给复制
$('.code').bind("cut copy paste",function(e) {
e.preventDefault();
});
$('.code .contentsignin').click(function(){
$(document).trigger("api.loginpopbox");
})
}else
{
// 获取所有的
元素
var preElements = document.querySelectorAll('pre');
// 遍历每个元素
preElements.forEach(function(preElement) {
// 创建复制按钮
var copyButton = document.createElement('button');
copyButton.className = 'copy-button';
copyButton.textContent = '复制';
// 添加点击事件处理程序
copyButton.addEventListener('click', function() {
// 获取当前按钮所属的元素中的文本内容
var textContent = preElement.textContent.trim();
// 创建一个临时 textarea 元素并设置其值为中的文本内容
var tempTextarea = document.createElement('textarea');
tempTextarea.value = textContent;
// 将临时 textarea 添加到文档中
document.body.appendChild(tempTextarea);
// 选中临时 textarea 中的文本内容并执行复制操作
tempTextarea.select();
document.execCommand('copy');
// 移除临时 textarea 元素
document.body.removeChild(tempTextarea);
// 更新按钮文本为 "已复制"
this.textContent = '已复制';
});
// 将按钮添加到元素前面
preElement.parentNode.insertBefore(copyButton, preElement);
});
}
})
function setCookie(name,value,iDay){ //name相当于键,value相当于值,iDay为要设置的过期时间(天)
var oDate = new Date();
oDate.setDate(oDate.getDate() + iDay);
document.cookie = name + '=' + value + ';path=/;domain=.php.cn;expires=' + oDate;
}
function getCookie(name) {
var cookieArr = document.cookie.split(";");
for(var i = 0; i < cookieArr.length; i++) {
var cookiePair = cookieArr[i].split("=");
if(name == cookiePair[0].trim()) {
return decodeURIComponent(cookiePair[1]);
}
}
return null;
}
window.onload = function() {
// 在这里引用需要延迟加载的 JavaScript 文件
var script = document.createElement('script');
script.src = 'https://cdn.wwads.cn/js/makemoney.js';
document.body.appendChild(script);
};
function aiask(ask){
layer.open({
type: 2,
area: ['1300px', '750px'],
content: 'https://www.php.cn/help/ask?q='+encodeURIComponent(ask),
fixed: true, // 不固定
//maxmin: true,
shadeClose: true,
title:"智能小助手",
btnAlign: 'c',
yes: function(index, layero){
// 获取 iframe 的窗口对象
var iframeWin = window[layero.find('iframe')[0]['name']];
var elemMark = iframeWin.$('#mark'); // 获得 iframe 中某个输入框元素
var value = elemMark.val();
if($.trim(value) === '') return elemMark.focus();
// 显示获得的值
layer.msg('获得 iframe 中的输入框标记值:'+ value);
}
});
}登录PHP中文网,和优秀的人一起学习!
全站2000+教程免费学
微信扫码登录
.layui-fixbar{display: none;}
精品班
技术支持
技术咨询

学习群
会员优惠
返回顶部
文章标题:将Transforming a WordPress Server Dashboard into a Widget
文章链接:https://www.prosaas.cn/15278.html
更新时间:2023年09月03日
声明: 本站大部分内容均收集于网络!若内容若侵犯到您的权益,请发送邮件至:973664285@qq.com我们将第一时间处理! 资源所需价格并非资源售卖价格,是收集、整理、编辑详情以及本站运营的适当补贴,并且本站不提供任何免费技术支持。 所有资源仅限于参考和学习,版权归原作者所有,更多请阅读知企PROSAAS协议











