PageRenderTime 61ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/wordpress-seo/admin/linkdex/linkdex.php

https://bitbucket.org/crypticrod/sr_wp_code
PHP | 571 lines | 528 code | 36 blank | 7 comment | 51 complexity | a0cb1582975d00f0c15a6d20f9ba5a12 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.1, GPL-3.0, LGPL-2.0, AGPL-3.0
  1. <?php
  2. class Linkdex {
  3. function __construct() {
  4. require WPSEO_PATH."/admin/linkdex/TextStatistics.php";
  5. }
  6. function output( $post ) {
  7. global $wpseo_metabox;
  8. $options = get_wpseo_options();
  9. if ( is_int( $post ) )
  10. $post = get_post( $post );
  11. if ( !$post )
  12. return;
  13. if ( !class_exists('DOMDocument') ) {
  14. $output = '<div class="wpseo_msg"><p><strong>'.__('Error').':</strong> '.sprintf(__("your hosting environment does not support PHP's %sDocument Object Model%s."), '<a href="http://php.net/manual/en/book.dom.php">','</a>').' '.__("To enjoy all the benefits of the page analysis feature, you'll need to (get your host to) install it.").'</p></div>';
  15. return $output;
  16. }
  17. if ( !wpseo_get_value('focuskw') ) {
  18. $output = '<div class="wpseo_msg"><p><strong>'.__('Error').':</strong> '.__("you have not specified a focus keyword, you'll have to enter one, then save or update this post, before this report works.").'</p></div>';
  19. return $output;
  20. }
  21. $output = '<div class="wpseo_msg"><p><strong>'.__('Note').':</strong> '.__('to update this page analysis, save as draft or update and check this tab again').'.</p></div>';
  22. $results = '';
  23. $job = array();
  24. $sampleurl = get_sample_permalink($post->ID);
  25. $job["pageUrl"] = preg_replace( '/%(post|page)name%/', $sampleurl[1], $sampleurl[0] );
  26. $job["pageSlug"] = urldecode( $post->post_name );
  27. $job["keyword"] = wpseo_get_value('focuskw');
  28. $job["keyword_folded"] = $this->strip_separators_and_fold( $job["keyword"] );
  29. $dom = new domDocument;
  30. $dom->strictErrorChecking = false;
  31. $dom->preserveWhiteSpace = false;
  32. @$dom->loadHTML($post->post_content);
  33. $xpath = new DOMXPath($dom);
  34. $statistics = new TextStatistics;
  35. // Keyword
  36. $this->ScoreKeyword($job, $results);
  37. // Title
  38. if ( wpseo_get_value('title') ) {
  39. $title = wpseo_get_value('title');
  40. } else {
  41. if ( isset( $options['title-'.$post->post_type] ) && $options['title-'.$post->post_type] != '' )
  42. $title_template = $options['title-'.$post->post_type];
  43. else
  44. $title_template = '%%title%% - %%sitename%%';
  45. $title = wpseo_replace_vars($title_template, (array) $post );
  46. }
  47. $this->ScoreTitle($job, $results, $title, $statistics);
  48. unset($title);
  49. // Meta description
  50. $description = '';
  51. if ( wpseo_get_value('metadesc') ) {
  52. $description = wpseo_get_value('metadesc');
  53. } else {
  54. if ( isset( $options['metadesc-'.$post->post_type] ) && $options['metadesc-'.$post->post_type] != '' )
  55. $description = wpseo_replace_vars( $options['metadesc-'.$post->post_type], (array) $post );
  56. }
  57. $this->ScoreDescription($job, $results, $description, $wpseo_metabox->wpseo_meta_length, $statistics);
  58. unset($description);
  59. // Body
  60. $body = $this->GetBody( $post );
  61. $firstp = $this->GetFirstParagraph( $post );
  62. $this->ScoreBody($job, $results, $body, $firstp, $statistics);
  63. unset($body);
  64. unset($firstp);
  65. // URL
  66. $this->ScoreUrl($job, $results, $statistics);
  67. // Headings
  68. $headings = $this->GetHeadings($post->post_content);
  69. $this->ScoreHeadings($job, $results, $headings);
  70. unset($headings);
  71. // Images
  72. $alts = $this->GetImagesAltText($post->post_content);
  73. $imgs = $this->GetImageCount($dom, $xpath);
  74. $this->ScoreImagesAltText($job, $results, $alts, $imgs);
  75. unset($alts);
  76. unset($imgs);
  77. // Anchors
  78. $anchors = $this->GetAnchorTexts($dom, $xpath);
  79. $count = $this->GetAnchorCount($dom, $xpath);
  80. $this->ScoreAnchorTexts($job, $results, $anchors, $count);
  81. unset($anchors);
  82. unset($count);
  83. unset($dom);
  84. asort($results);
  85. $output .= '<table class="wpseoanalysis">';
  86. foreach ($results as $result) {
  87. $scoreval = substr( $result, 0, 1 );
  88. switch ( $scoreval ) {
  89. case 1:
  90. case 2:
  91. case 3:
  92. $score = 'poor';
  93. break;
  94. case 4:
  95. case 5:
  96. case 6:
  97. $score = 'ok';
  98. break;
  99. case 7:
  100. case 8:
  101. case 9:
  102. $score = 'good';
  103. break;
  104. }
  105. $analysis = substr( $result, 2, strlen($result) );
  106. $output .= '<tr><td class="score"><img alt="'.$score.'" src="'.WPSEO_URL.'images/score_'.$score.'.png"/></td><td>'.$analysis.'</td></tr>';
  107. }
  108. $output .= '</table>';
  109. $output .= '<hr/>';
  110. $output .= '<p style="font-size: 13px;"><a href="http://yoa.st/linkdex"><img class="alignleft" style="margin: 0 10px 5px 0;" src="'.WPSEO_URL.'images/linkdex-logo.png" alt="Linkdex"/></a>'.'This page analysis brought to you by the collaboration of Yoast and <a href="http://yoa.st/linkdex">Linkdex</a>. <a href="http://yoa.st/linkdex">Linkdex</a> is an SEO suite that helps you optimize your site and offers you all the SEO tools you\'ll need. Yoast uses <a href="http://yoa.st/linkdex">Linkdex</a> and highly recommends you do too!'.'</p>';
  111. unset($results);
  112. unset($job);
  113. return $output;
  114. }
  115. function SaveScoreResult(&$results, $scoreValue, $scoreUrlStatusMessage) {
  116. $results[] = $scoreValue.'|'.$scoreUrlStatusMessage;
  117. }
  118. function strip_separators_and_fold($inputString, $removeOptionalCharacters=false) {
  119. $keywordCharactersAlwaysReplacedBySpace = array(",", "'", "\"", "?", "’", "“", "”", "|","/");
  120. $keywordCharactersRemovedOrReplaced = array("_","-");
  121. $keywordWordsRemoved = array(" a ", " in ", " an ", " on ", " for ", " the ", " and ");
  122. // lower
  123. $inputString = wpseo_strtolower_utf8($inputString);
  124. // default characters replaced by space
  125. $inputString = str_replace($keywordCharactersAlwaysReplacedBySpace, ' ', $inputString);
  126. // standardise whitespace
  127. $inputString = preg_replace('/\s+/',' ',$inputString);
  128. // deal with the separators that can be either removed or replaced by space
  129. if ($removeOptionalCharacters) {
  130. // remove word separators with a space
  131. $inputString = str_replace($keywordWordsRemoved, ' ', $inputString);
  132. $inputString = str_replace($keywordCharactersRemovedOrReplaced, '', $inputString);
  133. } else {
  134. $inputString = str_replace($keywordCharactersRemovedOrReplaced, ' ', $inputString);
  135. }
  136. // standardise whitespace again
  137. $inputString = preg_replace('/\s+/',' ',$inputString);
  138. return $inputString;
  139. }
  140. function ScoreKeyword($job, &$results) {
  141. $keywordStopWord = __("The keyword for this page contains one or more <a href=\"http://en.wikipedia.org/wiki/Stop_words\">stop words</a>, consider removing them. Found '%s'.");
  142. if ( wpseo_stopwords_check( $job["keyword"] ) !== false )
  143. $this->SaveScoreResult( $results, 5, sprintf( $keywordStopWord, wpseo_stopwords_check( $job["keyword"] ) ) );
  144. }
  145. function ScoreUrl($job, &$results, $statistics) {
  146. $urlGood = __("The keyword / phrase appears in the URL for this page.");
  147. $urlMedium = __("The keyword / phrase does not appear in the URL for this page. If you decide to rename the URL be sure to check the old URL 301 redirects to the new one!");
  148. $urlStopWords = __("The slug for this page contains one or more <a href=\"http://en.wikipedia.org/wiki/Stop_words\">stop words</a>, consider removing them.");
  149. $longSlug = __("The slug for this page is a bit long, consider shortening it.");
  150. $needle = $this->strip_separators_and_fold( $job["keyword"] );
  151. $haystack1 = $this->strip_separators_and_fold( $job["pageUrl"], true );
  152. $haystack2 = $this->strip_separators_and_fold( $job["pageUrl"], false );
  153. if (strrpos($haystack1,$needle) || strrpos($haystack2,$needle))
  154. $this->SaveScoreResult( $results, 9, $urlGood );
  155. else
  156. $this->SaveScoreResult( $results, 5, $urlMedium );
  157. // Check for Stop Words in the slug
  158. if ( wpseo_stopwords_check( $job["pageSlug"], true ) !== false )
  159. $this->SaveScoreResult( $results, 5, $urlStopWords );
  160. // Check if the slug isn't too long relative to the length of the keyword
  161. if ( ( $statistics->text_length( $job["keyword"] ) + 20 ) < $statistics->text_length( $job["pageSlug"] ) && 40 < $statistics->text_length( $job["pageSlug"] ) )
  162. $this->SaveScoreResult( $results, 5, $longSlug );
  163. }
  164. function ScoreTitle($job, &$results, $title, $statistics) {
  165. $scoreTitleMinLength = 40;
  166. $scoreTitleMaxLength = 70;
  167. $scoreTitleKeywordLimit = 0;
  168. $scoreTitleMissing = __("Please create a page title.");
  169. $scoreTitleCorrectLength = __("The page title is more than 40 characters and less than the recommended 70 character limit.");
  170. $scoreTitleTooShort = __("The page title contains %d characters, which is less than the recommended minimum of 40 characters. Use the space to add keyword variations or create compelling call-to-action copy.");
  171. $scoreTitleTooLong = __("The page title contains %d characters, which is more than the viewable limit of 70 characters; some words will not be visible to users in your listing.");
  172. $scoreTitleKeywordMissing = __("The keyword / phrase %s does not appear in the page title.");
  173. $scoreTitleKeywordBeginning = __("The page title contains keyword / phrase, at the beginning which is considered to improve rankings.");
  174. $scoreTitleKeywordEnd = __("The page title contains keyword / phrase, but it does not appear at the beginning; try and move it to the beginning.");
  175. $scoreTitleKeywordIn = __("The page title contains keyword / phrase.");
  176. if ( $title == "" ) {
  177. $this->SaveScoreResult($results, 1, $scoreTitleMissing);
  178. } else {
  179. $length = $statistics->text_length( $title );
  180. if ($length < $scoreTitleMinLength)
  181. $this->SaveScoreResult( $results, 6, sprintf($scoreTitleTooShort, $length) );
  182. else if ($length > $scoreTitleMaxLength)
  183. $this->SaveScoreResult( $results, 6, sprintf($scoreTitleTooLong, $length) );
  184. else
  185. $this->SaveScoreResult( $results, 9, $scoreTitleCorrectLength );
  186. // TODO MA Keyword/Title matching is exact match with separators removed, but should extend to distributed match
  187. $needle_position = stripos( $title, $job["keyword_folded"] );
  188. if ( $needle_position === false )
  189. $needle_position = stripos( $title, $job["keyword"] );
  190. if ( $needle_position === false )
  191. $this->SaveScoreResult( $results, 2, sprintf( $scoreTitleKeywordMissing, $job["keyword_folded"] ) );
  192. if ( $needle_position <= $scoreTitleKeywordLimit )
  193. $this->SaveScoreResult( $results, 9, $scoreTitleKeywordBeginning );
  194. else
  195. $this->SaveScoreResult( $results, 6, $scoreTitleKeywordEnd );
  196. }
  197. }
  198. function ScoreAnchorTexts($job, &$results, $anchor_texts, $count) {
  199. $scoreNoLinks = __("No outbound links appear in this page, consider adding some as appropriate.");
  200. $scoreKeywordInOutboundLink = __("You're linking to another page with the keyword you want this page to rank for, consider changing that if you truly want this page to rank.");
  201. $scoreLinksDofollow = __("This page has %s outbound link(s).");
  202. $scoreLinksNofollow = __("This page has %s outbound link(s), all nofollowed.");
  203. $scoreLinks = __("This page has %s nofollowed link(s) and %s normal outbound link(s).");
  204. if ( $count['external']['nofollow'] == 0 && $count['external']['dofollow'] == 0 ) {
  205. $this->SaveScoreResult( $results, 6, $scoreNoLinks );
  206. } else {
  207. $found = false;
  208. foreach ($anchor_texts as $anchor_text) {
  209. if ( wpseo_strtolower_utf8( $anchor_text ) == $job["keyword_folded"] )
  210. $found = true;
  211. }
  212. if ( $found )
  213. $this->SaveScoreResult($results, 2, $scoreKeywordInOutboundLink);
  214. if ( $count['external']['nofollow'] == 0 && $count['external']['dofollow'] > 0 ) {
  215. $this->SaveScoreResult($results, 9, sprintf( $scoreLinksDofollow, $count['external']['dofollow'] ) );
  216. } else if ( $count['external']['nofollow'] > 0 && $count['external']['dofollow'] == 0 ) {
  217. $this->SaveScoreResult($results, 7, sprintf( $scoreLinksNofollow, $count['external']['nofollow'] ) );
  218. } else {
  219. $this->SaveScoreResult($results, 8, sprintf( $scoreLinks, $count['external']['nofollow'], $count['external']['dofollow'] ) );
  220. }
  221. }
  222. }
  223. function GetAnchorTexts(&$dom, &$xpath) {
  224. $query = "//a|//A";
  225. $dom_objects = $xpath->query($query);
  226. $anchor_texts = array();
  227. foreach ($dom_objects as $dom_object) {
  228. if ( $dom_object->attributes->getNamedItem('href') ) {
  229. $href = $dom_object->attributes->getNamedItem('href')->textContent;
  230. if ( substr( $href, 0, 4 ) == 'http' )
  231. $anchor_texts['external'] = $dom_object->textContent;
  232. }
  233. }
  234. unset($dom_objects);
  235. return $anchor_texts;
  236. }
  237. function GetAnchorCount(&$dom, &$xpath) {
  238. $query = "//a|//A";
  239. $dom_objects = $xpath->query($query);
  240. $count = array(
  241. 'total' => 0,
  242. 'internal' => array( 'nofollow' => 0, 'dofollow' => 0 ),
  243. 'external' => array( 'nofollow' => 0, 'dofollow' => 0 ),
  244. 'other' => array( 'nofollow' => 0, 'dofollow' => 0 )
  245. );
  246. foreach ($dom_objects as $dom_object) {
  247. $count['total']++;
  248. if ( $dom_object->attributes->getNamedItem('href') ) {
  249. $href = $dom_object->attributes->getNamedItem('href')->textContent;
  250. $wpurl = get_bloginfo('url');
  251. if ( substr( $href, 0, 1 ) == "/" || substr( $href, 0, strlen( $wpurl ) ) == $wpurl )
  252. $type = "internal";
  253. else if ( substr( $href, 0, 4 ) == 'http' )
  254. $type = "external";
  255. else
  256. $type = "other";
  257. if ( $dom_object->attributes->getNamedItem('rel') ) {
  258. $link_rel = $dom_object->attributes->getNamedItem('rel')->textContent;
  259. if ( stripos($link_rel, 'nofollow') !== false )
  260. $count[$type]['nofollow']++;
  261. else
  262. $count[$type]['dofollow']++;
  263. } else {
  264. $count[$type]['dofollow']++;
  265. }
  266. }
  267. }
  268. return $count;
  269. }
  270. function ScoreImagesAltText($job, &$results, $alts, $imgcount) {
  271. $scoreImagesNoImages = __("No images appear in this page, consider adding some as appropriate.");
  272. $scoreImagesNoAlt = __("The images on this page are missing alt tags.");
  273. $scoreImagesAltKeywordIn = __("The images on this page contain alt tags with the target keyword / phrase.");
  274. $scoreImagesAltKeywordMissing = __("The images on this page do not have alt tags containing your keyword / phrase.");
  275. if ( $imgcount == 0 ) {
  276. $this->SaveScoreResult($results,6,$scoreImagesNoImages);
  277. } else if ( count($alts) == 0 && $imgcount != 0 ) {
  278. $this->SaveScoreResult($results,3,$scoreImagesNoAlt);
  279. } else {
  280. $found=false;
  281. foreach ($alts as $alt) {
  282. $haystack1=$this->strip_separators_and_fold($alt,true);
  283. $haystack2=$this->strip_separators_and_fold($alt,false);
  284. if (strrpos($haystack1,$job["keyword_folded"])!==false)
  285. $found=true;
  286. else if (strrpos($haystack2,$job["keyword_folded"])!==false)
  287. $found=true;
  288. }
  289. if ($found)
  290. $this->SaveScoreResult($results,9,$scoreImagesAltKeywordIn);
  291. else
  292. $this->SaveScoreResult($results,5,$scoreImagesAltKeywordMissing);
  293. }
  294. }
  295. function GetImagesAltText($postcontent) {
  296. preg_match_all( '/<img [^>]+ alt=(["\'])([^\\1]+)\\1[^>]+>/im', $postcontent, $matches );
  297. $alts = array();
  298. foreach ( $matches[2] as $alt ) {
  299. $alts[] = wpseo_strtolower_utf8( $alt );
  300. }
  301. return $alts;
  302. }
  303. function GetImageCount(&$dom, &$xpath) {
  304. $query = "//img|//IMG";
  305. $dom_objects = $xpath->query($query);
  306. $count = 0;
  307. foreach ($dom_objects as $dom_object)
  308. $count++;
  309. return $count;
  310. }
  311. function ScoreHeadings($job, &$results, $headings) {
  312. $scoreHeadingsNone = __("No heading tags appear in the copy.");
  313. $scoreHeadingsKeywordIn = __("Keyword / keyphrase appears in %s (out of %s) headings in the copy. While not a major ranking factor, this is beneficial.");
  314. $scoreHeadingsKeywordMissing = __("You have not used your keyword / keyphrase in any heading in your copy.");
  315. $headingCount = count( $headings );
  316. if ( $headingCount == 0 )
  317. $this->SaveScoreResult( $results, 6, $scoreHeadingsNone );
  318. else {
  319. $found = 0;
  320. foreach ($headings as $heading) {
  321. $haystack1 = $this->strip_separators_and_fold( $heading , true );
  322. $haystack2 = $this->strip_separators_and_fold( $heading , false );
  323. if ( strrpos( $haystack1, $job["keyword_folded"]) !== false )
  324. $found++;
  325. else if ( strrpos( $haystack2, $job["keyword_folded"]) !== false )
  326. $found++;
  327. }
  328. if ( $found )
  329. $this->SaveScoreResult($results,9, sprintf( $scoreHeadingsKeywordIn, $found, $headingCount ) );
  330. else
  331. $this->SaveScoreResult($results,3,$scoreHeadingsKeywordMissing);
  332. }
  333. }
  334. // Currently just returns an array of the text content
  335. function GetHeadings( $postcontent ) {
  336. preg_match_all('/<h([1-6])([^>]+)?>(.*)?<\/h\\1>/i', $postcontent, $matches);
  337. $headings = array();
  338. foreach ($matches[3] as $heading) {
  339. $headings[] = wpseo_strtolower_utf8( $heading );
  340. }
  341. return $headings;
  342. }
  343. function ScoreDescription($job, &$results, $description, $maxlength = 155, $statistics) {
  344. $scoreDescriptionMinLength = 120;
  345. $scoreDescriptionCorrectLength = __("In the specified meta description, consider: How does it compare to the competition? Could it be made more appealing?");
  346. $scoreDescriptionTooShort = __("The meta description is under 120 characters, however up to %s characters are available. %s");
  347. $scoreDescriptionTooLong = __("The specified meta description is over %s characters, reducing it will ensure the entire description is visible. %s");
  348. $scoreDescriptionMissing = __("No meta description has been specified, search engines will display copy from the page instead.");
  349. $scoreDescriptionKeywordIn = __("The meta description contains the primary keyword / phrase.");
  350. $scoreDescriptionKeywordMissing = __("A meta description has been specified, but it does not contain the target keyword / phrase.");
  351. $metaShorter = '';
  352. if ($maxlength != 155)
  353. $metaShorter = __("The available space is shorter than the usual 155 characters because Google will also include the publication date in the snippet.");
  354. if ( $description == "" ) {
  355. $this->SaveScoreResult($results,1,$scoreDescriptionMissing);
  356. } else {
  357. $length = $statistics->text_length( $description );
  358. if ($length < $scoreDescriptionMinLength)
  359. $this->SaveScoreResult( $results, 6, sprintf($scoreDescriptionTooShort, $maxlength, $metaShorter) );
  360. else if ($length <= $maxlength)
  361. $this->SaveScoreResult( $results, 9, $scoreDescriptionCorrectLength);
  362. else
  363. $this->SaveScoreResult( $results, 6, sprintf($scoreDescriptionTooLong, $maxlength, $metaShorter) );
  364. // TODO MA Keyword/Title matching is exact match with separators removed, but should extend to distributed match
  365. $haystack1 = $this->strip_separators_and_fold($description,true);
  366. $haystack2 = $this->strip_separators_and_fold($description,false);
  367. if (strrpos($haystack1,$job["keyword_folded"])===false && strrpos($haystack2,$job["keyword_folded"])===false)
  368. $this->SaveScoreResult($results,3,$scoreDescriptionKeywordMissing);
  369. else
  370. $this->SaveScoreResult($results,9,$scoreDescriptionKeywordIn);
  371. }
  372. }
  373. function ScoreBody($job, &$results, $body, $firstp, $statistics) {
  374. $scoreBodyGoodLimit = 300;
  375. $scoreBodyPoorLimit = 100;
  376. $scoreBodyGoodLength = __("There are %d words contained in the body copy, this is greater than the 300 word recommended minimum.");
  377. $scoreBodyPoorLength = __("There are %d words contained in the body copy, this is below the 300 word recommended minimum. Add more useful content on this topic for readers.");
  378. $scoreBodyBadLength = __("There are %d words contained in the body copy. This is far too low and should be increased.");
  379. $scoreKeywordDensityLow = __("The keyword density is %s%%, which is a bit low, the keyword was found %s times.");
  380. $scoreKeywordDensityHigh = __("The keyword density is %s%%, which is over the advised 5.5%% maximum, the keyword was found %s times.");
  381. $scoreKeywordDensityGood = __("The keyword density is %s%%, which is great, the keyword was found %s times.");
  382. $scoreFirstParagraphLow = __("The keyword doesn't appear in the first paragraph of the copy, make sure the topic is clear immediately.");
  383. $scoreFirstParagraphHigh = __("The keyword appears in the first paragraph of the copy.");
  384. $fleschurl = '<a href="http://en.wikipedia.org/wiki/Flesch-Kincaid_readability_test#Flesch_Reading_Ease">'.__('Flesch Reading Ease').'</a>';
  385. $scoreFlesch = __("The copy scores %s in the %s test, which is considered %s to read. %s");
  386. // Copy length check
  387. $wordCount = $statistics->word_count( $body );
  388. if ( $wordCount < $scoreBodyPoorLimit )
  389. $this->SaveScoreResult( $results, 1, sprintf( $scoreBodyBadLength, $wordCount ) );
  390. else if ( $wordCount < $scoreBodyGoodLimit )
  391. $this->SaveScoreResult( $results, 5, sprintf( $scoreBodyPoorLength, $wordCount ) );
  392. else
  393. $this->SaveScoreResult( $results, 9, sprintf( $scoreBodyGoodLength, $wordCount ) );
  394. $body = wpseo_strtolower_utf8( $body );
  395. // Keyword Density check
  396. if ( $wordCount > 0 ) {
  397. $keywordCount = preg_match_all("/".$job["keyword"]."/msiU", $body, $res);
  398. $keywordWordCount = str_word_count( $job["keyword"] );
  399. $keywordDensity = number_format( ( ($keywordCount / ($wordCount - (($keywordCount -1) * $keywordWordCount))) * 100 ) , 2 );
  400. }
  401. if ( $keywordDensity < 1 ) {
  402. $this->SaveScoreResult( $results, 4, sprintf( $scoreKeywordDensityLow, $keywordDensity, $keywordCount ) );
  403. } else if ( $keywordDensity > 5.5 ) {
  404. $this->SaveScoreResult( $results, 1, sprintf( $scoreKeywordDensityHigh, $keywordDensity, $keywordCount ) );
  405. } else {
  406. $this->SaveScoreResult( $results, 9, sprintf( $scoreKeywordDensityGood, $keywordDensity, $keywordCount ) );
  407. }
  408. $firstp = wpseo_strtolower_utf8( $firstp );
  409. // First Paragraph Test
  410. if ( stripos( $firstp, $job["keyword"] ) === false && strpos( $firstp, $job["keyword_folded"] ) === false ) {
  411. $this->SaveScoreResult( $results, 3, $scoreFirstParagraphLow );
  412. } else {
  413. $this->SaveScoreResult( $results, 9, $scoreFirstParagraphHigh );
  414. }
  415. $lang = get_bloginfo('language');
  416. if ( substr($lang, 0, 2) == 'en' ) {
  417. // Flesch Reading Ease check
  418. $flesch = $statistics->flesch_kincaid_reading_ease($body);
  419. $note = '';
  420. if ( $flesch >= 90 ) {
  421. $level = __('very easy');
  422. $score = 9;
  423. } else if ( $flesch >= 80 ) {
  424. $level = __('easy');
  425. $score = 8;
  426. } else if ( $flesch >= 70 ) {
  427. $level = __('fairly easy');
  428. $score = 7;
  429. } else if ( $flesch >= 60 ) {
  430. $level = __('OK');
  431. $score = 7;
  432. } else if ( $flesch >= 50 ) {
  433. $level = __('fairly difficult');
  434. $note = __('Try to make shorter sentences to improve readability.');
  435. $score = 6;
  436. } else if ( $flesch >= 30 ) {
  437. $level = __('difficult');
  438. $note = __('Try to make shorter sentences, using less difficult words to improve readability.');
  439. $score = 5;
  440. } else if ( $flesch >= 0 ) {
  441. $level = __('very difficult');
  442. $note = __('Try to make shorter sentences, using less difficult words to improve readability.');
  443. $score = 4;
  444. }
  445. $this->SaveScoreResult( $results, $score, sprintf( $scoreFlesch, $flesch, $fleschurl, $level, $note ) );
  446. }
  447. }
  448. function GetBody( $post ) {
  449. // Strip shortcodes, for obvious reasons
  450. $origHtml = wpseo_strip_shortcode( $post->post_content );
  451. if ( trim( $origHtml ) == '' )
  452. return '';
  453. $htmdata2 = preg_replace( "/\n|\r/"," ",$origHtml );
  454. if ( $htmdata2 == null )
  455. $htmdata2 = $origHtml;
  456. else
  457. unset( $origHtml );
  458. $htmdata3 = preg_replace( "/<(\x20*script|script).*?(\/>|\/script>)/", "", $htmdata2 );
  459. if ( $htmdata3 == null)
  460. $htmdata3 = $htmdata2;
  461. else
  462. unset( $htmdata2 );
  463. $htmdata4 = preg_replace( "/<!--.*?-->/", "", $htmdata3 );
  464. if ( $htmdata4 == null )
  465. $htmdata4 = $htmdata3;
  466. else
  467. unset( $htmdata3 );
  468. $htmdata5 = preg_replace( "/<(\x20*style|style).*?(\/>|\/style>)/", "", $htmdata4 );
  469. if ( $htmdata5 == null)
  470. $htmdata5 = $htmdata4;
  471. else
  472. unset( $htmdata4 );
  473. return $htmdata5;
  474. }
  475. function GetFirstParagraph( $post ) {
  476. // To determine the first paragraph we first need to autop the content, then match the first paragraph and return.
  477. preg_match( '/<p>(.*)<\/p>/', wpautop( $post->post_content ), $matches );
  478. return $matches[1];
  479. }
  480. }