שימוש במשתני סביבה (Environment Variables) הוא מנהג קבוע בשנים האחרונות בקרב מפתחים כחלק מתהליך של פיתוח מערכות ותהליכים.
מה הם משתני סביבה?
משתנה סביבה הוא משתנה אשר מוגדר ותפקידו בעיקר הוא השפעה על תהליך אחד או יותר אשר רץ על מכונה. לרוב שימוש במשתנה סביבה הוא כאשר רוצים לשמור מידע רגיש כגון מפתח API כלשהו או הגדרה מסויימת אשר אמורים להשפיע על אופן הריצה של תהליך מסויים.
משתני סביבה קיימים אפילו על המחשב שלכם 🤓, לדוגמה במערכת ההפעלה Windows יש משתנה אשר נקרא %temp%
אשר שומר את מיקום תיקיית ה-Temp של המערכת, כך תהליכים שונים יכולים לקבל את המיקום של התיקייה ע״י שימוש במשתנה הסביבה ולבצע פעולות כלשהן.
יתרונות של משתני סביבה
כיום כחלק מתהליך פיתוח של מערכת אשר אמורה לרוץ על שרת, אנו נרצה להגדיר קונפיגורציה מסויימת או אפילו פרמטרים רגישים ע״י שימוש במשתני סביבה.
אבטחה היא חלק מהותי בשימוש במשתני סביבה, לרוב כאשר אנחנו מפתחים מערכת כלשהי נרצה שהיא תתממשק מול שירות חיצוני או אפילו תשתמש במסד נתונים כלשהו. שמירה של הטוקן או הסיסמה בקוד שלנו הוא מנהג רע ומסוכן מאוד.
במידה ומישהו יוכל להשיג גישה לקוד שלנו (לדוגמה בחשבון ה-Git שלנו), הוא יראה את הסיסמאות ויוכל לעשות נזק. בעיה נוספת היא גמישות, כאשר אנו עובדים עם יותר מסביבה אחת (Production ו-Development) יהיו לנו לרוב סיסמאות שונות או טוקנים שונים, ובמידה והערכים האלו יהיו שמורים בקוד שלנו יהיה לנו קשה לעשות הפרדה בין השניים.
הגדרה של משתני סביבה
כאשר אנחנו עובדים עם מערכת CI/CD אשר אחראית על פריסת הקוד שלנו על השרת(ים), לרוב כחלק מתהליך הפריסה יוגדרו גם משתני סביבה שנרצה (תלוי מה נגדיר בשרת ה-CI/CD).
מלבד הגדרה דרך שרת CI/CD, לפעמים גם תהליכים שונים בשרת יגדירו משתני סביבה כחלק מתהליך עליית ה-Process.
כדי להגדיר משתני סביבה ב-PHP, נוכל להשתמש בפונקציה putenv
באופן הבא:
putenv( 'access_token=Pq5gjm5VqF' );
כמו שניתן לראות בדוגמא ☝️, הפונקציה מקבל מחרוזת אשר מורכבת משני ערכים שיש ביניהם סימן שווה.
הטקסט אשר נמצא לפני סימן השווה הוא המפתח, והחלק אחרי סימן השווה הוא הערך.
כלומר הגדרנו משתנה סביבה חדש ששמו access_token
שהערך שלו הוא Pq5gjm5VqF
.
על מנת להשתמש במשנה סביבה זה אנו נשתמש בפונקציה getenv
באופן הבא:
$token = getenv( 'access_token' );
שימוש בקבצי DotEnv
כיום נהוג להגדיר משתני סביבה בקבצי .env
, קבצים אלו נשמרים בתיקייה אשר לא נגישה באופן ציבורי.
קבצים אלו מכילים שורות כאשר כל שורה מכילה שם משתנה סביבה ואת הערך שלו.
כאשר תהליך מסויים עולה או כאשר מכונה עולה, לרוב ישנו סקריפט אשר רץ על קובץ .env
אשר הוגדר לו וטוען את התוכן של הקובץ לזיכרון המכונה ובעצם ממיר אותם למשתני סביבה.
יש לשמור את קבצים אלו עם הרשאות מתאימות ומחוץ לתיקייה של הפרוייקט שלנו על מנת למנוע גישה ישירה לקובץ דרך הדפדפן לדוגמא.
שימוש בקבצים אלו מאפשרים לנו לוודא שכאשר עולה תהליך או מכונה יהיה ניתן להגדיר את משתני הסביבה בצורה אוטומטית מבלי הצורך להתערבות ידנית.
בניית סקריפט לטעינה של קבצי DotEnv
הגענו לחלק המעניין של המאמר ובו נלמד איך לבנות סקריפט אשר קורא קובץ .env
וממיר את התוכן שלו למשתני סביבה 💪.
חלק ראשון – קריאת קובץ DotEnv
בשלב הראשון עלינו קודם כל לקרוא את הקובץ, על מנת לעשות זאת נשתמש בקוד הבא:
$lines = file( $dotEnvFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
אנו משתמשים בפונקציה file
על מנת לקרוא את הקובץ ומגדירים לו להתעלם משורות ריקות, לאחר הקריאה של הקובץ, אנו נקבל מערך עם שורות הקובץ.
חלק שני – ריצה והגדרה של משתני סביבה
כעת לאחר שיש לנו את התוכן של הקובץ, נרצה בעצם לפרק את השורות ולהמיר אותן למשתני סביבה.
foreach ( $lines as $line ) {
if ( strpos( trim( $line ), '#' ) === 0 ) {
continue;
}
[ $name, $value ] = explode( '=', $line, 2 );
$name = trim( $name );
$value = trim( $value );
if ( array_key_exists( $name, $_SERVER ) || array_key_exists( $name, $_ENV ) ) {
continue;
}
putenv( sprintf( '%s=%s', $name, $value ) );
$_ENV[ $name ] = $value;
$_SERVER[ $name ] = $value;
}
בתור התחלה אנו בודקים האם השורה מתחילה בסולמית, במידה וכן אנו מדלגים לשורה הבא. סולמית בתחילת שורת לרוב מייצגת הערה.
לאחר מכן אנו שוברים את השורה לשני חלקים במידה ויש בה סימן שווה, זה בעצם הדרך שלנו לקבל את שם משתנה הסביבה ואת הערך שלו במשתנים שונים.
על מנת למנוע דריסה של משתני סביבה קיימים, מתבצעת בדיקה האם כבר קיים משתנה סביבה עם השם הספציפי, במידה וכן אנו מדלגים לשורה הבאה.
לבסוף אנו מגדירים את משתנה הסביבה ב-3 דרכים שונות על מנת לוודא תאימות מלאה.
הקוד המלא כמחלקה
במידה ואתן מעוניינים להשתמש בקוד הבא במערכת שלכם אני ממליץ לכם להשתמש במחלקה פשוטה שכתבתי.
/**
* Class DotEnv
*
* @author Dor Zuberi
* @link https://www.dorzki.io
*/
class DotEnv {
/**
* DotEnv file path.
*
* @var string
*/
protected string $file;
/* ------------------------------------------ */
/**
* DotEnv constructor.
*
* @param string $file DotEnv file path.
*
* @throws InvalidArgumentException If file doesn't exist.
*/
public function __construct( string $file ) {
if ( ! file_exists( $file ) ) {
throw new InvalidArgumentException( 'DotEnv file does not exist.' );
}
$this->file = $file;
}
/* ------------------------------------------ */
/**
* Load .env file.
*
* @throws RuntimeException If file is not readable.
*
* @since 1.0.0
*/
public function load() {
if ( ! is_readable( $this->file ) ) {
throw new RuntimeException( 'DotEnv file is not readable.' );
}
$lines = file( $this->file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
foreach ( $lines as $line ) {
if ( strpos( trim( $line ), '#' ) === 0 ) {
continue;
}
[ $name, $value ] = explode( '=', $line, 2 );
$name = trim( $name );
$value = trim( $value );
if ( array_key_exists( $name, $_SERVER ) || array_key_exists( $name, $_ENV ) ) {
continue;
}
$this->set( $name, $value );
}
}
/* ------------------------------------------ */
/**
* Sets a new environment variable.
*
* @param string $name Variable name.
* @param string $value Variable value.
*
* @since 1.0.0
*/
private function set( string $name, string $value ) {
putenv( sprintf( '%s=%s', $name, $value ) );
$_ENV[ $name ] = $value;
$_SERVER[ $name ] = $value;
}
/**
* Retrieve a environment variable.
*
* @param string $name Variable name.
*
* @return string
*
* @since 1.0.0
*/
public static function get( string $name ) : string {
return getenv( $name );
}
}
על מנת להשתמש במחלקה יש לאתחל אותה ולהעביר לה את הנתיב של קובץ .env
הרלוונטי.
לאחר מכן, על מנת לקבל ערך של משתנה סביבה ניתן לכתוב כך:
$token = DotEnv::get( 'access_token' );
סיכום
משתני סביבה הם כלי חשוב כאשר כותבים מערכת, הם מנהג בריא 🥦 והם יכולים לעזור לנו לשמור על האבטחה של המערכת שלנו ע״י שמירת מידע רגיש בזיכרון השרת ולא בקוד שלנו.
נתראה במאמר הבא!