/server/placeholder.class.php

https://github.com/hustcc/placeholder.js · PHP · 357 lines · 213 code · 28 blank · 116 comment · 48 complexity · 07e115ad44a443b52bc034e73db2b575 MD5 · raw file

  1. <?php
  2. if (strpos(__FILE__, $_SERVER['PHP_SELF'])) { header('HTTP/1.0 403 Forbidden'); exit; }
  3. /**
  4. * Creates temporary placeholder images
  5. *
  6. * @package Placeholder
  7. * @version 1.1.1
  8. * @link http://github.com/img-src/placeholder
  9. */
  10. class Placeholder {
  11. private $backgroundColor, $cache, $cacheDir, $expires, $font, $height, $maxHeight, $maxWidth, $textColor, $width;
  12. function __construct()
  13. {
  14. $this->backgroundColor = 'cccccc';
  15. $this->cache = false;
  16. $this->cacheDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache';
  17. $this->expires = 604800;
  18. $this->font = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'consolaz.ttf';
  19. $this->maxHeight = 2000;
  20. $this->maxWidth = 2000;
  21. $this->textColor = '969696';
  22. }
  23. /**
  24. * Sets background color
  25. *
  26. * @param string $hex Hex code value
  27. * @throws InvalidArgumentException
  28. */
  29. function setBackgroundColor($hex)
  30. {
  31. if (strlen($hex) === 3 || strlen($hex) === 6) {
  32. if (preg_match('/^[a-f0-9]{3}$|^[a-f0-9]{6}$/i', $hex)) {
  33. $this->backgroundColor = $hex;
  34. } else {
  35. throw new InvalidArgumentException('Background color must be a valid RGB hex code.');
  36. }
  37. } else {
  38. throw new InvalidArgumentException('Background color must be 3 or 6 character hex code.');
  39. }
  40. }
  41. /**
  42. * Gets background color
  43. */
  44. function getBackgroundColor()
  45. {
  46. return $this->backgroundColor;
  47. }
  48. /**
  49. * Sets text color
  50. *
  51. * @param string $hex Hex code value
  52. * @throws InvalidArgumentException
  53. */
  54. function setTextColor($hex)
  55. {
  56. if (strlen($hex) === 3 || strlen($hex) === 6) {
  57. if (preg_match('/^[a-f0-9]{3}$|^[a-f0-9]{6}$/i', $hex)) {
  58. $this->textColor = $hex;
  59. } else {
  60. throw new InvalidArgumentException('Text color must be a valid RGB hex code.');
  61. }
  62. } else {
  63. throw new InvalidArgumentException('Text color must be 3 or 6 character hex code.');
  64. }
  65. }
  66. /**
  67. * Gets text color
  68. */
  69. function getTextColor()
  70. {
  71. return $this->textColor;
  72. }
  73. /**
  74. * Sets location of TTF font
  75. *
  76. * @param string $fontPath Location of TTF font
  77. * @throws InvalidArgumentException
  78. */
  79. function setFont($fontPath)
  80. {
  81. if (is_readable($fontPath)) {
  82. $this->font = $fontPath;
  83. } else {
  84. throw new InvalidArgumentException('Font file must exist and be readable by web server.');
  85. }
  86. }
  87. /**
  88. * Gets location of font
  89. */
  90. function getFont()
  91. {
  92. return $this->font;
  93. }
  94. /**
  95. * Set expires header value
  96. *
  97. * @param int $expires Seconds used in expires HTTP header
  98. * @throws InvalidArgumentException
  99. */
  100. function setExpires($expires)
  101. {
  102. if (preg_match('/^\d+$/', $expires)) {
  103. $this->expires = $expires;
  104. } else {
  105. throw new InvalidArgumentException('Expires must be an integer.');
  106. }
  107. }
  108. /**
  109. * Get expires header value
  110. */
  111. function getExpires()
  112. {
  113. return $this->expires;
  114. }
  115. /**
  116. * Set maximum width allowed for placeholder image
  117. *
  118. * @param int $maxWidth Maximum width of generated image
  119. * @throws InvalidArgumentException
  120. */
  121. function setMaxWidth($maxWidth)
  122. {
  123. if (preg_match('/^\d+$/', $maxWidth)) {
  124. $this->maxWidth = $maxWidth;
  125. } else {
  126. throw new InvalidArgumentException('Maximum width must be an integer.');
  127. }
  128. }
  129. /**
  130. * Get max width value
  131. */
  132. function getMaxWidth()
  133. {
  134. return $this->maxWidth;
  135. }
  136. /**
  137. * Set maximum height allowed for placeholder image
  138. *
  139. * @param int $maxHeight Maximum height of generated image
  140. * @throws InvalidArgumentException
  141. */
  142. function setMaxHeight($maxHeight)
  143. {
  144. if (preg_match('/^\d+$/', $maxHeight)) {
  145. $this->maxHeight = $maxHeight;
  146. } else {
  147. throw new InvalidArgumentException('Maximum height must be an integer.');
  148. }
  149. }
  150. /**
  151. * Get max height value
  152. */
  153. function getMaxHeight()
  154. {
  155. return $this->maxHeight;
  156. }
  157. /**
  158. * Enable or disable cache
  159. *
  160. * @param bool $cache Whether or not to cache
  161. * @throws InvalidArgumentException
  162. */
  163. function setCache($cache)
  164. {
  165. if (is_bool($cache)) {
  166. $this->cache = $cache;
  167. } else {
  168. throw new InvalidArgumentException('setCache expects a boolean value.');
  169. }
  170. }
  171. /**
  172. * Get cache value
  173. */
  174. function getCache()
  175. {
  176. return $this->cache;
  177. }
  178. /**
  179. * Sets caching path
  180. *
  181. * @param string $cacheDir Path to cache folder, must be writable by web server
  182. * @throws InvalidArgumentException
  183. */
  184. function setCacheDir($cacheDir)
  185. {
  186. if (is_dir($cacheDir)) {
  187. $this->cacheDir = $cacheDir;
  188. } else {
  189. throw new InvalidArgumentException('setCacheDir expects a directory.');
  190. }
  191. }
  192. /**
  193. * Get cache directory value
  194. */
  195. function getCacheDir()
  196. {
  197. return $this->cacheDir;
  198. }
  199. /**
  200. * Set width of image to render
  201. *
  202. * @param int $width Width of generated image
  203. * @throws InvalidArgumentException
  204. */
  205. function setWidth($width)
  206. {
  207. if (preg_match('/^\d+$/', $width)) {
  208. if ($width > 0) {
  209. $this->width = $width;
  210. } else {
  211. throw new InvalidArgumentException('Width must be greater than zero.');
  212. }
  213. } else {
  214. throw new InvalidArgumentException('Width must be an integer.');
  215. }
  216. }
  217. /**
  218. * Get width value
  219. */
  220. function getWidth()
  221. {
  222. return $this->width;
  223. }
  224. /**
  225. * Set height of image to render
  226. *
  227. * @param int $height Height of generated image
  228. * @throws InvalidArgumentException
  229. */
  230. function setHeight($height)
  231. {
  232. if (preg_match('/^\d+$/', $height)) {
  233. if ($height > 0) {
  234. $this->height = $height;
  235. } else {
  236. throw new InvalidArgumentException('Height must be greater than zero.');
  237. }
  238. } else {
  239. throw new InvalidArgumentException('Height must be an integer.');
  240. }
  241. }
  242. /**
  243. * Get height value
  244. */
  245. function getHeight()
  246. {
  247. return $this->height;
  248. }
  249. /**
  250. * Display image and cache (if enabled)
  251. *
  252. * @throws RuntimeException
  253. */
  254. function render()
  255. {
  256. if ($this->width <= $this->maxWidth && $this->height <= $this->maxHeight) {
  257. $cachePath = $this->cacheDir . '/' . $this->width . '_' . $this->height . '_' . (strlen($this->backgroundColor) === 3 ? $this->backgroundColor[0] . $this->backgroundColor[0] . $this->backgroundColor[1] . $this->backgroundColor[1] . $this->backgroundColor[2] . $this->backgroundColor[2] : $this->backgroundColor) . '_' . (strlen($this->textColor) === 3 ? $this->textColor[0] . $this->textColor[0] . $this->textColor[1] . $this->textColor[1] . $this->textColor[2] . $this->textColor[2] : $this->textColor) . '.png';
  258. header('Content-type: image/png');
  259. header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $this->expires));
  260. header('Cache-Control: public');
  261. if ($this->cache === true && is_readable($cachePath)) {
  262. // send header identifying cache hit & send cached image
  263. header('img-src-cache: hit');
  264. print file_get_contents($cachePath);
  265. } else {
  266. // cache disabled or no cached copy exists
  267. // send header identifying cache miss if cache enabled
  268. if ($this->cache === true) header('img-src-cache: miss');
  269. $image = $this->createImage();
  270. imagepng($image);
  271. // write cache
  272. if ($this->cache === true && is_writable($this->cacheDir)) {
  273. imagepng($image, $cachePath);
  274. }
  275. imagedestroy($image);
  276. }
  277. } else {
  278. throw new RuntimeException('Placeholder size may not exceed ' . $this->maxWidth . 'x' . $this->maxHeight . ' pixels.');
  279. }
  280. }
  281. private function createImage()
  282. {
  283. $image = imagecreate($this->width, $this->height);
  284. // convert backgroundColor hex to RGB values
  285. list($bgR, $bgG, $bgB) = $this->hexToDec($this->backgroundColor);
  286. $backgroundColor = imagecolorallocate($image, $bgR, $bgG, $bgB);
  287. // convert textColor hex to RGB values
  288. list($textR, $textG, $textB) = $this->hexToDec($this->textColor);
  289. $textColor = imagecolorallocate($image, $textR, $textG, $textB);
  290. $text = $this->width . 'x' . $this->height;
  291. imagefilledrectangle($image, 0, 0, $this->width, $this->height, $backgroundColor);
  292. $fontSize = 26;
  293. $textBoundingBox = imagettfbbox($fontSize, 0, $this->font, $text);
  294. // decrease the default font size until it fits nicely within the image
  295. while (((($this->width - ($textBoundingBox[2] - $textBoundingBox[0])) < 10) || (($this->height - ($textBoundingBox[1] - $textBoundingBox[7])) < 10)) && ($fontSize > 1)) {
  296. $fontSize--;
  297. $textBoundingBox = imagettfbbox($fontSize, 0, $this->font, $text);
  298. }
  299. imagettftext($image, $fontSize, 0, ($this->width / 2) - (($textBoundingBox[2] - $textBoundingBox[0]) / 2), ($this->height / 2) + (($textBoundingBox[1] - $textBoundingBox[7]) / 2), $textColor, $this->font, $text);
  300. return $image;
  301. }
  302. function renderToFile($file)
  303. {
  304. if (!file_exists($file)) {
  305. touch($file);
  306. }
  307. $image = $this->createImage();
  308. imagepng($image, $file);
  309. imagedestroy($image);
  310. }
  311. /**
  312. * Convert hex code to array of RGB decimal values
  313. *
  314. * @param string $hex Hex code to convert to dec
  315. * @return array
  316. * @throws InvalidArgumentException
  317. */
  318. private function hexToDec($hex)
  319. {
  320. if (strlen($hex) === 3) {
  321. $rgbArray = array(hexdec($hex[0] . $hex[0]), hexdec($hex[1] . $hex[1]), hexdec($hex[2] . $hex[2]));
  322. } else if (strlen($hex) === 6) {
  323. $rgbArray = array(hexdec($hex[0] . $hex[1]), hexdec($hex[2] . $hex[3]), hexdec($hex[4] . $hex[5]));
  324. } else {
  325. throw new InvalidArgumentException('Could not convert hex value to decimal.');
  326. }
  327. return $rgbArray;
  328. }
  329. }