מהירות טעינת האתר הינו פקטור מאוד משמעותי אשר משפיע על דירוג האתר במנועי החיפוש, Cache היא טכנולוגיה אשר יכולה לשפר פלאות את מהירות הטעינה של האתר.

מהירות טעינת הדף הינו מרכיב עיקרי וחשוב מאוד בבניית האתר, ידוע כי מנועי החיפוש, בדגש על גוגל, נותנים פקטור עיקרי למהירות עליית הדף בחישוב ציון האתר שלנו ומיקומו בתוצאות החיפוש.
ככל שלאתר לוקח יותר זמן לעלות, כך הדירוג שלו יהיה נמוך יותר במנועי החיפוש כקב ירידה בכמות הכניסות או עלייה ב-Bounce Rate.
אופטימיזציה היא חלק שנוגע לא רק בקידום האתר שלכם אלא גם בחווית המשתמש של הגולש – גולש לא מרוצה זהו הפסד הפוטנציאל להפיכתו ללקוח.

אחד הפתרונות הקלים והיעילים ביותר בקיצור מהירות עליית הדף היא טכנולוגיה הנקראת Caching.
Cache (שימוש בזיכרון מטמון), הינו פתרון אשר מייעל את צורת הגשת האתר וחוסכת במשאבי שרת בשעות עומס ע"י הגשת תוכן בצורה סטטית.

מהם הגורמים המשפיעים על מהירות עליית האתר?

לפני שנסביר ונתחיל לבנות את מנוע ה-Caching שלנו, אנחנו צריכים להבין קודם, מהם בעצם הגורמים אשר משפיעים על זמן עליית הדף שלנו:

ישנם עוד גורמים אשר משפיעים על מהירות עליית האתר, אך אלו הם הגורמים העיקריים.

מהי טכנולוגיית ה-Caching?

תחילה נסביר שני מושגים:

לרוב, כאשר מספר גולשים יכנסו לאותו העמוד, יוצג לכל אחד מהם אותו התוכן, דבר אשר יצריך כל פעם גישה למסד הנתונים, ביצוע שאילתה ועיבוד התוצאות לכדי עמוד תוכן.
קריאה למסד הנתונים ועיבוד הבקשה גוזלים משאבים יקרים מהשרת – כאן נכנסת טכנולוגיית ה-Caching, אשר מאפשרת לנו להגיש לגולש עמודים דינאמיים כסטטים.

טכנולוגיית ה-Caching עובדת בצורה פשוטה, כאשר גולש נכנס לעמוד מסויים, נוצרת בקשה אשר בודקת האם קיים עותק סטטי של העמוד.
במידה וקיים עותק סטטי של העמוד, נבדק האם היא עדכנית ועומדת במגבלת הזמן של העותק, אם כן העותק מוגש לגולש.
במידה ואין עותק או שהוא אינו עומד במגבלת הזמן, מוגש לגולש העמוד הדינאמי אך מאחורי הקלעים נשמר עותק סטטי עבור שימוש עתידי.

בניית מנוע Cache

לאחר שהבנו מהם הגורמים המשפיעים על זמן טעינת העמוד ומהי הטכנולוגיה, ניגש לחלק הטכני.

שלב ראשון – בניית מחלקת Caching

בשלב הראשון, ניצור את השלד של המחלקה אשר תהיה אחראית על טיפול יצירה ותיעוד של מנוע ה-Caching שלנו.

<?php

/**
 * Simple Caching Engine Class
 *
 * @author Dor Zuberi <dor@dorzki.co.il>
 * @copyright dorzki
 * @link https://www.dorzki.io
 */
class CacheEngine
{

	/**
	 * The path to the caching directory.
	 * 
	 * @var string
	 */
	private $cacheDirectory = '../cache/';

	/**
	 * Cache file extension.
	 * 
	 * @var string
	 */
	private $cacheExtension = '.html';

	/**
	 * Cache max life.
	 * 
	 * @var integer
	 */
	private $cacheTime = 36000;

	/**
	 * List of pages not to cache.
	 * 
	 * @var array
	 */
	private $dontCache = array();

	/**
	 * The last file name of the cached page.
	 * 
	 * @var string
	 */
	private $lastCached = '';

	/**
	 * Log file name.
	 * 
	 * @var string
	 */
	private $logFile = '.cache-log';
	
	/**
	 * Initiates the class by ensuring the caching directory exists.
	 */
	function __construct() {

	}

	/**
	 * Get class property.
	 * 
	 * @param  mixed $property a class property
	 * @return mixed           class property value.
	 */
	public function __get( $property ) {

	}

	/**
	 * Set class property.
	 * 
	 * @param mixed $property a class property
	 * @param mixed $value    new property value.
	 */
	public function __set( $property, $value ) {

	}

	/**
	 * The beginning of the caching process, including checking the copy and
	 * delivering the static copy or regenerating it.
	 */
	public function header() {

	}

	/**
	 * The end of the caching process, only acts if no static updated copy
	 * exists.
	 */
	public function footer() {

	}

	/**
	 * Checks if the cached file exists.
	 *
	 * @return boolean is the page cached?
	 */
	private function isCached() {

	}

	/**
	 * Checks if the cached file hasn't expired.
	 *
	 * @return boolean has the page expired?
	 */
	private function notExpired() {

	}

	/**
	 * Add page name or url for the engine to skip caching it.
	 *
	 * @param string $page page url or name to skip.
	 */
	public function skipPage( $page ) {

	}

	/**
	 * Purge a single page from the cache directory.
	 *
	 * @return boolean was the file deleted?
	 */
	public function purgeFile( $page ) {

	}

	/**
	 * Purge the entire cache directory.
	 *
	 * @return boolean was the directory cleared?
	 */
	public function purgeCache() {

	}

	/**
	 * Opens the log file and add to the top of the file a custom
	 * event details.
	 *
	 * @param string $event the event description.
	 */
	private function logEvent( $event ) {

	}

}

השלד מורכב ממספר משתנים אשר מגדירים את התנהגות המנוע.

מלבד המשתנים הנ"ל, המחלקה מכילה גם מספר פונקציות אשר מבצעות את תפקידי המנוע, נבחן את כל אחת מהפונקציות העיקריות לעומק.

פונקציית הבנאי (אתחול)

הפונקצייה הראשונה היא פונקציית הבנאי, היא הפונקציה אשר רצח בעת אתחול / יצירה של אובייקט חדש של המחלקה שלנו.

/**
 * Initiates the class by ensuring the caching directory exists.
 */
function __construct() {

	if( !file_exists( $this->cacheDirectory ) OR !is_writeable( $this->cacheDirectory ) ) {
		mkdir( $this->cacheDirectory );
		chmod( $this->cacheDirectory, 755 );

		$this->logEvent( 'Engine cache directory created;' );
	}

	$this->logEvent( 'Engine Initiated;' );

}

פונקציית header

הפונקצייה העיקרית במנוע, אחראית על ביצוע בדיקה האם קיים עותק סטטי של העמוד והאם הוא עדכני.
במידה ולא קיים עותק, היא ופונקצייה footer() יחדיו מציגות את העמוד ומייצרות עותק סטטי של העמוד לפעם הבאה.

/**
 * The beginning of the caching process, including checking the copy and
 * delivering the static copy or regenerating it.
 */
public function header() {

	// Generate cache file name.
	$this->lastCached = sprintf( '%scached-%s%s', $this->cacheDirectory, base64_encode( $_SERVER['REQUEST_URI'] ), $this->cacheExtension );

	if( !in_array( $_SERVER['REQUEST_URI'], $this->dontCache ) ) {
		if( $this->isCached() AND $this->notExpired() ) {
			echo file_get_contents( $this->lastCached );

			$this->logEvent( "Cached page served [{$this->lastCached}]" );

			exit();
		} else {
			ob_start();
		}
	}
}

פונקציית footer

הפונקצייה אשר סוגרת את פעילות מנוע ה-caching שלנו, פועלת אך ורק אם פונקצייה header() הגישה עמוד דינאמי ולא סטטי.

/**
 * The end of the caching process, only acts if no static updated copy
 * exists.
 */
public function footer() {

	$cacheComment = "\n\n";

	if( !in_array( $_SERVER['REQUEST_URI'], $this->dontCache ) ) {
		if( !$this->isCached() OR !$this->notExpired() ) {
			file_put_contents( $this->lastCached, $cacheComment . ob_get_contents() );
			ob_end_flush();

			$this->logEvent( "Cached page generated [{$this->lastCached}]" );
		}
	} else {
		$this->logEvent( "Page served dynamically [{$_SERVER['REQUEST_URI']}]" );
	}

}

פונקציית isCached

הפונקציה בודקת האם קיים עותק של העמוד בתיקייה, במידה וכן הפונקצייה תחזיר ערך אמת, אחרת תחזיר ערך שקר.

/**
 * Checks if the cached file exists.
 *
 * @return boolean is the page cached?
 */
private function isCached() {

	return (boolean) file_exists( $this->lastCached );

}

פונקציית notExpired

הפונקציה מבצעת חישוב פשוט אשר קובץ האם הקובץ ששמור הוא קובץ עדכני.
החישוב הוא: זמן נוכחי - זמן יצירת הקובץ < זמן הוגדר כעדכני

/**
 * Checks if the cached file hasn't expired.
 *
 * @return boolean has the page expired?
 */
private function notExpired() {

	return ( time() - filemtime( $this->lastCached ) < $this->cacheTime ) ? true : false;

}

פונקציית skipPage

הפונקציה מקבלת כפרמטר את כתובת העמוד הרצוי ומכניסה אותו לרשימת עמודים אשר לא אמורים להיות סטטים בכלל.

/**
 * Add page name or url for the engine to skip caching it.
 *
 * @param string $page page url or name to skip.
 */
public function skipPage( $page ) {

	$this->dontCache[] = $page;

}

פונקציית purgeFile

הפונקציה מקבלת כפרמטר את כתובת העמוד אשר מעוננינים למחוק את העותק הסטטי שלו (לדוגמא לטובת יצירת עותק חדש אחרי עדכון העמוד).
הפונקציה תחזיר ערך אמת אם הקובץ נמחק ושקר אם התרחשה שגיאה.

/**
 * Purge a single page from the cache directory.
 *
 * @return boolean was the file deleted?
 */
public function purgeFile( $page ) {

	$cachedFile = sprintf( '%scached-%s%s', $this->cacheDirectory, base64_encode( $_SERVER['REQUEST_URI'] ), $this->cacheExtension );

	$this->logEvent( "Page cahced file purged [{$page}]" );

	return (boolean) unlink( $cachedFile );

}

פונקציית purgeCache

הפונקציה מבצעת מחיקה מלאה של התיקייה המכילה את ה-cache ששמורים בה, למעט קובץ התיעוד.
בסיום התהליך יחוזר ערך אמת (True) אם כלל הקבצים נמחקו או ערך שקר (False) בעת שגיאה.

/**
 * Purge the entire cache directory.
 *
 * @return boolean was the directory cleared?
 */
public function purgeCache() {

	$cachedFiles = glob( $this->cacheDirectory . '*' . $this->cacheExtension );

	foreach ( $cachedFiles as $file ) {
		if( !unlink( $file ) ) {
			$this->logEvent( "Error trying to empty cache folder" );

			return false;
		}
	}

	$this->logEvent( "Cache folder emptied" );

	return true;

}

פונקציית logEvent

הפונקציה מקבלת כפרמטר את הערך שיש לתעד ושומרת אותו בקובץ התיעוד של המנוע.
הקובץ שימושי כאשר רוצים לבדוק את פעולת המנוע, במידה והתרחשה שגיאה או בשביל לייצר סטטיסטיקה אודות פעילות המנוע.

/**
 * Opens the log file and add to the top of the file a custom
 * event details.
 *
 * @param string $event the event description.
 */
private function logEvent( $event ) {

	// Event Timestamp
	$eventTime = date( 'd-m-Y, H:i:s' );
	// Event Row Template
	$eventTemp = sprintf( "[ %s | > %s; ]\n", $eventTime, $event );

	$logger = fopen( $this->cacheDirectory . $this->logFile, 'a' );
	fwrite( $logger, $eventTemp );

	// Free up resources.
	fclose( $logger );
	
}

שלב שני – שימוש במנוע ה-Caching

על מנת להטמיע את מנוע ה-Cache יש לשמור את הקובץ בתיקייה ראשית של האתר ולהטמיע אותו בראש העמוד ובתחתית העמוד.

קוד להטמעה בראש העמוד

<?php
// Including the cache engine class.
include( 'cache.class.php' );
// Initiating the engine & calling the header() function.
$cache = new cacheEngine();
$cache->header();
?>

קוד להטמעה בתחתית העמוד

<?php 
// Calling the footer() function to end the engine's operation.
$cache->footer();
?>

סיכום

Cache הינו אחד מבין מגוון רחב של שיטות לקיצור זמני הטעינה של דפי האתר וביצוע אופטימיזציה לאתר.
שימוש במנוע Cache יאפשר לנו גם לחסוך בצורה משמעותית (כמעט 70%) במשאבי השרת ע״י הגשת עמודים סטיים.

ישנן מגוון רב של מנועי Cache אשר מאפשרים לבצע דברים יותר מוכבים ע״י שימוש בשיטות שונות ובטכניקות מורכבות יותר.

    כתיבת תגובה

    אפשר להציע לך עוגיות? יש גם קפה! השימוש בקוקיז עוזר לשפר את הביקור שלך באתר. המשך גלישה אומר שהסכמת למדיניות הפרטיות שלי, וגם לקפה.

    שתפו