@@ -408,4 +408,71 @@ public function parseSearch($query) {
408408 }
409409 else return ['success ' =>0 , 'message ' =>$ this ->lastError , 'data ' =>$ data ];
410410 }
411+
412+ /* Obtain Data Safety details for a given app
413+ * @method public parsePrivacy
414+ * @param string packageName identifier for the app, e.g. 'com.example.app'
415+ * @param optional string lang language for translations. Should be ISO 639-1 two-letter code. Default: en
416+ * @return array privacy details on the app on success, details on the error otherwise
417+ */
418+ public function parsePrivacy ($ packageName , $ lang ='en ' ) {
419+ $ link = sprintf ('https://play.google.com/store/apps/datasafety?id=%s&hl=%s ' , $ packageName , $ lang );
420+ if ( $ this ->input = @file_get_contents ($ link ) ) {
421+ preg_match ("!HTTP/1\.\d\s+(\d{3})\s+(.+)$!i " , $ http_response_header [0 ], $ match );
422+ $ response_code = $ match [1 ];
423+ switch ($ response_code ) {
424+ case "200 " : // HTTP/1.0 200 OK
425+ break ;
426+ case "400 " : // echo "! No XHR for '$pkg'\n";
427+ case "404 " : // app no longer on play
428+ default :
429+ $ this ->lastError = $ http_response_header [0 ];
430+ return ['success ' =>0 , 'values ' =>[], 'message ' =>$ http_response_header [0 ]];
431+ break ;
432+ }
433+ } else { // network error (e.g. "failed to open stream: Connection timed out")
434+ $ this ->lastError = 'network error ' ;
435+ return ['success ' =>0 , 'values ' =>[], 'message ' =>'network error ' ];
436+ }
437+
438+ $ doc = new DOMDocument ();
439+ @$ doc ->loadHTML ($ this ->input );
440+ $ xp = new DOMXPath ($ doc );
441+
442+ $ nlh = $ xp ->query ("//div[@class='Mf2Txd']/h2 " ); // node list of headers
443+ if ($ this ->debug ) echo "Privacy sections: " .$ nlh ->length ."\n" ;
444+
445+ $ sections = [];
446+ foreach ($ nlh as $ section ) {
447+ $ sname = trim ($ section ->nodeValue );
448+ $ node = $ section ->nextSibling ;
449+ $ desc = '' ; $ extras = [];
450+ switch ( $ node ->getAttribute ('class ' ) ) {
451+ case 'ivTO9c ' : $ desc = $ node ->firstChild ->textContent ; break ;
452+ case 'XgPdwe ' :
453+ $ desc = 'see extras ' ;
454+ foreach ($ node ->childNodes as $ child ) {
455+ $ ex = $ child ->firstChild ->nextSibling ->firstChild ; // the extra detail's header
456+ $ eh = $ ex ->nodeValue ;
457+ $ ex = $ ex ->nextSibling ; // the extra's details
458+ $ ed = $ ex ->nodeValue ;
459+ $ extras [] = ['name ' =>$ eh , 'desc ' =>$ ed ];
460+ }
461+ break ;
462+ default : if ($ this ->debug ) echo "Got unknown class ' " . strtolower ($ node ->getAttribute ('class ' ))."' for section description \n" ; break ;
463+ }
464+ $ node = $ node ->nextSibling ;
465+ if ( empty ($ extras ) ) {
466+ foreach ($ node ->childNodes as $ child ) {
467+ $ ex = $ child ->firstChild ->firstChild ->firstChild ->firstChild ->firstChild ->nextSibling ->firstChild ; // the extra detail's header
468+ $ eh = $ ex ->nodeValue ;
469+ $ ex = $ ex ->nextSibling ; // the extra's details
470+ $ ed = $ ex ->nodeValue ;
471+ $ extras [] = ['name ' =>$ eh , 'desc ' =>$ ed ];
472+ }
473+ }
474+ $ sections [] = ['name ' =>$ sname , 'desc ' =>$ desc , 'extras ' => $ extras ];
475+ }
476+ return ['success ' =>1 , 'values ' =>$ sections , 'message ' =>'' ];
477+ }
411478}
0 commit comments