22
33namespace DreamFactory \Core \Script \Services ;
44
5+ use Config ;
56use DreamFactory \Core \Contracts \HttpStatusCodeInterface ;
67use DreamFactory \Core \Contracts \ServiceResponseInterface ;
78use DreamFactory \Core \Enums \ApiOptions ;
1213use DreamFactory \Core \Services \BaseRestService ;
1314use DreamFactory \Core \Utility \ResourcesWrapper ;
1415use DreamFactory \Core \Utility \ResponseFactory ;
16+ use DreamFactory \Core \Utility \Session ;
1517use Illuminate \Foundation \Bus \DispatchesJobs ;
1618use Log ;
1719
@@ -39,6 +41,10 @@ class Script extends BaseRestService
3941 * @var array $scriptConfig Configuration for the engine for this particular script
4042 */
4143 protected $ scriptConfig ;
44+ /**
45+ * @type bool
46+ */
47+ protected $ cacheEnabled = false ;
4248 /**
4349 * @var boolean $queued Configuration for the engine for this particular script
4450 */
@@ -77,6 +83,9 @@ public function __construct($settings = [])
7783 $ this ->scriptConfig = [];
7884 }
7985
86+ $ this ->cacheEnabled = array_get_bool ($ this ->config , 'cache_enabled ' );
87+ $ this ->cacheTTL = intval (array_get ($ this ->config , 'cache_ttl ' , Config::get ('df.default_cache_ttl ' )));
88+
8089 $ this ->implementsAccessList = array_get_bool ($ this ->config , 'implements_access_list ' );
8190 }
8291
@@ -177,14 +186,79 @@ public function getAccessList()
177186 return array_values (array_unique ($ list ));
178187 }
179188
189+ protected function buildRequestCacheKey ()
190+ {
191+ // build cache_key
192+ $ cacheKey = $ this ->action ;
193+ $ resource = array_map ('rawurlencode ' , $ this ->resourceArray );
194+ if ($ resource ) {
195+ $ cacheKey .= ': ' . implode ('. ' , $ resource );
196+ }
197+
198+ $ cacheQuery = '' ;
199+ // Using raw query string here to allow for multiple parameters with the same key name.
200+ // The laravel Request object or PHP global array $_GET doesn't allow that.
201+ $ requestQuery = explode ('& ' , array_get ($ _SERVER , 'QUERY_STRING ' ));
202+
203+ // If request is coming from a scripted service then $_SERVER['QUERY_STRING'] will be blank.
204+ // Therefore need to check the Request object for parameters.
205+ foreach ($ this ->request ->getParameters () as $ pk => $ pv ) {
206+ if (is_array ($ pv )) {
207+ foreach ($ pv as $ ipk => $ ipv ) {
208+ $ param = $ pk . '[ ' . $ ipk . ']= ' . $ ipv ;
209+ if (!in_array ($ param , $ requestQuery )) {
210+ $ requestQuery [] = $ param ;
211+ }
212+ }
213+ } else {
214+ $ param = $ pk . '= ' . $ pv ;
215+ if (!in_array ($ param , $ requestQuery )) {
216+ $ requestQuery [] = $ param ;
217+ }
218+ }
219+ }
220+
221+ foreach ($ requestQuery as $ q ) {
222+ $ pairs = explode ('= ' , $ q );
223+ $ name = trim (array_get ($ pairs , 0 ));
224+ $ value = trim (array_get ($ pairs , 1 ));
225+ static ::parseParameter ($ cacheQuery , $ name , $ value );
226+ }
227+
228+ if (!empty ($ cacheQuery )) {
229+ $ cacheKey .= ': ' . $ cacheQuery ;
230+ }
231+
232+ return sha1 ($ cacheKey ); // the key may contain confidential info
233+ }
234+
235+ protected static function parseParameter (&$ key , $ name , $ value )
236+ {
237+ if ('_ ' !== $ name ) { // this value included with jQuery calls should not be considered
238+ if (is_array ($ value )) {
239+ foreach ($ value as $ sub => $ subValue ) {
240+ static ::parseParameter ($ key , $ name . '[ ' . $ sub . '] ' , $ subValue );
241+ }
242+ } else {
243+ Session::replaceLookups ($ value , true );
244+ $ part = $ name ;
245+ if (!empty ($ value )) {
246+ $ part .= '= ' . $ value ;
247+ }
248+ if (!empty ($ key )) {
249+ $ key .= '& ' ;
250+ }
251+ $ key .= $ part ;
252+ }
253+ }
254+ }
255+
180256 /**
181257 * @return bool|mixed
182- * @throws
183- * @throws \DreamFactory\Core\Script\Exceptions\ScriptException
184- * @throws \DreamFactory\Core\Exceptions\BadRequestException
185258 * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException
186259 * @throws \DreamFactory\Core\Exceptions\RestException
187260 * @throws \DreamFactory\Core\Exceptions\ServiceUnavailableException
261+ * @throws \DreamFactory\Core\Script\Exceptions\ScriptException
188262 */
189263 protected function processRequest ()
190264 {
@@ -208,6 +282,19 @@ protected function processRequest()
208282
209283 $ data = $ this ->getRequestData ();
210284
285+ $ cacheKey = null ;
286+ if ($ this ->cacheEnabled ) {
287+ switch ($ this ->action ) {
288+ case Verbs::GET :
289+ // build cache_key
290+ $ cacheKey = $ this ->buildRequestCacheKey ();
291+ if (null !== $ result = $ this ->getFromCache ($ cacheKey )) {
292+ return $ result ;
293+ }
294+ break ;
295+ }
296+ }
297+
211298 $ logOutput = $ this ->request ->getParameterAsBool ('log_output ' , true );
212299 $ result = $ this ->handleScript ('service. ' . $ this ->name , $ this ->getScriptContent (), $ this ->engineType ,
213300 $ this ->scriptConfig , $ data , $ logOutput );
@@ -222,11 +309,17 @@ protected function processRequest()
222309 $ status = array_get ($ result , 'status_code ' , HttpStatusCodeInterface::HTTP_OK );
223310 $ headers = (array )array_get ($ result , 'headers ' );
224311
225- return ResponseFactory::create ($ content , $ contentType , $ status , $ headers );
312+ $ result = ResponseFactory::create ($ content , $ contentType , $ status , $ headers );
313+ } else {
314+ // otherwise assume raw content
315+ $ result = ResponseFactory::create ($ result );
316+ }
317+
318+ if ($ cacheKey ) {
319+ $ this ->addToCache ($ cacheKey , $ result );
226320 }
227321
228- // otherwise assume raw content
229- return ResponseFactory::create ($ result );
322+ return $ result ;
230323 }
231324
232325 protected function getEventName ()
0 commit comments