<?php
/**
* 發送 POST 請求並返回回應主體
* @param string $url 請求 URL
* @param array $data POST 欄位陣列
* @param array $options 額外 cURL 選項
* @return string|false 回應主體或 false 失敗
*/
function httpPost(string $url, array $data = [], array $options = []): string|false {
$ch = curl_init($url);
$defaultOptions = [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data), // 自動編碼 application/x-www-form-urlencoded
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => 'MyApp/1.0 (+https://example.com)',
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded',
],
];
curl_setopt_array($ch, $defaultOptions + $options);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno !== 0) {
error_log("cURL 錯誤 [$errno]: $error (URL: $url)");
return false;
}
if ($httpCode < 200 || $httpCode >= 300) {
error_log("HTTP 錯誤 $httpCode (URL: $url)");
return false;
}
return $response;
}
// 使用範例
$response = httpPost('https://api.example.com/login', [
'username' => 'user123',
'password' => 'secret',
]);
if ($response !== false) {
$data = json_decode($response, true);
// 處理 $data...
}
?>
<?php
function httpPostJson(string $url, array $payload, array $options = []): string|false {
$ch = curl_init($url);
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
$defaultOptions = [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $json,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json',
'Content-Length: ' . strlen($json),
],
];
curl_setopt_array($ch, $defaultOptions + $options);
$response = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
if ($errno !== 0) return false;
return $response;
}
?>
<?php
/**
* 建立 PDO 連線 (單例模式)
*/
function getPdo(): PDO {
static $pdo = null;
if ($pdo instanceof PDO) return $pdo;
$dsn = 'mysql:host=127.0.0.1;dbname=myapp;charset=utf8mb4';
$user = 'db_user';
$pass = 'db_pass';
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 使用原生預處理
PDO::MYSQL_ATTR_SSL_CA => '/path/to/ca.pem', // 如需 SSL
]);
return $pdo;
}
/**
* 執行查詢並返回所有結果
*/
function fetchAll(string $sql, array $params = []): array {
$stmt = getPdo()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
/**
* 執行查詢並返回單行
*/
function fetchOne(string $sql, array $params = []): array|false {
$stmt = getPdo()->prepare($sql);
$stmt->execute($params);
return $stmt->fetch();
}
/**
* 執行寫入 (INSERT/UPDATE/DELETE) 返回影響行數
*/
function execute(string $sql, array $params = []): int {
$stmt = getPdo()->prepare($sql);
$stmt->execute($params);
return $stmt->rowCount();
}
// 使用範例
$users = fetchAll('SELECT id, name, email FROM users WHERE status = ? AND created_at > ?', [1, '2024-01-01']);
$user = fetchOne('SELECT * FROM users WHERE email = ?', ['user@example.com']);
$affected = execute('INSERT INTO logs (message, level) VALUES (?, ?)', ['User logged in', 'info']);
?>
<?php
/**
* 取得網頁原始碼
* @param string $url
* @param int $timeout 秒
* @return string|false
*/
function fetchPage(string $url, int $timeout = 30): string|false {
// 方法 A:file_get_contents (需 php.ini allow_url_fopen=On)
$ctx = stream_context_create([
'http' => [
'timeout' => $timeout,
'user_agent' => 'MyApp/1.0',
'follow_location' => 1,
'max_redirects' => 5,
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
$content = @file_get_contents($url, false, $ctx);
if ($content !== false) return $content;
// 方法 B:cURL 後備 (更穩健)
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_USERAGENT => 'MyApp/1.0',
]);
$content = curl_exec($ch);
curl_close($ch);
return $content !== false ? $content : false;
}
?>
安裝:`composer require sunra/php-simple-html-dom-parser` (支援 PHP 8+、PSR-4)
<?php
require 'vendor/autoload.php';
use Sunra\PhpSimple\HtmlDomParser;
$html = fetchPage('https://example.com/articles');
if ($html === false) die('抓取失敗');
$dom = HtmlDomParser::strGetHtml($html);
// 1. 選取單一元素 (CSS 選擇器)
$title = $dom->find('h1.title', 0)?->plaintext ?? '無標題';
// 2. 選取多個元素並遍歷
$articles = $dom->find('article.post');
foreach ($articles as $article) {
$link = $article->find('a', 0)?->href ?? '';
$summary = $article->find('.excerpt', 0)?->plaintext ?? '';
$date = $article->find('time', 0)?->datetime ?? '';
echo "[$date] $title: $summary\n";
}
// 3. 屬性操作
$img = $dom->find('img.cover', 0);
if ($img) {
$src = $img->src;
$alt = $img->alt;
$img->src = 'https://cdn.example.com/' . basename($src); // 修改屬性
}
// 4. 移除不需要的元素 (如廣告、腳本)
foreach ($dom->find('script, style, .ads, #footer') as $el) {
$el->outertext = '';
}
// 5. 輸出清理後的 HTML
$cleanHtml = $dom->save();
?>
<?php
function fetchMulti(array $urls, int $maxConcurrent = 10, int $timeout = 30): array {
$results = [];
$mh = curl_multi_init();
$handles = [];
// 初始化所有 handle
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_USERAGENT => 'MyApp/1.0',
CURLOPT_PRIVATE => $url, // 儲存 URL 供識別
]);
curl_multi_add_handle($mh, $ch);
$handles[(int)$ch] = $ch;
}
$running = null;
do {
// 執行並發
curl_multi_exec($mh, $running);
// 等待活動
curl_multi_select($mh, 0.1);
// 檢查完成的 handle
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$url = curl_getinfo($ch, CURLINFO_PRIVATE);
$errno = curl_errno($ch);
$response = $errno === 0 ? curl_multi_getcontent($ch) : false;
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$results[$url] = [
'success' => $errno === 0 && $httpCode >= 200 && $httpCode < 300,
'body' => $response,
'http_code' => $httpCode,
'error' => $errno ? curl_error($ch) : null,
];
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
} while ($running > 0);
curl_multi_cleanup($mh);
return $results;
}
// 使用:並發抓取 100 個 URL
$urls = array_map(fn($i) => "https://api.example.com/item/$i", range(1, 100));
$results = fetchMulti($urls, 20);
?>