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饼图必须同时加载 piepie3d 模块,否则报错找不到父类
  • 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 的文本渲染管线(StrokeTextLanguageConv_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-fallbackfonts-wqy-microhei
  4. 配置 CHINESE_TTF_FONT 指向可用的中文字体文件
  5. 全局统一使用 FF_CHINESE 设置中文字体

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

最近浏览
累计浏览次数:1
评论
点击登录
phpreturn,PHP武器库,专注PHP领域的项目和资讯,收录和介绍PHP相关项目。

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

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

鲁ICP备19027671号-2