MISC (system/collections/MISC.php) היא מחלקה סטטית של עזרים כלליים. היא גדלה לאורך השנים ואוספת דברים שאין ביניהם קשר: שער האבטחה של הבקשה, הצפנה, שליחת מייל, עבודה עם כתובות, פונקציות מחרוזת, זיהוי IP, ועורכי CKEditor. חלק מהמתודות בה פשוט שבורות או ישנות, והדף מסמן אותן במפורש כדי שלא תסתמכו עליהן.
system/ בדף הזה הוא תיקיית הליבה הפרוסה (api/core בריפו). החתימות מועתקות מהקוד, וכך גם ההתנהגות המתוארת: כל מתודה נקראה מול גוף הפונקציה.
רוב הקוד באתרים משתמש בקבוצה קטנה: MISC::email, MISC::encode / MISC::decode, MISC::get_ip, MISC::ip_info, MISC::uuid, MISC::absolute_url, MISC::ping ו-MISC::breadcrumbs. את שאר המתודות עדיף לא להכניס לקוד חדש. פרק "עזרים ישנים או שבורים" בסוף הדף מרכז אותן.
מאפיינים ציבוריים#
| מאפיין | משמעות |
|---|---|
MISC::$GLOBALS | מערך משותף לבקשה אחת (array() בהתחלה). מחזיק בין היתר app_html, body_class, minify_files, minify_groups, arrIncDirs ו-breadcrumbs. רוב המפתחות נקבעים ונקראים על ידי הליבה, ואין רשימה סגורה שלהם |
MISC::$JSONVARS | מאגר ישן של משתנים לשליחה ל-JavaScript. אינו בשימוש בקוד החדש |
שערי אבטחה ושגיאות#
MISC::init_security()#
public static function init_security()
שער ההגנה הראשון של הבקשה: מסנן heuristic ש-core.php קורא לו בראש תהליך הטעינה. הוא מריץ את detect_sql_injection על הערכים והמפתחות של $_GET (הודעות SECURITY ERROR1 ו-SECURITY ERROR2), על המפתחות של $_POST (SECURITY ERROR4), ומעביר כל קובץ ב-$_FILES דרך is_file_secure (SECURITY ERROR5 ו-SECURITY ERROR6). בהתאמה הוא מסיים את הבקשה עם die והודעה.
| פרמטרים | אין |
| החזרה | אין. כשמזהה בקשה חשודה לא חוזרת בכלל |
הבדיקה היא heuristic. בדיקת הערכים של $_POST מושבתת (הקוד מסומן TEMP ומוער, ושגיאה 3 לא קיימת בפועל), ורק המפתחות נבדקים. בדיקת הקבצים כבויה גם היא (ראו is_file_secure). ההודעה נשלחת בסטטוס HTTP 200 ולא ב-403, כך שמנועי חיפוש וכלי ניטור רואים אותה כדף תקין. אל תסמכו עליה במקום DB::escape, פרמטרים מוגבלים ובדיקת הרשאות. ראו מודל האבטחה ו-CSRF, XSS והזרקת SQL.
MISC::detect_sql_injection()#
static function detect_sql_injection($val)
בודקת מחרוזת אחת מול רשימת מילות מפתח של SQL. מחזירה true כשנמצאו לפחות ארבע התאמות, או כשהמחרוזת מכילה select * או union all. אחרת false.
| פרמטר | משמעות |
|---|---|
$val | מחרוזת לבדיקה |
החזרה: bool. זה חלק מ-init_security() ואינו כלי הגנה עצמאי: ספירת מילים נותנת גם false positives (טקסט חופשי שמכיל מילים כמו select, from, where) וגם false negatives.
MISC::is_file_secure()#
static function is_file_secure($file)
נועדה לבדוק שקובץ שהועלה אינו מסוכן.
גוף הפונקציה מתחיל ב-return true; // Disable check, ולכן היא מחזירה true לכל קובץ. אל תסתמכו עליה כדי לאשר העלאות. בדקו סיומת, סוג MIME ותוכן בעצמכם.
MISC::cli_check()#
static function cli_check()
מכינה את הסביבה כשהסקריפט רץ משורת פקודה (cron, MISC::ping). היא קוראת את argv בצורת key=value: הארגומנט url=... הופך ל-$_SERVER['REQUEST_URI'], וכל ארגומנט אחר נכנס ל-$_GET. מגדירה HTTPS=on, מריצה CONFIG::init(), מסמנת ignore_domain_check ומשתמשת בדומיין של הפלטפורמה הראשונה.
| פרמטרים | אין |
| החזרה | אין |
אין צורך לקרוא לה ידנית: core.php קורא לה בתחילת הטעינה (ב-web היא לא עושה כלום). ראו Cron.
MISC::service_unavailable()#
public static function service_unavailable($reason = "", $retry_after = 120)
מסיימת את הבקשה בתשובת "נסו שוב מאוחר יותר" אמיתית: סטטוס 503 עם Retry-After, במקום דף שבור בסטטוס 200 או 404 מטעה. נקראת כשתלות קריטית (בעיקר מסד הנתונים) אינה זמינה.
| פרמטר | משמעות |
|---|---|
$reason | טקסט פנימי שנכתב ל-error_log (ב-CLI: ל-STDERR). אינו מוצג לגולש |
$retry_after | ערך הכותרת Retry-After בשניות. ברירת מחדל 120 |
החזרה: לא חוזרת, מסתיימת ב-exit.
מה היא עושה (ב-web):
- מנקה את כל ה-output buffers, כדי שלא יישלח דף חלקי.
- שולחת 503,
Retry-Afterוכותרות no-store. - מכבה את מטמון העמוד (
PAGE::$cache_this_page = false), כדי שהשגיאה לא תישמר. - כותבת ל-
error_logומדפיסה עמוד שגיאה בעברית עםnoindex.
ב-CLI היא כותבת ל-STDERR ויוצאת עם קוד 1. יש הגנה מפני כניסה חוזרת. בכוונה היא לא קוראת ל-ADMIN::is_admin(), כי זה דורש מסד נתונים, וזה בדיוק מה שנפל.
try {
$row = DB::get_row("SELECT 1 FROM CRM_modules LIMIT 1");
} catch (Exception $e) {
MISC::service_unavailable("db down: " . $e->getMessage(), 300);
}
ראו עמודי שגיאה.
MISC::debug()#
public static function debug($data)
מדפיסה var_dump בתוך <pre> ומסיימת את הבקשה.
| פרמטר | משמעות |
|---|---|
$data | כל ערך |
לגולש רגיל או למשתמש לא מחובר המתודה לא עושה כלום, בלי הודעה. למנהל היא עושה die ולכן עוצרת את כל הבקשה. לא להשאיר בקוד production.
הצפנה וקידוד#
ראו גם את הדף על הצפנה.
MISC::set_encrypt_secret_keys()#
static function set_encrypt_secret_keys($key, $iv)
קובעת את המפתח וה-IV שישמשו את encrypt_decrypt.
| פרמטר | משמעות |
|---|---|
$key | מחרוזת סוד. נעשה עליה sha256 לפני השימוש כמפתח AES-256 |
$iv | מחרוזת סוד נוספת. 16 התווים הראשונים של sha256 שלה הם ה-IV |
אם האתר לא קורא ל-set_encrypt_secret_keys() בעצמו, encrypt_decrypt, encode ו-decode משתמשות במפתח וב-IV ברירת מחדל שכתובים בקוד המקור, וזהים בכל האתרים שמריצים את אותה גרסת ליבה. מי שיודע את ברירת המחדל יכול לפענח כל ערך שהוצפן כך, ולזייף ערכים. קראו לפונקציה פעם אחת בעת האתחול (למשל ב-CONFIG_USER) עם סודות ייחודיים לאתר מהכספת, ואל תכתבו אותם בקוד.
MISC::encrypt_decrypt()#
static function encrypt_decrypt($action, $string)
הצפנת AES-256-CBC. המפתח הוא sha256 של סוד המפתח, וה-IV הוא 16 התווים הראשונים של sha256 של סוד ה-IV.
| פרמטר | משמעות |
|---|---|
$action | 'encrypt' להצפנה, 'decrypt' לפענוח |
$string | המחרוזת לעיבוד. בהצפנה: טקסט רגיל. בפענוח: הפלט של הצפנה קודמת |
החזרה: בהצפנה, מחרוזת base64. בפענוח, הטקסט המקורי. לא מאמתת שלמות (אין MAC), ולכן ciphertext שונה ידנית עשוי להתפענח לזבל או להיכשל.
MISC::encode()#
public static function encode($str)
מקודדת ערך PHP כלשהו למחרוזת בטוחה ל-URL: serialize, אחר כך encrypt_decrypt('encrypt'), אחר כך base64 עם החלפת +/= ב--_,. מחרוזת ריקה מחזירה מחרוזת ריקה.
| פרמטר | משמעות |
|---|---|
$str | ערך לקידוד. בפועל כל ערך שניתן ל-serialize, לא רק מחרוזת |
החזרה: string.
MISC::decode()#
public static function decode($str)
ההפך של encode: מחליפה בחזרה את התווים, מפענחת base64, מפענחת AES ואז קוראת ל-unserialize.
| פרמטר | משמעות |
|---|---|
$str | פלט של MISC::encode |
החזרה: הערך המקורי, או false/null כשהקלט פגום.
בדרך כלל התוצאה של encode נשלחת לגולש (בקישור, ב-cookie או בשדה נסתר) ומתקבלת חזרה. כל עוד המפתח הוא ברירת המחדל, גולש יכול לבנות מחרוזת שתפוענח ל-unserialize של אובייקט לבחירתו. גם עם מפתח סודי, העדיפו להצפין ערך פשוט (מזהה, מחרוזת JSON) ולא אובייקטים, ואמתו את התוצאה אחרי הפענוח.
$token = MISC::encode(["user" => 17, "exp" => time() + 3600]);
$data = MISC::decode($token);
if (!is_array($data) || ($data["exp"] ?? 0) < time()) {
// ערך פגום או שפג תוקפו
}
MISC::uuid()#
static function uuid($data = null)
מחזירה UUID מגרסה 4 (36 תווים). משמשת בליבה לשמות קבצים זמניים ולמזהי אלמנטים.
| פרמטר | משמעות |
|---|---|
$data | אופציונלי: בדיוק 16 בתים בינאריים. בלעדיו נוצרים 16 בתים אקראיים עם random_bytes |
החזרה: string בצורה xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx. אורך שונה מ-16 בתים נבדק רק ב-assert, שכבוי ברוב סביבות הייצור.
$name = MISC::uuid() . ".jpg";
מייל#
ראו גם את הדף על מייל.
MISC::email()#
public static function email($to,$from,$subj,$msg, $bcc = "")
שולחת מייל HTML באמצעות mail() של PHP. הנושא מקודד base64 (=?UTF-8?B?...?=), ושם השולח בפורמט Name <addr> מקודד בנפרד.
| פרמטר | משמעות |
|---|---|
$to | כתובת היעד |
$from | addr@example.com או Name <addr@example.com>. משמש גם ל-From וגם ל-Reply-To |
$subj | נושא (טקסט רגיל, לא מקודד) |
$msg | גוף ההודעה כ-HTML |
$bcc | אופציונלי: כתובת העתק מוסתר |
החזרה: bool מ-mail(). true אומר רק שה-MTA המקומי קיבל את ההודעה.
$from, $to ו-$bcc נכנסים לכותרות כמו שהם. שורות הניקוי של \r\n מוערות בקוד, ולכן ערך שמכיל \r\n יכול להוסיף כותרות (Bcc, Cc) או להחליף את גוף ההודעה. לעולם אל תעבירו לשדות האלה קלט של גולש (כתובת מטופס יצירת קשר, למשל) בלי לנקות \r ו-\n ולאמת עם filter_var($x, FILTER_VALIDATE_EMAIL). עדיף לשלוח דרך mail_tpl, שעובד עם תור (add_mail_to_queue).$from = filter_var($_POST["email"] ?? "", FILTER_VALIDATE_EMAIL);
if ($from) {
MISC::email("office@example.com", "Site <noreply@example.com>", "פנייה חדשה", "<p>מאת: " . MISC::special_chars($from) . "</p>");
}
בדוגמה הכתובת שהגולש הקליד היא רק בתוך הגוף ואחרי אימות, וה-From קבוע.
MISC::MakeEmailAttach()#
function MakeEmailAttach($to,$from,$subj,$msg, $attach)
שליחת מייל עם קבצים מצורפים (multipart).
| פרמטר | משמעות |
|---|---|
$to, $from, $subj, $msg | כמו ב-email() |
$attach | מערך נתיב_בדיסק => שם_קובץ_במייל |
החזרה: bool מ-mail().
הפונקציה אינה מוגדרת static, ולכן הקריאה MISC::MakeEmailAttach(...) נכשלת ב-PHP 8 (קריאה סטטית למתודה לא סטטית). בכותרת From: שלה יש ; בסוף, הנושא אינו מקודד, והיא מושפעת מאותה בעיית הזרקת כותרות של email(). לא נמצאו קריאות אליה בליבה. השתמשו ב-mail_tpl או בספריית מייל חיצונית.
MISC::add_mail_to_queue()#
public static function add_mail_to_queue($mail, $priority = 1, $mail_tpl= "", $is_sent=false,$pid=false)
מוסיפה שורה לטבלת התור emails_queue (CRM_emails_queue), שממנה נשלחים מיילים ברקע. נקראת על ידי mail_tpl בליבה.
| פרמטר | משמעות |
|---|---|
$mail | מערך נתוני המייל. נשמר מסודר (serialize) |
$priority | עדיפות. ברירת מחדל 1 |
$mail_tpl | שם התבנית (sysName) לצורך תיעוד והצגה |
$is_sent | true כדי לרשום מייל שכבר נשלח: נקבעים is_sent=1, log=1 ו-send_date |
$pid | אופציונלי: מזהה תהליך לקישור לרשומה |
החזרה: אין (הערך של ->insert() אינו מוחזר).
המתודה רק מוסיפה שורה לתור. השליחה עצמה נעשית על ידי תהליך הרקע של התור. כדי לשלוח מייל בקוד אתר השתמשו ב-mail_tpl ולא בה ישירות.
MISC::is_valid_email()#
public static function is_valid_email($str)
בודקת כתובת מייל בביטוי רגולרי ^[\w\-\.]+\@[\w\-\.]+\.[\w\-]+$ (בלי רגישות לאותיות).
| פרמטר | משמעות |
|---|---|
$str | כתובת לבדיקה |
החזרה: bool. לא מקבלת + בחלק המקומי (a+b@x.com נדחית), ולא כתובות עם דומיין בעברית. לאימות מדויק יותר העדיפו filter_var($s, FILTER_VALIDATE_EMAIL).
כתובות ומחרוזת שאילתה#
MISC::absolute_url()#
static function absolute_url($url)
הופכת כתובת יחסית לאבסולוטית.
| פרמטר | משמעות |
|---|---|
$url | כתובת. אם היא עוברת FILTER_VALIDATE_URL היא מוחזרת כמו שהיא |
החזרה: string: אחרת מוצמדים לה הפרוטוקול (https כש-$_SERVER['HTTPS'] === 'on', אחרת http) ו-$_SERVER['HTTP_HOST']. אין בדיקה שהכתובת מתחילה ב-/, ולכן absolute_url("a/b") נותנת https://hosta/b (בלי לוכסן).
MISC::absolute_url("/he/contact"); // https://example.com/he/contact
הערך נלקח מהכותרת Host. בלי הגבלת דומיינים בשרת, כתובת כזו שמוצגת במייל (איפוס סיסמה, למשל) עלולה להצביע לדומיין זר. בקישורים רגישים העדיפו דומיין קבוע מההגדרות.
MISC::add_querystring_var()#
public static function add_querystring_var($url, $key, $val)
מוסיפה או מחליפה פרמטר בכתובת: קודם מסירה את הקיים (remove_querystring_var), אחר כך מצמידה ?key=val או &key=val.
| פרמטר | משמעות |
|---|---|
$url | כתובת, עם או בלי שאילתה |
$key | שם הפרמטר |
$val | ערך. אינו מקודד |
החזרה: string.
הערך אינו עובר urlencode, ולכן ערך עם &, רווח או # שובר את הכתובת. הבדיקה strpos($url,"?") != false נכשלת כשה-? נמצא בתו הראשון (מיקום 0), אז כתובת שמתחילה ב-? מקבלת ? נוסף במקום &. קודדו בעצמכם: MISC::add_querystring_var($u, "q", rawurlencode($q)).
MISC::add_querystring_var("/shop?page=2", "sort", "price"); // /shop?page=2&sort=price
MISC::remove_querystring_var()#
public static function remove_querystring_var($url, $key)
מסירה פרמטר מכתובת ומרכיבה אותה מחדש.
| פרמטר | משמעות |
|---|---|
$url | כתובת |
$key | שם הפרמטר להסרה |
החזרה: string. כתובת בלי שאילתה מוחזרת כמו שהיא.
השאילתה מפורקת עם parse_str ומורכבת מחדש ללא urlencode. פרמטר מערך (a[]=1) הופך למחרוזת Array, וערכים עם תווים מיוחדים משתנים. אל תשתמשו בה על כתובות עם פרמטרים מערכיים.
MISC::bgl_url_encode()#
static function bgl_url_encode($url)
מקודדת כל קטע של נתיב בנפרד עם urlencode ומשאירה את הלוכסנים.
| פרמטר | משמעות |
|---|---|
$url | נתיב מופרד בלוכסנים |
החזרה: string. שימושית לנתיבי קבצים עם עברית. שימו לב ש-urlencode הופך רווח ל-+ ולא ל-%20, מה שנכון לשאילתה ולא תמיד לנתיב.
MISC::bgl_url_encode("uploads/תמונה 1.jpg"); // uploads/%D7%AA%D7%9E%D7%95%D7%A0%D7%94+1.jpg
MISC::url_exists()#
static function url_exists($url)
שולחת בקשת HEAD (cURL, עם מעקב אחרי הפניות ו-CONNECTTIMEOUT של 5 שניות) ובודקת שהכתובת קיימת.
| פרמטר | משמעות |
|---|---|
$url | כתובת מלאה |
החזרה: bool: true כשקוד הסטטוס הסופי הוא 200, 301 או 302. כיוון שעוקבים אחרי הפניות, בפועל מתקבל כמעט תמיד הקוד של היעד הסופי. אין CURLOPT_TIMEOUT כולל, ולכן שרת שמגיב לאט עלול לתקוע את הבקשה.
הפונקציה מתחברת לכל כתובת שהיא מקבלת, כולל http://127.0.0.1 ו-http://169.254.169.254. אל תעבירו לה כתובת שהגולש הקליד בלי לאמת מארח וסכמה.
MISC::file_get_contents()#
static function file_get_contents($url)
מורידה כתובת עם cURL (כותרות דפדפן, מעקב הפניות) ומחזירה את הגוף.
| פרמטר | משמעות |
|---|---|
$url | כתובת מלאה |
החזרה: string כשקוד התשובה 200, אחרת false (גם שגיאת רשת וגם 404 או 301 סופי נותנים false).
הפונקציה מכבה CURLOPT_SSL_VERIFYPEER ו-CURLOPT_SSL_VERIFYHOST, ולכן כל מי שנמצא בדרך יכול להחליף את התשובה בלי שהשרת ישים לב. אל תשתמשו בה להורדת קוד, הגדרות, מחירים או כל דבר שמשפיע על הרשאות או כסף. כמו url_exists, היא גם פתוחה ל-SSRF כשהכתובת מגיעה מגולש.
MISC::breadcrumbs()#
static function breadcrumbs($list)
קובעת את פירורי הלחם של עמוד ניהול: שולחת אותם בכותרת תגובה בשם breadcrumbs (JSON) ושומרת ב-MISC::$GLOBALS["breadcrumbs"].
| פרמטר | משמעות |
|---|---|
$list | מערך של פריטים, כל אחד ["name" => "...", "url" => "..."]. בקוד הליבה נפוץ פריט אחד עם name בלבד |
החזרה: אין.
MISC::breadcrumbs([["name" => "הזמנות"]]);
כיוון שזו כותרת HTTP, יש לקרוא לה לפני שנשלח output כלשהו.
MISC::fancy_title() ו-MISC::fancy_subtitle()#
public static function fancy_title($title)
public static function fancy_subtitle($title)
שולחות את הכותרת וכותרת המשנה של עמוד הניהול כשהן כותרות תגובה (fancy_title, fancy_subtitle), כשהערך עובר rawurlencode. ה-JavaScript של הניהול קורא אותן מהתגובה לבקשת ה-AJAX ומציג בראש המסך.
| פרמטר | משמעות |
|---|---|
$title | הטקסט |
החזרה: אין. כמו breadcrumbs, צריכות לבוא לפני כל output.
מחרוזות ומספרים#
MISC::special_chars()#
public static function special_chars($text)
מקודדת תווי HTML: htmlspecialchars ואז החלפת ' ב-'.
| פרמטר | משמעות |
|---|---|
$text | טקסט |
החזרה: string בטוחה להדפסה בתוך תוכן HTML או בתוך ערך מאפיין עם גרשיים כפולים או בודדים. זה לא מגן על הקשר JavaScript או URL.
echo '<input value="' . MISC::special_chars($row["name"]) . '">';
MISC::clear_quotes()#
public static function clear_quotes($str)
מוחקת את כל הגרשיים (" ו-') מהמחרוזת.
| פרמטר | משמעות |
|---|---|
$str | טקסט |
החזרה: string. זה לא ניקוי אבטחה: היא פשוט מוחקת, ואינה מטפלת ב-<, ב-\ או בתווי בקרה.
MISC::max_chars() ו-MISC::MaxChars()#
public static function max_chars($str, $max)
public static function MaxChars($str, $max)
חותכת טקסט ארוך ל-$max תווים ומוסיפה .... MaxChars היא כינוי שקורא ל-max_chars.
| פרמטר | משמעות |
|---|---|
$str | טקסט |
$max | מספר תווים מרבי |
החזרה: string.
התנאי משתמש ב-strlen (בתים), והחיתוך ב-mb_substr (תווים). בעברית כל תו הוא שני בתים, ולכן טקסט בן 6 אותיות עם $max = 10 נחתך ומקבל ... לפני שהוא באמת עבר את המגבלה: כלומר מתקבלת מחרוזת שלמה עם שלוש נקודות מיותרות. אם חשוב לכם, כתבו mb_strlen($s) > $max ? mb_substr($s, 0, $max) . "..." : $s.
MISC::my_number_format()#
public static function my_number_format($num)
מעצבת מספר עם שתי ספרות אחרי הנקודה ופסיק לאלפים, ומורידה .00.
| פרמטר | משמעות |
|---|---|
$num | מספר או מחרוזת מספרית |
החזרה: string, או המספר 0 כשהקלט null (או כל ערך שהשוואה רופפת מול NULL נותנת עבורו true, כמו 0 ו-"").
MISC::my_number_format(1234.5); // "1,234.50"
MISC::my_number_format(1200); // "1,200"
MISC::is_pos()#
public static function is_pos($x)
החזרה: bool: (float)$x > 0. מחרוזת שאינה מספר נחשבת 0.
MISC::is_int()#
public static function is_int ($x)
החזרה: bool. למרות השם, הפונקציה מחזירה is_numeric($x), כלומר גם "1.5" ו-"1e3" נחשבים "int". לבדיקה אמיתית השתמשו ב-filter_var($x, FILTER_VALIDATE_INT) !== false.
MISC::value_in()#
public static function value_in($search_in, $search_for)
בודקת אם ערך נמצא ברשימה.
| פרמטר | משמעות |
|---|---|
$search_in | הערך לחיפוש (שם הפרמטר מבלבל: זה הערך, לא הרשימה) |
$search_for | מערך, או מחרוזת שמפוצלת לפי פסיקים |
החזרה: bool. ההשוואה היא == רופפת, ולכן MISC::value_in(0, "a,b") נותנת true ב-PHP מתחת ל-8 ו-false מ-8 ומעלה, והשוואות כמו "1" == "01" נותנות true.
MISC::value_in("b", "a,b,c"); // true
MISC::IsSerialized()#
public static function IsSerialized($str)
בודקת אם מחרוזת היא פלט של serialize, על ידי ניסיון unserialize בשקט.
| פרמטר | משמעות |
|---|---|
$str | מחרוזת |
החזרה: bool. מכיוון שהיא מריצה unserialize על הקלט, אל תקראו לה על קלט של גולש.
רשימות ומערכים#
MISC::array_reverse()#
public static function array_reverse($a)
הופכת סדר של מערך ושומרת את המפתחות (גם מספריים), בניגוד ל-array_reverse של PHP ללא preserve_keys.
| פרמטר | משמעות |
|---|---|
$a | מערך |
החזרה: מערך חדש.
MISC::array_unshift()#
public static function array_unshift(&$array, $key, $val)
מוסיפה איבר בתחילת מערך אסוציאטיבי עם מפתח לבחירה (ה-array_unshift של PHP מאפס מפתחות).
| פרמטר | משמעות |
|---|---|
&$array | המערך, מועבר by reference ומשתנה במקום |
$key | המפתח החדש |
$val | הערך החדש |
החזרה: המערך אחרי השינוי (גם הוא נכתב ל-$array). אם המפתח כבר קיים הוא יידרס במקומו ולא יעבור לתחילה.
$opts = ["b" => "Two"];
MISC::array_unshift($opts, "a", "One"); // ["a" => "One", "b" => "Two"]
MISC::bgl_implode()#
public static function bgl_implode($arr)
מאחדת מערך למחרוזת בפורמט |a||b||c|, שמאפשר חיפוש LIKE '%|b|%' בעמודה אחת בלי התאמה חלקית. ערכים ריקים (falsy, כולל 0 ו-"0") מסוננים.
| פרמטר | משמעות |
|---|---|
$arr | מערך. כל דבר אחר, או מערך ריק, מחזיר "" |
החזרה: string.
MISC::bgl_implode([3, 7, 12]); // "|3||7||12|"
MISC::bgl_explode()#
public static function bgl_explode($str)
ההפך של bgl_implode.
| פרמטר | משמעות |
|---|---|
$str | מחרוזת בפורמט ` |
החזרה: מערך (ריק עבור מחרוזת ריקה). המפתחות לא מאופסים אחרי array_filter, אז אם הוסר איבר יתכנו חורים במפתחות. הערכים חוזרים כמחרוזות.
MISC::bgl_explode("|3||7||12|"); // ["3", "7", "12"]
מודולים ותרגום#
MISC::get_modules_tbl()#
public static function get_modules_tbl()
מחזירה את כל שורות טבלת CRM_modules, מאונדקסות לפי moduleName, עם מטמון של 24*7 (לפי יחידות cache_engine, ראו רפרנס מטמון) לכל שפה (modules_lang + CONFIG::$lang).
| פרמטרים | אין |
| החזרה | מערך אסוציאטיבי moduleName => שורה |
הקוד שהיה מצרף את modules_content מוער בקובץ. אם הוספתם או שיניתם שורה בטבלה, נקו את המטמון או חכו לפקיעתו. ראו קונטרולרים.
רשת, IP ותהליכים#
MISC::get_ip()#
static function get_ip()
מחזירה את כתובת ה-IP של הגולש: קודם HTTP_CLIENT_IP, אחר כך HTTP_X_FORWARDED_FOR, ואחרת REMOTE_ADDR.
| פרמטרים | אין |
| החזרה | string, כפי שהגיעה, בלי אימות |
שתי הכותרות הראשונות נשלחות על ידי הלקוח, ולכן גולש יכול לקבוע כל כתובת שירצה ולעקוף הגבלות קצב, חסימות לפי IP ולוגים. X-Forwarded-For יכולה גם להכיל רשימה מופרדת בפסיקים. אם האתר אחרי Cloudflare או proxy מהימן, קראו את הכותרת של אותו proxy בלבד (HTTP_CF_CONNECTING_IP), ואחרת השתמשו ב-$_SERVER['REMOTE_ADDR']. אל תשתמשו בערך הזה להחלטות אבטחה.
MISC::ip_info()#
static function ip_info($ip = NULL, $purpose = "location", $deep_detect = TRUE)
מחזירה מיקום גיאוגרפי של כתובת IP.
| פרמטר | משמעות |
|---|---|
$ip | כתובת לבדיקה. ללא ערך (או ערך לא תקין): נלקחת כתובת הגולש |
$purpose | location (ברירת מחדל), address, city, state, region, country, countrycode. האות גדולה/קטנה, רווחים, -, _ והמילה name מתעלמים מהם |
$deep_detect | true: כשאין $ip נלקחת כתובת מ-HTTP_CF_CONNECTING_IP, אחר כך HTTP_X_FORWARDED_FOR, אחר כך HTTP_CLIENT_IP, ורק אחריהן REMOTE_ADDR |
החזרה:
$purpose | הערך |
|---|---|
location | מערך עם city, state, country, country_code, continent, continent_code |
address | מחרוזת עיר, אזור, מדינה |
city, state, region, country, countrycode | מחרוזת |
| ערך אחר, או כשל הבדיקה | NULL |
סדר המקורות: כותרת Cloudflare CF-IPCountry (מחזירה קוד מדינה בלבד, בלי עיר או אזור), ואחריה geoplugin.net, ואחריו ip-api.com, כשהמקורות החיצוניים נקראים ב-HTTP פשוט, עם זמן מרבי של 2 שניות לכל שירות ו-3 שניות בסך הכל. תוצאה מוצלחת נשמרת ב-session (ip_check) ונעשה בה שימוש חוזר עד שה-IP משתנה. כשל לא נשמר.
בלי Cloudflare, הבקשה הראשונה של כל גולש (שלא נשמר לו ערך ב-session) תלויה בשירות חיצוני וחוסמת עד 3 שניות. ה-IP של הגולש נשלח ב-HTTP גלוי. כשכל המקורות נכשלים מקבלים NULL, ולכן תמיד טפלו בו ($c = MISC::ip_info(null, "countrycode"); if (!$c) { ... }). אל תחסמו גולשים לפי תוצאה ריקה. ראו גם את אזהרת get_ip על זיוף כותרות.
$country = MISC::ip_info(null, "countrycode"); // "IL", או NULL כשלא ידוע
MISC::get_ip_data_with_fallback()#
static function get_ip_data_with_fallback($ip)
הפונקציה הפנימית של ip_info: בודקת את כותרת Cloudflare (CF-IPCountry), ואם אין, מנסה את geoplugin.net ואז את ip-api.com (שניהם ב-HTTP), עד לתשובה תקינה.
| פרמטר | משמעות |
|---|---|
$ip | כתובת IP תקינה |
החזרה: אובייקט בצורת geoplugin (geoplugin_city, geoplugin_regionName, geoplugin_countryName, geoplugin_countryCode, geoplugin_continentCode), או אובייקט ריק (object)[] כשכל המקורות נכשלו. קראו ל-ip_info במקום לקרוא לה ישירות.
MISC::validate_ip_data() ו-MISC::normalize_ip_data()#
static function validate_ip_data($data, $parser)
static function normalize_ip_data($data, $parser)
עזרים פנימיים של get_ip_data_with_fallback. $parser הוא אחד מ-geoplugin, ipapi, ipapi_com, ipinfo. validate_ip_data מחזירה bool האם בתשובה יש קוד מדינה של שני תווים. normalize_ip_data ממירה את התשובה של כל ספק לצורה של geoplugin. אין סיבה לקרוא להן מקוד אתר.
MISC::ping_url()#
public static function ping_url($url)
שולחת בקשת GET בלי גוף (CURLOPT_NOBODY) עם timeout של 2 שניות, בעיקר כדי "להעיר" כתובת בלי לחכות לתשובה מלאה.
| פרמטר | משמעות |
|---|---|
$url | כתובת מלאה |
החזרה: לא מוגדרת למשתמש: אל תסתמכו על ערך ההחזרה. הפונקציה עוקבת אחרי הפניות, ולכן גם היא פתוחה ל-SSRF כשהכתובת מגיעה מגולש. אחרי timeout הבקשה כבר נשלחה, אבל אין ודאות שהשרת הספיק לסיים אותה.
MISC::ping()#
public static function ping($url, $params = [])
מריצה כתובת פנימית של האתר ברקע, בתהליך PHP נפרד, בלי לחכות. כך מפעילים עבודה ארוכה (למשל system/ai_agent_runner/run מ-Form.php) בלי לתקוע את בקשת הגולש.
| פרמטר | משמעות |
|---|---|
$url | נתיב של האתר (בלי דומיין), למשל "system/cron/send_queue". מחרוזת לא ריקה |
$params | מערך key => value שמועבר כמו פרמטרי $_GET |
החזרה: true כשהפקודה הופעלה (לא אומר שהיא הצליחה), false כש-$url ריק או לא מחרוזת.
איך זה עובד: נבנית פקודה <php> index.php url='<url>' key='<value>' ..., שמורצת עם nohup ו-exec ברקע. הפלט נזרק, ושגיאות נכתבות ל-/tmp/ping_errors.log. בתהליך החדש MISC::cli_check() הופכת את url= ל-REQUEST_URI. את בינארי ה-PHP מוצאת detect_cli_php().
MISC::ping("system/ai_agent_runner/run", ["job" => $jobId]);
הפונקציה דורשת ש-exec יהיה פעיל (ב-disable_functions מושבת באחסון משותף רבים). ב-$params רק הערכים עוברים escapeshellarg, והמפתחות מוצמדים לפקודה כמו שהם: תנו רק מפתחות קבועים בקוד, לעולם לא מפתחות שמקורם בגולש. כל האתרים כותבים לאותו /tmp/ping_errors.log, והקובץ נדרס בכל קריאה. אין שום בדיקה שהתהליך הסתיים או הצליח, ולכן עבודה שחשוב לדעת על כשלה צריכה לרשום את עצמה (לוג או שורה בטבלה). ראו Cron.
MISC::detect_cli_php()#
public static function detect_cli_php(): string
מוצאת את קובץ ה-PHP שמתאים להרצה משורת פקודה. היא בודקת לפי הסדר /usr/local/bin/php, /usr/bin/php, ואחר כך נתיבי cPanel מ-ea-php85 עד ea-php74, ואז את PHP_BINARY (כשאינו lsphp או FPM). אם לא נמצא כלום מוחזר /usr/local/bin/php. התוצאה נשמרת בזיכרון לשאר הבקשה.
| פרמטרים | אין |
| החזרה | string, נתיב לקובץ ההפעלה. מחזירה נתיב גם כשהוא לא קיים (ברירת המחדל האחרונה) |
עורכי טקסט (CKEditor)#
שלוש הפונקציות הבאות כמעט זהות: כל אחת מחזירה HTML של textarea שהופך לעורך CKEditor 4, וקוראת ל-PAGE::add_asset("assets/ckeditor/ckeditor.js"). כל אחת מגדירה גם פונקציית JavaScript בשם check_<name>() שמעדכנת את ה-textarea לפני שליחה. הן נבדלות בסרגל הכלים.
| מתודה | סרגל הכלים |
|---|---|
create_editor | Full |
create_editor_mini | Basic |
create_editor_new | new_editor |
public static function create_editor($name, $text='', $height='450', $width='800')
public static function create_editor_mini($name, $text='', $height = "300", $width = "100%")
public static function create_editor_new($name, $text='', $height='450', $width='800')
| פרמטר | משמעות |
|---|---|
$name | שם השדה. משמש גם כ-name של ה-textarea וגם בשם פונקציית ה-JS ובבורר |
$text | התוכן ההתחלתי |
$height, $width | מידות העורך. ברירות המחדל שונות בין הפונקציות, כמו בחתימה |
החזרה: string של HTML ו-JavaScript להדפסה בטופס.
$name נכנס כמו שהוא לשם פונקציית JavaScript ולבורר, ולכן שם שמגיע מגולש מאפשר הזרקת קוד. השתמשו בשמות קבועים ([a-z0-9_]) בלבד. CKEditor 4 עצמו כבר אינו מתוחזק, וקיימת בו סיכון של XSS בתוכן שנשמר. נקו את ה-HTML שנשמר מהעורך לפני הצגתו לגולשים.echo MISC::create_editor_mini("description", $row["description"], "250", "100%");
עזרים ישנים או שבורים#
הפונקציות האלה נשארו בקוד, אך לא בטוח שהן עובדות. הטבלה מסכמת את מצבן.
| מתודה | מצב |
|---|---|
ae_detect_ie() | עובדת: true כש-User-Agent מכיל Trident או MSIE. מיותרת כיום |
ieversion() | Fatal: קוראת ל-ae_detect_ie() כפונקציה גלובלית ללא MISC::, ופונקציה גלובלית כזו לא קיימת, ולכן כל קריאה זורקת שגיאה על פונקציה לא מוגדרת |
heb2utf($s) | שבורה: משתמשת ב-$t לפני שהוא מוגדר (notice, ובמצבים מסוימים שגיאה). ממירה windows-1255 ל-UTF-8 |
IsHebrew($string) | לא אמינה: ה-pattern "[א-ת]" משתמש ב-[ ו-] כתוחמים ולא כמחלקת תווים, ובלי דגל u. אל תסתמכו על התוצאה. השתמשו ב-preg_match('/\p{Hebrew}/u', $s) |
MySubstr($string, $start, $length = null) | חיתוך מחרוזת עברית דרך windows-1255 ו-iconv. תווים שאינם עברית ואנגלית (אימוג'י, למשל) אובדים. העדיפו mb_substr |
LogFile($filename, $dump) | לא static (אותה בעיה כמו MakeEmailAttach), כותבת print_r($dump) לקובץ עם 'w' (דורסת) ו-die אם הקובץ לא נפתח. השתמשו ב-error_log |
sanitize_output($buffer) | מחזירה את הקלט כמו שהוא: השורה הראשונה היא return $buffer;, והקוד שאחריה לא מורץ |
encode/decode | ראו פרק ההצפנה: הפרמטר $key שמופיע בהערות הקוד אינו בשימוש |
MISC::ieversion()#
public static function ieversion()
קריאה אליה מפילה את הבקשה. אל תקראו לה. החליפו ב-MISC::ae_detect_ie() אם אתם באמת צריכים לזהות IE.
MISC::ae_detect_ie()#
public static function ae_detect_ie()
החזרה: bool: האם ה-User-Agent מכיל Trident או MSIE.
MISC::heb2utf()#
public static function heb2utf($s)
| פרמטר | משמעות |
|---|---|
$s | מחרוזת בקידוד windows-1255 |
החזרה: string ב-UTF-8, אבל $t אינו מאותחל. השתמשו ב-iconv("windows-1255", "UTF-8", $s).
MISC::IsHebrew()#
public static function IsHebrew($string)
| פרמטר | משמעות |
|---|---|
$string | מחרוזת |
החזרה: תוצאת preg_match (1, 0 או false), לא bool. ראו את הטבלה למעלה: לא אמינה.
MISC::MySubstr()#
public static function MySubstr($string, $start, $length = null)
| פרמטר | משמעות |
|---|---|
$string | מחרוזת UTF-8 |
$start | מיקום התחלה (בבתים של windows-1255, כלומר בתווים) |
$length | אורך. אם null או 0, עד סוף המחרוזת |
החזרה: string.
MISC::LogFile()#
function LogFile($filename, $dump)
החזרה: אין. כותבת את print_r($dump, true) ל-$filename ומוחקת את תוכנו הקודם. אינה static, ולכן הקריאה MISC::LogFile() נכשלת ב-PHP 8.
MISC::sanitize_output()#
public static function sanitize_output($buffer)
החזרה: $buffer ללא שינוי. אל תפעילו אותה כ-ob_start callback בציפייה לכיווץ HTML: לכך משמשת PAGE::minify_html (PAGE).
ראו גם#
- עזרים כלליים - הסבר מושגי על הקבוצה הנפוצה של MISC
- הצפנה - מפתחות,
encodeו-decode - מייל - שליחה, תבניות ותור
- מודל האבטחה - איפה
init_securityנמצא בתמונה כולה - הקשחה - מה לשנות באתר לפני העלאה
- CSRF, XSS והזרקת SQL - הגנות בקוד האתר
- עמודי שגיאה - 503, 404 ו-500
- Cron - משימות מתוזמנות ו-
MISC::ping - PAGE (רפרנס) ו-FILES (רפרנס)
- פרמטרים ושפות