PageRenderTime 70ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/tagclouds.php

https://github.com/danielrusciuga/phpld-simple-tagcloud
PHP | 870 lines | 394 code | 117 blank | 359 comment | 41 complexity | 48a85bdc8fa90e3fe78fcd10d67f836c MD5 | raw file
  1. <?php
  2. /**
  3. #########################################################################################################
  4. # Project: PHP Link Directory - Link exchange directory @ http://www.phplinkdirectory.com/
  5. # Module: phpLD Simple TagCloud
  6. # Homepage: http://www.frozenminds.com/phpld-tagclouds.html
  7. # Author: Constantin Bejenaru aKa Boby @ http://www.frozenminds.com/
  8. # Language: PHP + xHTML + CSS
  9. # License: GPL @ http://www.gnu.org/copyleft/gpl.html
  10. # Version: 1.3
  11. # Notice: Please maintain this section
  12. #########################################################################################################
  13. # Copyright (c) 2006-2009 Constantin Bejenaru - http://www.frozenminds.com/
  14. #
  15. # This program is free software; you can redistribute it and/or
  16. # modify it under the terms of the GNU General Public License
  17. # as published by the Free Software Foundation; either version 2
  18. # of the License, or (at your option) any later version.
  19. #
  20. # This program is distributed in the hope that it will be useful,
  21. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. # GNU General Public License for more details.
  24. #
  25. # You should have received a copy of the GNU General Public License
  26. # along with this program; if not, write to the Free Software
  27. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  28. #########################################################################################################
  29. */
  30. class phpld_tagclouds
  31. {
  32. /**
  33. * Array with words and word count
  34. *
  35. * @var array
  36. */
  37. protected $_tags = array ();
  38. /**
  39. * String of words
  40. *
  41. * @var string
  42. */
  43. protected $_tags_string = '';
  44. /**
  45. * Maximum allowed words (most important selected, the rest is removed)
  46. *
  47. * @var integer (zero or NULL means infinite)
  48. */
  49. protected $_maxWordsCount = 20;
  50. /**
  51. * Minimum occurrences of a word to be displayed
  52. *
  53. * @var integer
  54. */
  55. protected $_minCountValue = 0;
  56. /**
  57. * Minimum length of word to be displayed
  58. *
  59. * @var integer
  60. */
  61. protected $_minWordLen = 2;
  62. /**
  63. * Maximum length of a word to be displayed
  64. *
  65. * @var integer
  66. */
  67. protected $_maxWordLen = 30;
  68. /**
  69. * Minimum font size of a tag to be displayed
  70. *
  71. * @var integer
  72. */
  73. protected $_minFontSize = 30;
  74. /**
  75. * Maximum font size of a tag to be displayed
  76. *
  77. * @var integer
  78. */
  79. protected $_maxFontSize = 400;
  80. /**
  81. * Maximum number of style class to use
  82. *
  83. * @var integer
  84. */
  85. protected $_styleclass = 5;
  86. /**
  87. * Sort method
  88. * (alphabetical, count, random)
  89. *
  90. * @var string
  91. */
  92. protected $_sort = 'alphabetical';
  93. /**
  94. * Filter numeric words
  95. *
  96. * @var boolean
  97. */
  98. protected $_filter_numeric = TRUE;
  99. /**
  100. * Normalize tag cloud
  101. *
  102. * @var boolean
  103. */
  104. protected $_normalize = TRUE;
  105. /**
  106. * Words to not be displayed
  107. *
  108. * @var array
  109. */
  110. protected $_exclude_words = array ();
  111. /**
  112. * Change search URL variable
  113. *
  114. * @var string
  115. */
  116. protected $_searchVariable = 'search';
  117. /**
  118. * Tagclouds title
  119. *
  120. * @var string
  121. */
  122. protected $_cloudsTitle = '';
  123. /**
  124. * Encoding
  125. *
  126. * @var string
  127. */
  128. protected $_encoding = 'UTF-8';
  129. /**
  130. * Full HTML output
  131. *
  132. * @var string
  133. */
  134. protected $output = '';
  135. /**
  136. * Build tags output and return it
  137. *
  138. * @return string
  139. */
  140. public function get_tagclouds ()
  141. {
  142. //Detect current encoding
  143. $this->_encoding = mb_strtoupper (mb_detect_encoding ($this->_tags_string, 'auto', TRUE));
  144. //Convert to UTF-8 if needed
  145. if ('UTF-8' !== $this->_encoding)
  146. {
  147. $this->_tags_string = $this->toUTF8 ($this->_tags_string);
  148. //Detect new encoding
  149. $this->_encoding = mb_strtoupper (mb_detect_encoding ($this->_tags_string, 'auto', TRUE));
  150. }
  151. //Clean string
  152. $this->_tags_string = $this->clean_tagstring ($this->_tags_string);
  153. //Build array of tags from string
  154. $this->build_tags_array ($this->_tags_string);
  155. //Build the output
  156. $this->build_output ();
  157. //Make output
  158. return $this->output;
  159. }
  160. /**
  161. * Define maximum number of allowed tags
  162. *
  163. * @param integer (zero or NULL mean infinite)
  164. */
  165. public function maxWordsCount ($val)
  166. {
  167. //Validate and save value
  168. if (empty ($val))
  169. {
  170. $this->_maxWordsCount = null;
  171. }
  172. $this->_maxWordsCount = is_numeric ($val) ? intval ($val) : $this->_minCountValue;
  173. }
  174. /**
  175. * Change minimum occurrences of a word to be displayed
  176. *
  177. * @param integer
  178. */
  179. public function mincount ($val)
  180. {
  181. //Validate and save value
  182. $this->_minCountValue = is_numeric ($val) ? intval ($val) : $this->_minCountValue;
  183. }
  184. /**
  185. * Change minimum length of a word to be displayed
  186. *
  187. * @param integer
  188. */
  189. public function minWordLength ($val)
  190. {
  191. //Validate and save value
  192. $this->_minWordLen = is_numeric ($val) ? intval ($val) : $this->_minWordLen;
  193. }
  194. /**
  195. * Change maximum length of a word to be displayed
  196. *
  197. * @param integer
  198. */
  199. public function maxWordLength ($val)
  200. {
  201. //Validate and save value
  202. $this->_maxWordLen = is_numeric ($val) ? intval ($val) : $this->_maxWordLen;
  203. }
  204. /**
  205. * Change minimum font size of a tag to be displayed
  206. *
  207. * @param integer
  208. */
  209. public function minfontsize ($val)
  210. {
  211. //Validate and save value
  212. $this->_minFontSize = is_numeric ($val) ? intval ($val) : $this->_minFontSize;
  213. }
  214. /**
  215. * Change maximum font size of a tag to be displayed
  216. *
  217. * @param integer
  218. */
  219. public function maxfontsize ($val)
  220. {
  221. //Validate and save value
  222. $this->_maxFontSize = is_numeric ($val) ? intval ($val) : $this->_maxFontSize;
  223. }
  224. /**
  225. * Define maximum nuber for element class (CSS styling)
  226. *
  227. * @param integer
  228. */
  229. public function styleclass ($val)
  230. {
  231. //Validate and save value
  232. $this->_styleclass = is_numeric ($val) ? intval ($val) : $this->_styleclass;
  233. }
  234. /**
  235. * Maximum number of style class to use
  236. *
  237. * @param integer
  238. */
  239. public function sortmethod ($val)
  240. {
  241. //Correct value
  242. $val = strtolower (trim ($val));
  243. //Define supported methods for sorting
  244. $allowedSortMethods = array (
  245. 'alphabetical' ,
  246. 'count' ,
  247. 'random'
  248. );
  249. //Validate and save value
  250. $this->_sort = in_array ($val, $allowedSortMethods) ? $val : 'alphabetical';
  251. }
  252. /**
  253. * Change search URL variable
  254. *
  255. * @param string
  256. */
  257. public function setsearchvariable ($variable = 'search')
  258. {
  259. //Save value
  260. $this->_searchVariable = trim ($variable);
  261. }
  262. /**
  263. * Filter numeric words
  264. *
  265. * @param boolean
  266. */
  267. public function filter_numeric ($filter = TRUE)
  268. {
  269. $this->_filter_numeric = (boolean) $filter;
  270. }
  271. /**
  272. * Normalize tag cloud
  273. *
  274. * @param boolean
  275. */
  276. public function normalize ($normalize = TRUE)
  277. {
  278. $this->_normalize = (boolean) $normalize;
  279. }
  280. /**
  281. * Change title for tagclouds
  282. *
  283. * @param string
  284. */
  285. public function title ($title = '')
  286. {
  287. //Save value
  288. $this->_cloudsTitle = trim ($title);
  289. }
  290. /**
  291. * Add more words to not be displayed
  292. *
  293. * @param mixed (Array or string)
  294. * @return boolean TRUE on success, FALSE on error
  295. */
  296. public function exclude ($exclude)
  297. {
  298. if (empty ($exclude))
  299. {
  300. //Nothing to do
  301. return FALSE;
  302. }
  303. else
  304. {
  305. if (is_array ($exclude)) //If passed variable is an array, process it as an array
  306. {
  307. //Merge with existing exclusion words array
  308. $this->_exclude_words = array_merge ($this->_exclude_words, $exclude);
  309. }
  310. elseif (is_string ($exclude)) //If passed variable is a string, process it as string
  311. {
  312. //Clean string
  313. $exclude = $this->strip_specialchars ($exclude);
  314. //Convert string to array
  315. $exclude = explode (' ', $exclude);
  316. //Merge with existing exclusion words array
  317. if (is_array ($exclude) && ! empty ($exclude))
  318. {
  319. $this->_exclude_words = array_merge ($this->_exclude_words, $exclude);
  320. }
  321. }
  322. }
  323. return TRUE;
  324. }
  325. /**
  326. * Process categories
  327. *
  328. * @param mixed (Array or string)
  329. * @return boolean TRUE on success, FALSE on error
  330. */
  331. public function add_categories ($categories)
  332. {
  333. if (empty ($categories))
  334. {
  335. //Nothing to do
  336. return FALSE;
  337. }
  338. else
  339. {
  340. //If passed variable is an array, process it as an array
  341. if (is_array ($categories))
  342. {
  343. //Process each category at a time
  344. foreach ($categories as $key => $categ)
  345. {
  346. //Add category title text
  347. if (! empty ($categ['TITLE']))
  348. {
  349. $this->_tags_string .= ' ' . $categ['TITLE'];
  350. }
  351. //Add category description text
  352. if (! empty ($categ['DESCRIPTION']))
  353. {
  354. $this->_tags_string .= ' ' . $categ['DESCRIPTION'];
  355. }
  356. //Process subdirectories if available
  357. if (isset ($categ['SUBCATS']) && is_array ($categ['SUBCATS']) && ! empty ($categ['SUBCATS']))
  358. {
  359. //Process each subcategory at a time
  360. foreach ($categ['SUBCATS'] as $subcateg)
  361. {
  362. //Add subcategory title text
  363. if (! empty ($subcateg['TITLE']))
  364. {
  365. $this->_tags_string .= ' ' . $subcateg['TITLE'];
  366. }
  367. //Add subcategory description text
  368. if (! empty ($subcateg['DESCRIPTION']))
  369. {
  370. $this->_tags_string .= ' ' . $subcateg['DESCRIPTION'];
  371. }
  372. //Free memory
  373. unset ($subcateg, $categ['SUBCATS'][$key]);
  374. }
  375. }
  376. //Free memory
  377. unset ($categ, $categories[$key]);
  378. }
  379. }
  380. elseif (is_string ($categories)) //If passed variable is a string, process it as string
  381. {
  382. //Add category text
  383. $this->_tags_string .= ' ' . $categories;
  384. }
  385. //Free memory
  386. unset ($categories);
  387. }
  388. return TRUE;
  389. }
  390. /**
  391. * Process links
  392. *
  393. * @param mixed (Array or string)
  394. * @return boolean TRUE on success, FALSE on error
  395. */
  396. public function add_links ($links)
  397. {
  398. if (empty ($links))
  399. {
  400. //Nothing to do
  401. return FALSE;
  402. }
  403. else
  404. {
  405. //If passed variable is an array, process it as an array
  406. if (is_array ($links))
  407. {
  408. //Process each link at a time
  409. foreach ($links as $key => $link)
  410. {
  411. //Add link title text
  412. if (! empty ($link['TITLE']))
  413. {
  414. $this->_tags_string .= ' ' . $link['TITLE'];
  415. }
  416. //Add link description text
  417. if (! empty ($link['DESCRIPTION']))
  418. {
  419. $this->_tags_string .= ' ' . $link['DESCRIPTION'];
  420. }
  421. //Free memory
  422. unset ($link, $links[$key]);
  423. }
  424. }
  425. elseif (is_string ($links)) //If passed variable is a string, process it as string
  426. {
  427. //Add link text
  428. $this->_tags_string .= ' ' . $links;
  429. }
  430. //Free memory
  431. unset ($links);
  432. }
  433. return TRUE;
  434. }
  435. /**
  436. * Process articles
  437. *
  438. * @param mixed (Array or string)
  439. * @return boolean TRUE on success, FALSE on error
  440. */
  441. function add_articles ($articles)
  442. {
  443. if (empty ($articles))
  444. {
  445. //Nothing to do
  446. return FALSE;
  447. }
  448. else
  449. {
  450. //If passed variable is an array, process it as an array
  451. if (is_array ($articles))
  452. {
  453. //Process each link at a time
  454. foreach ($articles as $key => $article)
  455. {
  456. //Add article title text
  457. if (! empty ($article['TITLE']))
  458. {
  459. $this->_tags_string .= ' ' . $article['TITLE'];
  460. }
  461. //Add article body text
  462. if (! empty ($article['ARTICLE']))
  463. {
  464. $this->_tags_string .= ' ' . $article['ARTICLE'];
  465. }
  466. //Add article description text
  467. if (! empty ($article['DESCRIPTION']))
  468. {
  469. $this->_tags_string .= ' ' . $article['DESCRIPTION'];
  470. }
  471. //Free memory
  472. unset ($article, $articles[$key]);
  473. }
  474. }
  475. elseif (is_string ($article)) //If passed variable is a string, process it as string
  476. {
  477. //Add link text
  478. $this->_tags_string .= ' ' . $article;
  479. }
  480. //Free memory
  481. unset ($article);
  482. }
  483. return TRUE;
  484. }
  485. /**
  486. * Clean tag string of unneeded characters
  487. * (This will also escape the string)
  488. *
  489. * @param string
  490. * @return string
  491. */
  492. protected function clean_tagstring ($string = '')
  493. {
  494. //Remove HTML and PHP tags
  495. $string = strip_tags ($string);
  496. //Strip whitespace
  497. $string = $this->strip_specialchars ($string);
  498. //Convert special characters to HTML entities
  499. $string = $this->escape ($string);
  500. //Make lowercase and trim
  501. $string = mb_strtolower ($string, $this->_encoding);
  502. return trim ($string);
  503. }
  504. /**
  505. * Clean string of whitespace and special characters
  506. *
  507. * @param string
  508. * @return string
  509. */
  510. protected function strip_specialchars ($string = '')
  511. {
  512. //Handle whitespace
  513. $string = str_replace ("&nbsp;", " ", $string); //Windows
  514. $string = str_replace ("\r\n", " ", $string); //Windows
  515. $string = str_replace ("\r", " ", $string); //Mac
  516. $string = str_replace ("\n", " ", $string); //*NIX
  517. $string = str_replace ("\t", " ", $string); //TAB
  518. $string = str_replace ("\0", "", $string); //NULL BYTE
  519. $string = str_replace ("\x0B", "", $string); //Vertical TAB
  520. //Remove anything except word, digit characters and spaces, at the end remove multiple spaces
  521. $pattern = array (
  522. '#[\'"/\.,_\-()!?@\#$%^&*+:;=`<>\\\]#i' , //special chars
  523. '#[\s]{2,}#' //multiple white-spaces
  524. );
  525. $string = preg_replace ($pattern, ' ', $string);
  526. return trim ($string);
  527. }
  528. /**
  529. * Convert special characters to HTML entities
  530. *
  531. * @param string String to be escaped
  532. * @return string Escaped string
  533. */
  534. protected function escape ($string = '')
  535. {
  536. return htmlspecialchars ($string, ENT_COMPAT, $this->_encoding);
  537. }
  538. /**
  539. * Converts strings to UTF-8 via iconv. NB, the result may not by UTF-8 if the conversion failed.
  540. *
  541. * This file comes from Prado (BSD License)
  542. *
  543. * @param string $string string to convert to UTF-8
  544. * @param string $from current encoding
  545. * @return string UTF-8 encoded string, original string if iconv failed
  546. */
  547. protected function toUTF8 ($string, $from)
  548. {
  549. $from = mb_strtoupper ($from);
  550. if ($from !== 'UTF-8')
  551. {
  552. // to UTF-8
  553. $s = iconv ($from, 'UTF-8', $string);
  554. // it could return FALSE
  555. return $s !== FALSE ? $s : $string;
  556. }
  557. return $string;
  558. }
  559. /**
  560. * Normalizes a tag cloud, ie. changes a (tag => weight) array into a
  561. * (tag => normalized_weight) one.
  562. * Normalized weights range from -2 to 2.
  563. *
  564. * @copyright 2007 Xavier Lacot (sfPropelActAsTaggableBehaviorPlugin Plugin for Symfony project)
  565. * @copyright 2007 Michael Nolan (sfPropelActAsTaggableBehaviorPlugin Plugin for Symfony project)
  566. *
  567. * @deprecated
  568. *
  569. * @param array $tag_cloud
  570. * @return array
  571. */
  572. protected function normalize_tag_cloud ($tag_cloud)
  573. {
  574. //Make sure tag cloud array is an array
  575. $tag_cloud = (array) $tag_cloud;
  576. $tags = array ();
  577. $levels = 5;
  578. $power = 0.7;
  579. if (count ($tag_cloud) > 0)
  580. {
  581. $max_count = max ($tag_cloud);
  582. $min_count = min ($tag_cloud);
  583. $max = intval ($levels / 2);
  584. if ($max_count != 0)
  585. {
  586. foreach ($tag_cloud as $tag => $count)
  587. {
  588. $tag = (string) $tag;
  589. $count = (int) $count;
  590. $tags[$tag] = (int) round (.9999 * $levels * (pow ($count / $max_count, $power) - .5), 0);
  591. }
  592. }
  593. }
  594. return (array) $tags;
  595. }
  596. /**
  597. * Build array with tags
  598. *
  599. * @param string
  600. * @return boolean TRUE on success, FALSE on error (=empty tags string)
  601. */
  602. protected function build_tags_array ($string = '')
  603. {
  604. if (empty ($string))
  605. {
  606. return FALSE;
  607. }
  608. else
  609. {
  610. //Split string into array
  611. $array = explode (' ', $string);
  612. //Drop empty values
  613. $array = array_filter ($array);
  614. //Count words
  615. $array_content = array_count_values ($array);
  616. //Process each word at a time
  617. foreach ($array_content as $word => $count)
  618. {
  619. //Check if word is numeric
  620. if (TRUE === $this->_filter_numeric && is_numeric ($word))
  621. {
  622. continue;
  623. }
  624. //Check minimum count value
  625. if ($count < $this->_minCountValue)
  626. {
  627. continue;
  628. }
  629. //Check word length
  630. if (mb_strlen ($word) < $this->_minWordLen || mb_strlen ($word) > $this->_maxWordLen)
  631. {
  632. continue;
  633. }
  634. //Check if in exclude words
  635. if (in_array ($word, $this->_exclude_words))
  636. {
  637. continue;
  638. }
  639. //Word is OK, add to new array
  640. $this->_tags[$word] = $count;
  641. //Free memory
  642. unset ($count, $array_content[$word]);
  643. }
  644. //Check if we need to normalize tag cloud
  645. if (TRUE === $this->_normalize)
  646. {
  647. //Normalize
  648. //$this->_tags = $this->normalize_tag_cloud ($this->_tags);
  649. }
  650. //Sort in reverse order (highest counts first)
  651. //Sortmethod = count
  652. arsort ($this->_tags);
  653. if (! empty ($this->_maxWordsCount))
  654. {
  655. //Truncate array to maximum allowed words count
  656. $this->_tags = array_slice ($this->_tags, 0, $this->_maxWordsCount, TRUE);
  657. }
  658. switch ($this->_sort)
  659. {
  660. case 'alphabetical':
  661. //Sort words alphabetically
  662. ksort ($this->_tags, SORT_STRING);
  663. break;
  664. case 'random':
  665. //Shuffle arrray (random order)
  666. //Workaround to shuffle array because the "shuffle()" function
  667. //assigns new keys for the elements in array
  668. $keys = array_keys ($this->_tags);
  669. shuffle ($keys);
  670. $newTags = array ();
  671. foreach ($keys as $key)
  672. {
  673. $newTags[$key] = $this->_tags[$key];
  674. }
  675. $this->_tags = $newTags;
  676. unset ($newTags, $keys);
  677. break;
  678. default:
  679. //Nothing to do, array is already sorted by words count
  680. break;
  681. }
  682. }
  683. return TRUE;
  684. }
  685. /**
  686. * Build HTML output
  687. *
  688. * @param string
  689. * @return boolean TRUE on success ($output populated), FALSE on error (no tags/words available)
  690. */
  691. protected function build_output ()
  692. {
  693. if (! is_array ($this->_tags) || empty ($this->_tags))
  694. {
  695. //No tags/word, return empty output
  696. $this->output = '';
  697. return FALSE;
  698. }
  699. else
  700. {
  701. //Determine maximum occurrence value
  702. $maxHits = max ($this->_tags);
  703. //Determine minimum and maximum occurences
  704. $minOccurs = min ($this->_tags);
  705. $maxOccurs = max ($this->_tags);
  706. //Add module details (please keep this)
  707. $this->add_module_details ();
  708. //Add tagclouds box
  709. $this->output .= '<div class="tagclouds">';
  710. //Add a title if available
  711. if (! empty ($this->_cloudsTitle))
  712. {
  713. $this->output .= $this->_cloudsTitle;
  714. }
  715. //Add the tagclouds paragraph
  716. $this->output .= '<p>';
  717. //Process each tag
  718. foreach ($this->_tags as $word => $count)
  719. {
  720. //Calculate font-size for current tag
  721. $weight = (log ($count) - log ($minOccurs)) / (log ($maxOccurs) - log ($minOccurs));
  722. $fontSize = intval ($this->_minFontSize + round (($this->_maxFontSize - $this->_minFontSize) * $weight));
  723. //Calculate style class (usually for CSS colors)
  724. $styleClass = ceil (((($count - $this->_minCountValue) * 100) / ($maxHits - $this->_minCountValue)) / (100 / $this->_styleclass));
  725. //Add word to output
  726. $this->output .= '<a href="' . DOC_ROOT . '/index.php?' . $this->_searchVariable . '=' . $word . '" style="font-size:' . $fontSize . '%;" class="cloud-word cloud-style-' . $styleClass . '" title="' . $word . '">' . $word . '</a> ';
  727. //Free memory
  728. unset ($this->_tags[$word], $word, $count, $fontSize, $styleClass);
  729. }
  730. //Trim output (last char is a space)
  731. $this->output = trim ($this->output);
  732. //Close paragraph and box
  733. $this->output .= '</p></div>';
  734. }
  735. return TRUE;
  736. }
  737. /**
  738. * Build HTML module information output
  739. *
  740. * (!! please maintain this section !!)
  741. */
  742. public function add_module_details ()
  743. {
  744. $this->output = "\n<!--";
  745. $this->output .= "\n# Project: PHP Link Directory - Link exchange directory @ http://www.phplinkdirectory.com/";
  746. $this->output .= "\n# Module: phpLD Simple TagCloud";
  747. $this->output .= "\n# Homepage: http://www.frozenminds.com/phpld-tagclouds.html";
  748. $this->output .= "\n# Author: Constantin Bejenaru aKa Boby @ http://www.frozenminds.com/";
  749. $this->output .= "\n# Language: PHP + xHTML + CSS";
  750. $this->output .= "\n# License: GNU/GPL";
  751. $this->output .= "\n-->\n";
  752. }
  753. }