我来详细介绍PHP项目中使用TheGraph进行索引的相关内容。

TheGraph 与 PHP 集成
TheGraph 简介
TheGraph 是一个去中心化的索引协议,用于查询区块链数据,它允许开发者创建和发布开放的API(称为subgraphs),使得区块链数据更容易被查询。
PHP 与 TheGraph 的连接方式
使用 GraphQL 客户端
<?php
// 使用 composer 安装: composer require webonyx/graphql-php
use GraphQL\Client;
use GraphQL\Query;
class TheGraphClient {
private $client;
private $endpoint;
public function __construct($endpoint = null) {
// TheGraph 提供的 GraphQL 端点
$this->endpoint = $endpoint ?? 'https://api.thegraph.com/subgraphs/name/your-subgraph';
$this->client = new Client($this->endpoint);
}
// 查询通用方法
public function query($query, $variables = []) {
try {
$result = $this->client->runRawQuery($query, true, $variables);
return $result->getData();
} catch (Exception $e) {
throw new Exception("TheGraph查询失败: " . $e->getMessage());
}
}
}
常见的区块链查询场景
以太坊交易查询
<?php
class EthereumQueryService {
private $theGraphClient;
public function __construct(TheGraphClient $client) {
$this->theGraphClient = $client;
}
// 查询以太坊交易
public function getTransactions($address, $limit = 10) {
$query = '
{
transactions(
first: ' . $limit . ',
where: { from: "' . $address . '" }
) {
id
blockNumber
timestamp
value
gasPrice
to {
id
}
}
}';
return $this->theGraphClient->query($query);
}
// 查询ERC20代币余额
public function getTokenBalances($tokenAddress, $holderAddress) {
$query = '
{
token(id: "' . $tokenAddress . '") {
name
symbol
decimals
holders(
where: { id: "' . $holderAddress . '" }
) {
balance
}
}
}';
return $this->theGraphClient->query($query);
}
}
完整的 PHP TheGraph 封装类
<?php
require_once 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class TheGraphService {
private $httpClient;
private $subgraphUrl;
private $retryCount = 3;
private $timeout = 30;
public function __construct($subgraphName, $network = 'mainnet') {
$this->subgraphUrl = "https://api.thegraph.com/subgraphs/name/{$subgraphName}";
$this->httpClient = new Client([
'timeout' => $this->timeout,
'headers' => [
'Content-Type' => 'application/json',
]
]);
}
// 执行GraphQL查询
public function executeQuery($query, $variables = []) {
for ($i = 0; $i < $this->retryCount; $i++) {
try {
$response = $this->httpClient->post($this->subgraphUrl, [
'json' => [
'query' => $query,
'variables' => $variables
]
]);
$result = json_decode($response->getBody(), true);
if (isset($result['errors'])) {
throw new Exception('GraphQL Error: ' . json_encode($result['errors']));
}
return $result['data'];
} catch (RequestException $e) {
if ($i === $this->retryCount - 1) {
throw new Exception("请求失败: " . $e->getMessage());
}
sleep(pow(2, $i)); // 指数退避
}
}
}
// 分页查询
public function queryWithPagination($baseQuery, $pageSize = 100) {
$allResults = [];
$skip = 0;
$hasMore = true;
while ($hasMore) {
$paginationQuery = str_replace(
'{PAGINATION}',
"first: {$pageSize}, skip: {$skip}",
$baseQuery
);
$result = $this->executeQuery($paginationQuery);
// 假设结果在某个字段中
$items = $result['items'] ?? [];
$allResults = array_merge($allResults, $items);
$hasMore = count($items) === $pageSize;
$skip += $pageSize;
}
return $allResults;
}
}
实际应用示例
<?php
// 示例:查询Uniswap交易对数据
class UniswapQuery {
private $theGraph;
public function __construct() {
$this->theGraph = new TheGraphService('uniswap/uniswap-v2');
}
// 获取交易对信息
public function getPairInfo($pairAddress) {
$query = '
query GetPairInfo($pairId: String!) {
pair(id: $pairId) {
id
token0 {
id
symbol
name
decimals
}
token1 {
id
symbol
name
decimals
}
reserve0
reserve1
totalSupply
volumeUSD
txCount
}
}';
return $this->theGraph->executeQuery($query, [
'pairId' => $pairAddress
]);
}
// 获取历史交易数据
public function getSwapHistory($pairAddress, $limit = 50) {
$query = '
query GetSwaps($pairId: String!, $limit: Int!) {
swaps(
first: $limit,
orderBy: timestamp,
orderDirection: desc,
where: { pair: $pairId }
) {
id
timestamp
amount0In
amount0Out
amount1In
amount1Out
amountUSD
sender
}
}';
return $this->theGraph->executeQuery($query, [
'pairId' => $pairAddress,
'limit' => $limit
]);
}
}
性能优化建议
<?php
class CachedTheGraphService {
private $cache;
private $theGraph;
private $cacheTTL = 300; // 5分钟缓存
public function __construct($cacheInstance) {
$this->cache = $cacheInstance; // 可以是Redis或Memcached
$this->theGraph = new TheGraphService('your-subgraph');
}
// 带缓存的查询
public function query($query, $variables = []) {
$cacheKey = md5($query . json_encode($variables));
// 检查缓存
$cached = $this->cache->get($cacheKey);
if ($cached !== null) {
return $cached;
}
// 执行查询
$result = $this->theGraph->executeQuery($query, $variables);
// 存入缓存
$this->cache->set($cacheKey, $result, $this->cacheTTL);
return $result;
}
// 批量查询
public function batchQueries($queries) {
$results = [];
$batch = [];
foreach ($queries as $key => $query) {
$batch[] = [
'id' => $key,
'query' => $query['query'],
'variables' => $query['variables'] ?? []
];
}
// 发送批量请求
$responses = $this->sendBatchRequest($batch);
foreach ($responses as $response) {
$results[$response['id']] = $response['data'];
}
return $results;
}
}
错误处理与监控
<?php
class TheGraphMonitor {
private $logger;
private $metrics;
public function __construct($logger, $metrics) {
$this->logger = $logger;
$this->metrics = $metrics;
}
// 监控查询性能
public function monitoredQuery($query, $variables = []) {
$startTime = microtime(true);
try {
$result = $this->theGraph->executeQuery($query, $variables);
$duration = microtime(true) - $startTime;
// 记录性能指标
$this->metrics->histogram('thegraph_query_duration', $duration, [
'query_type' => $this->getQueryType($query)
]);
if ($duration > 1) {
$this->logger->warning('慢查询', [
'duration' => $duration,
'query' => substr($query, 0, 100)
]);
}
return $result;
} catch (Exception $e) {
$this->logger->error('TheGraph查询失败', [
'error' => $e->getMessage(),
'query' => substr($query, 0, 100)
]);
$this->metrics->increment('thegraph_query_errors');
throw $e;
}
}
}
使用建议
- 选择合适的Subgraph:确保使用官方或经过验证的Subgraph
- 优化查询:只请求需要的字段,避免过度查询
- 实现缓存:对频繁查询的数据实施缓存策略
- 错误处理:实现完整的错误处理和重试机制
- 监控性能:跟踪查询时间和错误率
- 考虑限制:注意TheGraph的速率限制和查询复杂度限制
这个PHP集成方案可以帮助你高效地利用TheGraph进行区块链数据索引和查询,根据具体需求,你可以进一步定制和优化这些实现。