JpGraph:用PHP生成专业级图表

2026-04-15 奥古斯宏 #JpGraph #图表 #数据可视化 #PHP
还在用前端 JS 库画图表?JpGraph 纯 PHP 生成折线图、柱状图、饼图、甘特图,图片直接嵌入 PDF 和邮件,不依赖浏览器

你的报表系统需要展示销售数据,但Excel导出的图表样式老旧。你的监控系统需要实时显示流量趋势,但不知道如何生成图表。JpGraph就是你要找的答案——一个功能强大、支持中文的PHP图表生成库,让你用代码生成媲美专业报表软件的精美图表。

JpGraph官网

JpGraph 是一个老牌的PHP图表库,从PHP 5时代一直维护到现在,最新版本支持 PHP 8.5。虽然官网设计看起来有些年头了(上图),但它的功能一点不比现代方案弱——而且它是纯PHP服务端渲染,不需要JavaScript,对SEO友好,也不依赖任何外部服务。


安装

推荐方式:Composer安装(mitoteam 维护版)

composer require mitoteam/jpgraph

为什么不用 jpgraph/jpgraph?这个包已被标记为 abandoned(已废弃),在 PHP 8.4 上直接崩溃。社区维护的 mitoteam/jpgraph(当前版本 10.5.4)基于官方 4.4.3 打了大量 PHP 8.x 兼容性补丁,是实际可用的选择。

官方下载地址:https://jpgraph.net/download/ (免费版 4.4.3,支持 PHP 5/7/8)

环境要求

# JpGraph 需要 GD 扩展(带 FreeType 和 JPEG 支持)
sudo apt-get install php8.4-gd fonts-droid-fallback

# 验证
php -m | grep gd
# gd ✅

快速开始

基础配置

<?php
require_once __DIR__ . '/vendor/autoload.php';
use mitoteam\jpgraph\MtJpGraph;

// 加载所需模块(按需加载)
MtJpGraph::load(['line']);

// 中文字体配置(重要!)
// FF_CHINESE 使用 iconv 做 UTF-8 转换,兼容性最好
DEFINE('CHINESE_TTF_FONT', '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf');

第一个折线图

<?php
require_once __DIR__ . '/vendor/autoload.php';
use mitoteam\jpgraph\MtJpGraph;
DEFINE('CHINESE_TTF_FONT', '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf');

MtJpGraph::load(['line']);

$data = [12, 8, 15, 6, 10, 20];
$months = ['1月', '2月', '3月', '4月', '5月', '6月'];

$graph = new Graph(400, 300);
$graph->SetScale('textlin');
$graph->xaxis->SetTickLabels($months);
$graph->xaxis->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->yaxis->SetTitle('销售额(万)', 'center');
$graph->yaxis->title->SetFont(FF_CHINESE, FS_NORMAL, 9);

$lineplot = new LinePlot($data);
$lineplot->SetColor('#FF6B6B');
$lineplot->SetWeight(3);

$graph->Add($lineplot);
$graph->title->Set('月度销售趋势');
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);
$graph->Stroke('sales_trend.png');

折线图示例

几个关键点:

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

  • 类名没有命名空间:是 new Graph() 不是 new \Amenadiel\JpGraph\Graph\Graph()(网上不少教程写的是不存在的命名空间)
  • 模块按需加载:MtJpGraph::load(['line']) 只加载需要的部分
  • 中文字体用 FF_CHINESE:不要用 FF_SIMSUN,它的 GB2312 编码转换表缺大量常用汉字

核心图表类型

折线图(Line Plot)— 展示趋势变化

多条折线对比

MtJpGraph::load(['line']);

$data1 = [12, 8, 15, 6, 10, 20];
$data2 = [5, 10, 8, 12, 15, 18];
$months = ['1月', '2月', '3月', '4月', '5月', '6月'];

$graph = new Graph(600, 400);
$graph->SetScale('textlin');
$graph->xaxis->SetTickLabels($months);
$graph->xaxis->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->yaxis->SetTitle('销售额(万)', 'center');
$graph->yaxis->title->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->img->SetMargin(60, 80, 40, 60); // 增加边距避免图例遮挡数据

$l1 = new LinePlot($data1);
$l1->SetColor('#FF6B6B');
$l1->SetLegend('产品A');

$l2 = new LinePlot($data2);
$l2->SetColor('#4ECDC4');
$l2->SetLegend('产品B');

$graph->Add($l1);
$graph->Add($l2);
$graph->legend->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->legend->Pos(0.02, 0.1, 'right', 'top'); // 图例放右上角
$graph->title->Set('产品销售对比');
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);
$graph->Stroke('comparison.png');

适用场景:销售趋势分析、流量监控、股票走势、温度变化

柱状图(Bar Plot)— 对比数据差异

分组柱状图

MtJpGraph::load(['bar']);

$data = [
    [12, 8, 15, 6, 10],  // Q1
    [15, 10, 18, 8, 12], // Q2
];
$cats = ['华东', '华南', '华北', '西南', '西北'];

$graph = new Graph(600, 400);
$graph->SetScale('textlin');
$graph->xaxis->SetTickLabels($cats);
$graph->xaxis->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->yaxis->SetTitle('销量(万)', 'center');
$graph->yaxis->title->SetFont(FF_CHINESE, FS_NORMAL, 9);

$barPlots = [];
foreach ($data as $values) {
    $barPlots[] = new BarPlot($values);
}

$groupBar = new GroupBarPlot($barPlots);
$graph->Add($groupBar);
$barPlots[0]->SetFillColor('#FF6B6B');
$barPlots[0]->SetLegend('Q1');
$barPlots[1]->SetFillColor('#4ECDC4');
$barPlots[1]->SetLegend('Q2');
$graph->legend->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->title->Set('各区域季度销售对比');
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);
$graph->Stroke('quarterly_sales.png');

适用场景:季度销售对比、部门业绩对比、产品销量排名

饼图(Pie Plot)— 展示占比分布

3D饼图

MtJpGraph::load(['pie', 'pie3d']); // 注意:3D饼图需要同时加载 pie 和 pie3d

$data = [30, 25, 20, 15, 10];
$labels = ['电子产品', '服装配饰', '食品饮料', '家居日用', '其他'];

$graph = new PieGraph(550, 450); // 加大画布给标签留空间
$graph->title->Set('产品线收入占比');
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);

$pie = new PiePlot3D($data);
$pie->SetLegends($labels);        // 图例显示名称
$pie->ExplodeSlice(0, 20);         // 突出第一块
$pie->SetSliceColors([           // 自定义配色
    '#FF6B6B', '#4ECDC4', '#45B7D1',
    '#FFA07A', '#98D8C8'
]);
$pie->value->SetFont(FF_FONT1, FS_BOLD, 10);     // 数值标签是纯数字,用内置字体即可
$pie->value->SetColor('#333');
$pie->SetCenter(0.4, 0.55);       // 饼图偏移,避免标签遮挡数值
$graph->legend->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->legend->Pos(0.02, 0.1, 'right', 'top');

$graph->Add($pie);
$graph->Stroke('market_share.png');

注意两个坑:

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

  • 3D饼图必须同时加载 pie 和 pie3d 模块,否则报错找不到父类
  • SetCenter() 调整饼图位置很重要,否则扇区标签和数值会互相遮挡

适用场景:市场份额、预算分配、用户来源分析

面积图(Area Plot)— 展示累积效果

面积图

MtJpGraph::load(['line']);

$data = [120, 80, 150, 60, 100, 200, 180, 220];
$weeks = ['W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'W7', 'W8'];

$graph = new Graph(600, 400);
$graph->SetScale('textlin');
$graph->xaxis->SetTickLabels($weeks);
$graph->xaxis->SetFont(FF_FONT2, FS_NORMAL, 9);    // X轴是英文标签(W1-W8),用内置字体
$graph->yaxis->SetTitle('访问量(千)', 'center');
$graph->yaxis->title->SetFont(FF_CHINESE, FS_NORMAL, 9);

// ⚠️ JpGraph 没有 AreaPlot 类!面积图用 LinePlot + Fill 实现
$area = new LinePlot($data);
$area->SetFillColor('#4ECDC4');
$area->SetFillGradient('#E8F5E9', '#4ECDC4');
$area->SetColor('#3BA99C');
$area->SetWeight(2);

$graph->Add($area);
$graph->title->Set('网站流量趋势');
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);
$graph->Stroke('area_chart.png');

这是一个常见的误区——很多教程写 new Plot\AreaPlot($data),但这个类根本不存在。正确做法是用 LinePlot 加上填充色和渐变。

适用场景:累计收入、网站流量、用户增长

雷达图(Radar Plot)— 多维数据对比

雷达图

MtJpGraph::load(['radar']);

$data = [90, 80, 85, 70, 95, 88];
$labels = ['技术能力', '沟通能力', '团队协作', '创新能力', '学习能力', '执行力'];

$graph = new RadarGraph(600, 600);
$graph->SetTitles($labels);           // ⚠️ 标签设在 graph 上,不是 RadarPlot 上
$graph->axis->SetFont(FF_CHINESE, FS_NORMAL, 10); // 轴标签字体
$graph->title->Set('员工能力评估');
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);

$p1 = new RadarPlot($data);          // 构造函数只接受 data,不接受 labels
$p1->SetLegend('员工A');
$p1->SetColor('#FF6B6B');
$p1->SetFillColor('#FF6B6B');
$graph->legend->SetFont(FF_CHINESE, FS_NORMAL, 10);
$graph->Add($p1);
$graph->Stroke('skill_radar.png');

雷达图有两个常见错误:

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

  • RadarPlot($data, $labels) — 第二个参数会被静默忽略,标签要用 $graph->SetTitles($labels)
  • 中文轴标签乱码 — 这是 JpGraph 的已知 bug(文本管线问题),需要额外处理(见下文"中文问题详解")

适用场景:员工绩效考核、产品功能对比、多维数据分析

为什么选择 JpGraph?

对比项 JpGraph Chart.js Google Charts ECharts
语言 纯PHP JavaScript JavaScript JavaScript
服务端渲染 支持 不支持 不支持 不支持
SEO友好 是 否 否 否
外部依赖 无 CDN Google服务器 无
国内访问 正常 正常 可能较慢 正常
中文支持 需要配置 原生支持 原生支持 原生支持
图表类型 10+种 8种 多种 丰富
交互性 无 强 中 很强

一句话总结:需要服务端生成静态图片时选 JpGraph,需要前端交互时选 Chart.js/ECharts。

适用场景

适合:

  • 数据报表系统(销售、财务、运营)
  • PDF报表导出(发票、证书、合同)
  • 邮件图表(营销邮件、周报)
  • 后台管理系统(数据统计)
  • 监控大屏(配合定时任务生成图片)

不适合:

  • 交互式前端图表 → 用 Chart.js / ECharts
  • 实时动态图表 → 用 WebSocket + ECharts
  • 移动端APP内嵌图表 → 用原生图表库

中文问题详解

JpGraph 的中文支持是一个老大难问题。经过实际测试,情况如下:

字体方案对比

方案 常量 编码转换 效果
FF_SIMSUN 30 GB2312 表 大量常用汉字缺失或乱码
FF_CHINESE 31 iconv UTF-8 标题/图例/坐标轴正常
FF_CHINESE + 雷达轴 31 iconv UTF-8 轴标签乱码(已知bug)

推荐配置

// 字体文件:系统自带的 Droid Sans Fallback(支持中文)
DEFINE('CHINESE_TTF_FONT', '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf');

// 所有中文文本统一使用 FF_CHINESE
$graph->title->SetFont(FF_CHINESE, FS_NORMAL, 14);
$graph->xaxis->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->yaxis->title->SetFont(FF_CHINESE, FS_NORMAL, 9);
$graph->legend->SetFont(FF_CHINESE, FS_NORMAL, 9);

字体选择规则(重要)

FF_CHINESE 在部分渲染上下文中不稳定,会显示为方块。按内容类型选字体:

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

内容类型 示例 推荐字体 原因
中文文本 标题、图例、轴标题 FF_CHINESE 需要 iconv UTF-8 转换
纯数字/百分号 饼图数值标签 30% FF_FONT1 内置字体,稳定
英文/数字混合 X轴刻度 W1, W2 FF_FONT2 内置字体,稳定
中文坐标轴标签 X/Y轴的中文刻度 FF_CHINESE 需要 TTF 渲染

核心原则:只有真正包含中文字符的地方才用 FF_CHINESE,纯英文/数字用内置字体更可靠。

雷达图中文的特殊处理

如果使用雷达图且轴标签出现乱码,可以通过后处理方式解决——先用英文占位生成图表,再用 GD 在精确位置覆盖中文:

// 1. 用英文占位生成
$dummy = ['A','B','C','D','E','F'];
$graph->SetTitles($dummy);
$graph->axis->SetFont(FF_FONT2, FS_NORMAL, 8); // 内置英文字体

ob_start();
$graph->Stroke();
$img_data = ob_get_clean();

// 2. 用 GD 在同一位置写中文
$im = imagecreatefromstring($img_data);
$font = '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf';
$dark = imagecolorallocate($im, 40, 40, 40);

// 从 JpGraph 内部提取的坐标(6个轴标签位置)
$coords = [
    ['x'=>300, 'y'=>84],  // 顶部
    ['x'=>113, 'y'=>192], // 左上
    ['x'=>113, 'y'=>408], // 左下
    ['x'=>300, 'y'=>516], // 底部
    ['x'=>487, 'y'=>408], // 右下
    ['x'=>487, 'y'=>192], // 右上
];

$labels = ['技术能力','沟通能力','团队协作','创新能力','学习能力','执行力'];
$white = imagecolorallocate($im, 255, 255, 255);

foreach ($coords as $i => $c) {
    imagefilledrectangle($im, $c['x']-2, $c['y']-16, $c['x']+60, $c['y']+4, $white);
    imagettftext($im, 11, 0, $c['x'], $c['y'], $dark, $font, $labels[$i]);
}

imagepng($im, 'radar_chinese.png');
imagedestroy($im);

这个问题的根因是 JpGraph 的文本渲染管线(StrokeText → LanguageConv → _StrokeTTF)在处理雷达轴标签时有 bug。官方示例全部使用英文标签,所以这个问题从未被修复。mitoteam 的 fork 也继承了同样的行为。

最佳实践

性能优化:缓存生成的图表

$cacheFile = "cache/chart_{$type}_{$date}.png";
if (file_exists($cacheFile) && filemtime($cacheFile) > strtotime('-1 hour')) {
    header('Content-Type: image/png');
    readfile($cacheFile);
    exit;
}
$graph->Stroke($cacheFile);

错误处理:优雅降级

try {
    $graph->Stroke();
} catch (Exception $e) {
    $errorImg = imagecreate(400, 200);
    imagecolorallocate($errorImg, 255, 255, 255);
    $red = imagecolorallocate($errorImg, 255, 0, 0);
    imagestring($errorImg, 3, 20, 80, "图表生成失败:" . $e->getMessage(), $red);
    header('Content-Type: image/png');
    imagepng($errorImg);
    imagedestroy($errorImg);
}

输出格式

// PNG(默认,推荐)
$graph->Stroke('chart.png');

// JPEG(适合照片类图表)
$graph->img->SetImgFormat('jpeg');
$graph->img->SetQuality(90);

// GIF(需要透明背景时)
$graph->img->SetImgFormat('gif');

总结

JpGraph 虽然是个老牌库,但在纯PHP图表生成领域仍然是最全面的选择之一。它支持 10+ 种图表类型,输出格式丰富,高度可定制。唯一需要注意的是中文配置要使用 FF_CHINESE + iconv 方案,避开有缺陷的 FF_SIMSUN/GB2312 路径。

部署清单:

  1. composer require mitoteam/jpgraph
  2. 确保 PHP 安装了 GD 扩展(php-gd)
  3. 安装中文字体包(fonts-droid-fallback 或 fonts-wqy-microhei)
  4. 配置 CHINESE_TTF_FONT 指向可用的中文字体文件
  5. 全局统一使用 FF_CHINESE 设置中文字体

本文所有代码均在 PHP 8.4 + mitoteam/jpgraph 10.5.4 环境下测试通过。

最近浏览
IP用户:57.141.*.*
1 天前 Generic Bot
IP用户:45.182.*.*
2 天前 Firefox Mobile Android 14
IP用户:170.247.*.*
2 天前 Firefox Mobile iOS iOS 17.4
IP用户:46.250.*.*
3 天前 Chrome Mac 10.15
IP用户:141.94.*.*
4 天前 Chrome Mac 10.15
IP用户:57.141.*.*
4 天前 Generic Bot
IP用户:120.225.*.*
4 天前 Chrome Mac 10.15
IP用户:123.131.*.*
4 天前 Chrome Mac 10.15
IP用户:14.116.*.*
6 天前 Chrome Windows 10
IP用户:36.57.*.*
6 天前 Chrome Windows 10
IP用户:57.141.*.*
7 天前 Generic Bot
IP用户:57.141.*.*
8 天前 Generic Bot
累计浏览次数:255
评论
点击登录
phpreturn,PHP武器库,专注PHP领域的项目和资讯,收录和介绍PHP相关项目。

本站所有权利归 phpreturn.com 所有

举报/反馈/投稿邮箱:phpreturn@ulthon.com

鲁ICP备19027671号-2