PHP项目TheGraph与索引

wen PHP项目 10

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

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;
        }
    }
}

使用建议

  1. 选择合适的Subgraph:确保使用官方或经过验证的Subgraph
  2. 优化查询:只请求需要的字段,避免过度查询
  3. 实现缓存:对频繁查询的数据实施缓存策略
  4. 错误处理:实现完整的错误处理和重试机制
  5. 监控性能:跟踪查询时间和错误率
  6. 考虑限制:注意TheGraph的速率限制和查询复杂度限制

这个PHP集成方案可以帮助你高效地利用TheGraph进行区块链数据索引和查询,根据具体需求,你可以进一步定制和优化这些实现。

抱歉,评论功能暂时关闭!