From f1c855e755aa1a51bba210221a5a2c1850cfec88 Mon Sep 17 00:00:00 2001
From: Hypolite Petovan <ben.lort@gmail.com>
Date: Fri, 28 Oct 2016 06:28:16 -0400
Subject: [PATCH] ping.php performance: Cache notification bbcode

- Add two fields to notifiy table to store cached bbcode result
- Add bbcode conversion on notif creation
- Add postfact caching if cache field is empty on retrieval
---
 boot.php                |  2 +-
 database.sql            |  2 ++
 include/dbstructure.php |  2 ++
 include/enotify.php     | 35 +++++++++++++++++++++++++----
 mod/ping.php            | 49 +++++++++++++++++++++++------------------
 update.php              |  2 +-
 6 files changed, 65 insertions(+), 27 deletions(-)

diff --git a/boot.php b/boot.php
index 4fefe6f7ed..cb4feef903 100644
--- a/boot.php
+++ b/boot.php
@@ -38,7 +38,7 @@ define ( 'FRIENDICA_PLATFORM',     'Friendica');
 define ( 'FRIENDICA_CODENAME',     'Asparagus');
 define ( 'FRIENDICA_VERSION',      '3.5.1-dev' );
 define ( 'DFRN_PROTOCOL_VERSION',  '2.23'    );
-define ( 'DB_UPDATE_VERSION',      1206      );
+define ( 'DB_UPDATE_VERSION',      1207      );
 
 /**
  * @brief Constant with a HTML line break.
diff --git a/database.sql b/database.sql
index f12746e7e5..c5fd49ba01 100644
--- a/database.sql
+++ b/database.sql
@@ -655,6 +655,8 @@ CREATE TABLE IF NOT EXISTS `notify` (
 	`seen` tinyint(1) NOT NULL DEFAULT 0,
 	`verb` varchar(255) NOT NULL DEFAULT '',
 	`otype` varchar(16) NOT NULL DEFAULT '',
+	`name_cache` tinytext,
+	`msg_name` mediumtext,
 	 PRIMARY KEY(`id`),
 	 INDEX `uid` (`uid`)
 ) DEFAULT CHARSET=utf8mb4;
diff --git a/include/dbstructure.php b/include/dbstructure.php
index fdf09d90de..8053965e03 100644
--- a/include/dbstructure.php
+++ b/include/dbstructure.php
@@ -1036,6 +1036,8 @@ function db_definition($charset) {
 					"seen" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
 					"verb" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
 					"otype" => array("type" => "varchar(16)", "not null" => "1", "default" => ""),
+					"name_cache" => array("type" => "tinytext"),
+					"msg_cache" => array("type" => "mediumtext")
 					),
 			"indexes" => array(
 					"PRIMARY" => array("id"),
diff --git a/include/enotify.php b/include/enotify.php
index 4973bedc24..bcb2ebe7ca 100644
--- a/include/enotify.php
+++ b/include/enotify.php
@@ -418,6 +418,7 @@ function notification($params) {
 		$datarray = array();
 		$datarray['hash']  = $hash;
 		$datarray['name']  = $params['source_name'];
+		$datarray['name_cache'] = strip_tags(bbcode($params['source_name']));
 		$datarray['url']   = $params['source_link'];
 		$datarray['photo'] = $params['source_photo'];
 		$datarray['date']  = datetime_convert();
@@ -439,7 +440,7 @@ function notification($params) {
 
 		// create notification entry in DB
 
-		$r = q("INSERT INTO `notify` (`hash`, `name`, `url`, `photo`, `date`, `uid`, `link`, `iid`, `parent`, `type`, `verb`, `otype`)
+		$r = q("INSERT INTO `notify` (`hash`, `name`, `url`, `photo`, `date`, `uid`, `link`, `iid`, `parent`, `type`, `verb`, `otype`, `name_cache`)
 			values('%s', '%s', '%s', '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s')",
 			dbesc($datarray['hash']),
 			dbesc($datarray['name']),
@@ -452,7 +453,8 @@ function notification($params) {
 			intval($datarray['parent']),
 			intval($datarray['type']),
 			dbesc($datarray['verb']),
-			dbesc($datarray['otype'])
+			dbesc($datarray['otype']),
+			dbesc($datarray["name_cache"])
 		);
 
 		$r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' AND `uid` = %d LIMIT 1",
@@ -494,8 +496,10 @@ function notification($params) {
 
 		$itemlink = $a->get_baseurl().'/notify/view/'.$notify_id;
 		$msg = replace_macros($epreamble, array('$itemlink' => $itemlink));
-		$r = q("UPDATE `notify` SET `msg` = '%s' WHERE `id` = %d AND `uid` = %d",
+		$msg_cache = format_notification_message($datarray['name_cache'], strip_tags(bbcode($msg)));
+		$r = q("UPDATE `notify` SET `msg` = '%s', `msg_cache` = '%s' WHERE `id` = %d AND `uid` = %d",
 			dbesc($msg),
+			dbesc($msg_cache),
 			intval($notify_id),
 			intval($params['uid'])
 		);
@@ -778,4 +782,27 @@ function check_item_notification($itemid, $uid, $defaulttype = "") {
 	if (isset($params["type"]))
 		notification($params);
 }
-?>
+
+/**
+ * @brief Formats a notification message with the notification author
+ *
+ * Replace the name with {0} but ensure to make that only once. The {0} is used
+ * later and prints the name in bold.
+ *
+ * @param string $name
+ * @param string $message
+ * @return string Formatted message
+ */
+function format_notification_message($name, $message) {
+	if ($name != '') {
+		$pos = strpos($message, $name);
+	} else {
+		$pos = false;
+	}
+
+	if ($pos !== false) {
+		$message = substr_replace($message, '{0}', $pos, strlen($name));
+	}
+
+	return $message;
+}
\ No newline at end of file
diff --git a/mod/ping.php b/mod/ping.php
index 8c28e74744..a905371226 100644
--- a/mod/ping.php
+++ b/mod/ping.php
@@ -344,6 +344,12 @@ function ping_init(&$a) {
 	killme();
 }
 
+/**
+ * @brief Retrieves the notifications array for the given user ID
+ *
+ * @param int $uid
+ * @return array
+ */
 function ping_get_notifications($uid) {
 
 	$result = array();
@@ -372,46 +378,47 @@ function ping_get_notifications($uid) {
 			$seensql = "";
 			$order = "DESC";
 			$offset = 0;
-		} elseif (!$r)
+		} elseif (!$r) {
 			$quit = true;
-		else
+		} else {
 			$offset += 50;
-
+		}
 
 		foreach ($r AS $notification) {
-			if (is_null($notification["visible"]))
+			if (is_null($notification["visible"])) {
 				$notification["visible"] = true;
+			}
 
-			if (is_null($notification["spam"]))
+			if (is_null($notification["spam"])) {
 				$notification["spam"] = 0;
+			}
 
-			if (is_null($notification["deleted"]))
+			if (is_null($notification["deleted"])) {
 				$notification["deleted"] = 0;
+			}
 
-			$notification["message"] = strip_tags(bbcode($notification["msg"]));
-			$notification["name"] = strip_tags(bbcode($notification["name"]));
+			if ($notification["msg_cache"]) {
+				$notification["name"] = $notification["name_cache"];
+				$notification["message"] = $notification["msg_cache"];
+			} else {
+				$notification["name"] = strip_tags(bbcode($notification["name"]));
+				$notification["message"] = format_notification_message($notification["name"], strip_tags(bbcode($notification["msg"])));
 
-			// Replace the name with {0} but ensure to make that only once
-			// The {0} is used later and prints the name in bold.
+				q("UPDATE `notify` SET `name_cache` = '%s', `msg_cache` = '%s' WHERE `id` = %d",
+					dbesc($notification["name"]),
+					dbesc($notification["message"]),
+					intval($notification["id"])
+				);
+			}
 
-			if ($notification['name'] != "")
-				$pos = strpos($notification["message"],$notification['name']);
-			else
-				$pos = false;
-
-			if ($pos !== false)
-				$notification["message"] = substr_replace($notification["message"],"{0}",$pos,strlen($notification["name"]));
-
-			$notification['href'] = $a->get_baseurl() . '/notify/view/' . $notification['id'];
+			$notification["href"] = $a->get_baseurl() . "/notify/view/" . $notification["id"];
 
 			if ($notification["visible"] AND !$notification["spam"] AND
 				!$notification["deleted"] AND !is_array($result[$notification["parent"]])) {
 				$result[$notification["parent"]] = $notification;
 			}
 		}
-
 	} while ((count($result) < 50) AND !$quit);
 
-
 	return($result);
 }
diff --git a/update.php b/update.php
index 7560911134..5eab9c2207 100644
--- a/update.php
+++ b/update.php
@@ -1,6 +1,6 @@
 <?php
 
-define('UPDATE_VERSION' , 1206);
+define('UPDATE_VERSION' , 1207);
 
 /**
  *