تابع preg_match_all() در PHP
تابع preg_match_all() یکی از ابزارهای قدرتمند PHP برای جستجو و استخراج چندگانه الگوها (regular expressions) در یک رشته است. این تابع بر پایهٔ موتور PCRE (Perl Compatible Regular Expressions) کار میکند و امکان گرفتن تمامی تطابقها، گروههای گروهبندیشده و اطلاعات مربوط به موقعیت آنها را فراهم میسازد.
امضای تابع و مقدار بازگشتی
امضای تابع به صورت زیر است:
int preg_match_all(string $pattern, string $subject, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]])تابع تعداد تطابقهای کامل (matches) را برمیگرداند یا در صورت خطا مقدار false را بازمیگرداند. آرگومان $matches به صورت مرجع پر میشود.
ساختار آرایهٔ خروجی و Flags مهم
چند فلگ پرکاربرد که ساختار خروجی را تعیین میکنند:
- PREG_PATTERN_ORDER (پیشفرض): آرایهای با ایندکسهای 0، 1، … که 0 شامل تطابقهای کامل و 1 شامل گروه اول برای تمام تطابقها است.
- PREG_SET_ORDER: هر عنصر آرایه یک تطابق کامل است و داخلش ایندکسهای 0، 1، … برای گروهها وجود دارند.
- PREG_OFFSET_CAPTURE: به جای رشتهٔ ساده، هر مقدار به صورت آرایهٔ [match, offset] برگردانده میشود که offset موقعیت شروع تطابق در $subject را نشان میدهد.
- PREG_UNMATCHED_AS_NULL (از PHP 7.2): گروههایی که تطابق نداشتهاند به جای رشتهٔ خالی null میشوند.
مثال پایه — استخراج ایمیلها
$text = "Contact: alice@example.com, bob@example.org.";
$pattern = '/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}/';
$result = preg_match_all($pattern, $text, $matches);
var_dump($result);
var_dump($matches);در این مثال تابع، تعداد ایمیلهای پیدا شده را در $result قرار میدهد و آرایهٔ $matches شامل تمام ایمیلها میشود. الگوی ساده بالا برای مثال است؛ در عمل میتوانید از الگوهای دقیقتر برای ایمیل استفاده کنید.
مثال با گروهبندی و PREG_SET_ORDER
$html = 'Page 1Page 2';
$pattern = '/([^<]+)/i';
preg_match_all($pattern, $html, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
echo "url: {$m[1]}, text: {$m[2]}n";
}این کد همهٔ تگهای <a> با href را پیدا میکند. با استفاده از PREG_SET_ORDER، هر تطابق به شکل یک رکورد مستقل در $matches آمده و میتوانیم به راحتی آدرس و متن لینک را استخراج کنیم.
مثال با PREG_OFFSET_CAPTURE
$subject = "one two one";
$pattern = '/one/';
preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE);
var_dump($matches);با استفاده از PREG_OFFSET_CAPTURE هر تطابق به صورت [‘one’, offset] برگردانده میشود که offset موقعیتی است که تطابق از آن آغاز شده. این حالت وقتی مفید است که لازم باشد جایگاه دقیق تطابقها را بدانیم.
نکات عملی و بهترین روشها
- اگر فقط میخواهید وجود الگو را بررسی کنید، از preg_match() استفاده کنید — سریعتر و سبکتر است.
- برای رشتههای یونیکد حتماً از فلگ u در الگو استفاده کنید (مثلاً ‘/pattern/u’) تا کلاسهای p{L} یا متغیرهای یونیکد صحیح کار کنند.
- از الگوهای خاص و محدود به جای .* گسترده استفاده کنید تا از Backtracking زیاد جلوگیری شود.
- برای جلوگیری از آسیبپذیری ReDoS از کوانتفایرهای تسخیرناپذیر (possessive quantifiers) یا گروههای اتمیک استفاده کنید: (?>…) و +/
- در هنگام کار با ورودیهای کاربر، الگوها را بررسی یا محدود کنید تا محتوای مخرب نتواند باعث مصرف زیاد منابع شود.
مثال جلوگیری از Catastrophic Backtracking
// آسیبپذیر:
$pattern_bad = '/^(.*a){10}/';
// امنتر با possessive quantifier یا گروه اتمیک:
$pattern_safe = '/^(?>.*)a{10}/'; // یا استفاده از .*+ (possessive) اگر قابلپشتیبانی باشدالگوی اول میتواند باعث backtracking شدید شود. الگوهای اتمیک یا possessive جلوی بازگشتهای متعدد را میگیرند و خطر حملات ReDoS را کاهش میدهند.
خطایابی و بررسی خطاها
در صورت بروز خطا، preg_match_all مقدار false برمیگرداند. برای تشخیص نوع خطا میتوانید از تابع preg_last_error() استفاده کنید که مقادیری مانند PREG_NO_ERROR، PREG_INTERNAL_ERROR، PREG_BACKTRACK_LIMIT_ERROR، PREG_RECURSION_LIMIT_ERROR، PREG_BAD_UTF8_ERROR و PREG_BAD_UTF8_OFFSET_ERROR را بازمیگرداند.
جدول خلاصهٔ فلگها
| فلگ | توضیح |
|---|---|
| PREG_PATTERN_ORDER | پیشفرض — آرایه بر اساس گروههای الگو مرتب میشود |
| PREG_SET_ORDER | هر عنصر آرایه یک تطابق کامل است |
| PREG_OFFSET_CAPTURE | بازگرداندن موقعیت شروع هر تطابق |
| PREG_UNMATCHED_AS_NULL | گروههای بدون تطابق به صورت null بازگردانده میشوند |
موارد کاربرد واقعی
- استخراج لینکها، ایمیلها یا شمارهها از متن
- آنالیز لاگها و گرفتن فیلدهای تکرارشونده
- پاکسازی و اعتبارسنجی ورودیها قبل از ذخیرهٔ پایگاهداده
- جایگزینیهای پیچیده در ترکیب با preg_replace_callback
خلاصهٔ عملی
preg_match_all() برای گرفتن همهٔ تطابقهای یک الگو بسیار مناسب است، اما باید با احتیاط و دانش نسبت به ساختار الگوها از آن استفاده شود تا هم کارایی حفظ شود و هم از مشکلاتی مانند ReDoS جلوگیری شود. با درک فلگها (مثل PREG_SET_ORDER و PREG_OFFSET_CAPTURE) و مدیریت خطاها میتوانید از این تابع در پروژههای واقعی به شکل ایمن و بهینه بهره ببرید.
آیا این مطلب برای شما مفید بود ؟



