PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/timber-image.php

https://gitlab.com/aristath/timber
PHP | 528 lines | 240 code | 43 blank | 245 comment | 58 complexity | e1758021893c9fa1d4c9226c2148a7ce MD5 | raw file
  1. <?php
  2. /**
  3. * If TimberPost is the class you're going to spend the most time, TimberImage is the class you're going to have the most fun with.
  4. * @example
  5. * ```php
  6. * $context = Timber::get_context();
  7. * $post = new TimberPost();
  8. * $context['post'] = $post;
  9. *
  10. * // lets say you have an alternate large 'cover image' for your post stored in a custom field which returns an image ID
  11. * $cover_image_id = $post->cover_image;
  12. * $context['cover_image'] = new TimberImage($cover_image_id);
  13. * Timber::render('single.twig', $context);
  14. * ```
  15. *
  16. * ```twig
  17. * <article>
  18. * <img src="{{cover_image.src}}" class="cover-image" />
  19. * <h1 class="headline">{{post.title}}</h1>
  20. * <div class="body">
  21. * {{post.content}}
  22. * </div>
  23. *
  24. * <img src="{{ Image(post.custom_field_with_image_id).src }}" alt="Another way to initialize images as TimberImages, but within Twig" />
  25. * </article>
  26. * ```
  27. *
  28. * ```html
  29. * <article>
  30. * <img src="http://example.org/wp-content/uploads/2015/06/nevermind.jpg" class="cover-image" />
  31. * <h1 class="headline">Now you've done it!</h1>
  32. * <div class="body">
  33. * Whatever whatever
  34. * </div>
  35. * <img src="http://example.org/wp-content/uploads/2015/06/kurt.jpg" alt="Another way to initialize images as TimberImages, but within Twig" />
  36. * </article>
  37. * ```
  38. */
  39. class TimberImage extends TimberPost implements TimberCoreInterface {
  40. protected $_can_edit;
  41. protected $_dimensions;
  42. public $abs_url;
  43. /**
  44. * @var string $object_type what does this class represent in WordPress terms?
  45. */
  46. public $object_type = 'image';
  47. /**
  48. * @var string $representation what does this class represent in WordPress terms?
  49. */
  50. public static $representation = 'image';
  51. /**
  52. * @var array of supported relative file types
  53. */
  54. private $file_types = array('jpg', 'jpeg', 'png', 'svg', 'bmp', 'ico', 'gif', 'tiff', 'pdf');
  55. /**
  56. * @api
  57. * @var string $file_loc the location of the image file in the filesystem (ex: `/var/www/htdocs/wp-content/uploads/2015/08/my-pic.jpg`)
  58. */
  59. public $file_loc;
  60. public $file;
  61. /**
  62. * @api
  63. * @var integer the ID of the image (which is a WP_Post)
  64. */
  65. public $id;
  66. public $sizes = array();
  67. /**
  68. * @api
  69. * @var string $caption the string stored in the WordPress database
  70. */
  71. public $caption;
  72. /**
  73. * @var $_wp_attached_file the file as stored in the WordPress database
  74. */
  75. protected $_wp_attached_file;
  76. /**
  77. * Creates a new TimberImage object
  78. * @example
  79. * ```php
  80. * // You can pass it an ID number
  81. * $myImage = new TimberImage(552);
  82. *
  83. * //Or send it a URL to an image
  84. * $myImage = new TimberImage('http://google.com/logo.jpg');
  85. * ```
  86. * @param int|string $iid
  87. */
  88. public function __construct($iid) {
  89. $this->init($iid);
  90. }
  91. /**
  92. * @return string the src of the file
  93. */
  94. public function __toString() {
  95. if ( $this->get_src() ) {
  96. return $this->get_src();
  97. }
  98. return '';
  99. }
  100. /**
  101. * Get a PHP array with pathinfo() info from the file
  102. * @return array
  103. */
  104. function get_pathinfo() {
  105. return pathinfo($this->file);
  106. }
  107. /**
  108. * @internal
  109. * @param string $dim
  110. * @return array|int
  111. */
  112. protected function get_dimensions($dim = null) {
  113. if ( isset($this->_dimensions) ) {
  114. return $this->get_dimensions_loaded($dim);
  115. }
  116. if ( file_exists($this->file_loc) && filesize($this->file_loc) ) {
  117. list($width, $height) = getimagesize($this->file_loc);
  118. $this->_dimensions = array();
  119. $this->_dimensions[0] = $width;
  120. $this->_dimensions[1] = $height;
  121. return $this->get_dimensions_loaded($dim);
  122. }
  123. }
  124. /**
  125. * @internal
  126. * @param string|null $dim
  127. * @return array|int
  128. */
  129. protected function get_dimensions_loaded($dim) {
  130. if ( $dim === null ) {
  131. return $this->_dimensions;
  132. }
  133. if ( $dim == 'w' || $dim == 'width' ) {
  134. return $this->_dimensions[0];
  135. }
  136. if ( $dim == 'h' || $dim == 'height' ) {
  137. return $this->_dimensions[1];
  138. }
  139. return null;
  140. }
  141. /**
  142. * @internal
  143. * @param int $iid the id number of the image in the WP database
  144. */
  145. protected function get_image_info( $iid ) {
  146. $image_info = $iid;
  147. if (is_numeric($iid)) {
  148. $image_info = wp_get_attachment_metadata($iid);
  149. if (!is_array($image_info)) {
  150. $image_info = array();
  151. }
  152. $image_custom = get_post_custom($iid);
  153. $basic = get_post($iid);
  154. if ($basic) {
  155. if (isset($basic->post_excerpt)) {
  156. $this->caption = $basic->post_excerpt;
  157. }
  158. $image_custom = array_merge($image_custom, get_object_vars($basic));
  159. }
  160. return array_merge($image_info, $image_custom);
  161. }
  162. if (is_array($image_info) && isset($image_info['image'])) {
  163. return $image_info['image'];
  164. }
  165. if (is_object($image_info)) {
  166. return get_object_vars($image_info);
  167. }
  168. return $iid;
  169. }
  170. /**
  171. * @internal
  172. * @param string $url for evaluation
  173. * @return string with http/https corrected depending on what's appropriate for server
  174. */
  175. protected static function _maybe_secure_url($url) {
  176. if ( is_ssl() && strpos($url, 'https') !== 0 && strpos($url, 'http') === 0 ) {
  177. $url = 'https' . substr($url, strlen('http'));
  178. }
  179. return $url;
  180. }
  181. public static function wp_upload_dir() {
  182. static $wp_upload_dir = false;
  183. if ( !$wp_upload_dir ) {
  184. $wp_upload_dir = wp_upload_dir();
  185. }
  186. return $wp_upload_dir;
  187. }
  188. /**
  189. * @internal
  190. * @param int $iid
  191. */
  192. function init( $iid = false ) {
  193. if(!$iid) { TimberHelper::error_log('Initalized TimberImage without providing first parameter.'); return; }
  194. if ( !is_numeric( $iid ) && is_string( $iid ) ) {
  195. if (strstr($iid, '://')) {
  196. $this->init_with_url($iid);
  197. return;
  198. }
  199. if ( strstr($iid, ABSPATH) ) {
  200. $this->init_with_file_path($iid);
  201. return;
  202. }
  203. $relative = false;
  204. $iid_lower = strtolower($iid);
  205. foreach( $this->file_types as $type ) { if( strstr( $iid_lower, $type ) ) { $relative = true; break; } };
  206. if ( $relative ) {
  207. $this->init_with_relative_path( $iid );
  208. return;
  209. }
  210. } else if ( $iid instanceof WP_Post ) {
  211. $ref = new ReflectionClass($this);
  212. $post = $ref->getParentClass()->newInstance($iid->ID);
  213. if (isset($post->_thumbnail_id) && $post->_thumbnail_id) {
  214. return $this->init((int) $post->_thumbnail_id);
  215. }
  216. return $this->init($iid->ID);
  217. } else if ( $iid instanceof TimberPost ) {
  218. /**
  219. * This will catch TimberPost and any post classes that extend TimberPost,
  220. * see http://php.net/manual/en/internals2.opcodes.instanceof.php#109108
  221. * and https://github.com/timber/timber/wiki/Extending-Timber
  222. */
  223. $iid = (int) $iid->_thumbnail_id;
  224. }
  225. $image_info = $this->get_image_info($iid);
  226. $this->import($image_info);
  227. $basedir = self::wp_upload_dir();
  228. $basedir = $basedir['basedir'];
  229. if ( isset($this->file) ) {
  230. $this->file_loc = $basedir . DIRECTORY_SEPARATOR . $this->file;
  231. } else if ( isset($this->_wp_attached_file) ) {
  232. $this->file = reset($this->_wp_attached_file);
  233. $this->file_loc = $basedir . DIRECTORY_SEPARATOR . $this->file;
  234. }
  235. if ( isset($image_info['id']) ) {
  236. $this->ID = $image_info['id'];
  237. } else if ( is_numeric($iid) ) {
  238. $this->ID = $iid;
  239. }
  240. if ( isset($this->ID) ) {
  241. $custom = get_post_custom($this->ID);
  242. foreach ($custom as $key => $value) {
  243. $this->$key = $value[0];
  244. }
  245. $this->id = $this->ID;
  246. } else {
  247. if ( is_array($iid) || is_object($iid) ) {
  248. TimberHelper::error_log('Not able to init in TimberImage with iid=');
  249. TimberHelper::error_log($iid);
  250. } else {
  251. TimberHelper::error_log('Not able to init in TimberImage with iid=' . $iid);
  252. }
  253. }
  254. }
  255. /**
  256. * @internal
  257. * @param string $relative_path
  258. */
  259. protected function init_with_relative_path( $relative_path ) {
  260. $this->abs_url = home_url( $relative_path );
  261. $file_path = TimberURLHelper::get_full_path( $relative_path );
  262. $this->file_loc = $file_path;
  263. $this->file = $file_path;
  264. }
  265. /**
  266. * @internal
  267. * @param string $file_path
  268. */
  269. protected function init_with_file_path( $file_path ) {
  270. $url = TimberURLHelper::file_system_to_url( $file_path );
  271. $this->abs_url = $url;
  272. $this->file_loc = $file_path;
  273. $this->file = $file_path;
  274. }
  275. /**
  276. * @internal
  277. * @param string $url
  278. */
  279. protected function init_with_url($url) {
  280. $this->abs_url = $url;
  281. if ( TimberURLHelper::is_local($url) ) {
  282. $this->file = ABSPATH . TimberURLHelper::get_rel_url($url);
  283. $this->file_loc = ABSPATH . TimberURLHelper::get_rel_url($url);
  284. }
  285. }
  286. /**
  287. * @api
  288. * @example
  289. * ```twig
  290. * <img src="{{ image.src }}" alt="{{ image.alt }}" />
  291. * ```
  292. * ```html
  293. * <img src="http://example.org/wp-content/uploads/2015/08/pic.jpg" alt="W3 Checker told me to add alt text, so I am" />
  294. * ```
  295. * @return string alt text stored in WordPress
  296. */
  297. public function alt() {
  298. $alt = trim(strip_tags(get_post_meta($this->ID, '_wp_attachment_image_alt', true)));
  299. return $alt;
  300. }
  301. /**
  302. * @api
  303. * @example
  304. * ```twig
  305. * {% if post.thumbnail.aspect < 1 %}
  306. * {# handle vertical image #}
  307. * <img src="{{ post.thumbnail.src|resize(300, 500) }}" alt="A basketball player" />
  308. * {% else %}
  309. * <img src="{{ post.thumbnail.src|resize(500) }}" alt="A sumo wrestler" />
  310. * {% endif %}
  311. * ```
  312. * @return float
  313. */
  314. public function aspect() {
  315. $w = intval($this->width());
  316. $h = intval($this->height());
  317. return $w / $h;
  318. }
  319. /**
  320. * @api
  321. * @example
  322. * ```twig
  323. * <img src="{{ image.src }}" height="{{ image.height }}" />
  324. * ```
  325. * ```html
  326. * <img src="http://example.org/wp-content/uploads/2015/08/pic.jpg" height="900" />
  327. * ```
  328. * @return int
  329. */
  330. public function height() {
  331. return $this->get_dimensions('height');
  332. }
  333. /**
  334. * Returns the link to an image attachment's Permalink page (NOT the link for the image itself!!)
  335. * @api
  336. * @example
  337. * ```twig
  338. * <a href="{{ image.link }}"><img src="{{ image.src }} "/></a>
  339. * ```
  340. * ```html
  341. * <a href="http://example.org/my-cool-picture"><img src="http://example.org/wp-content/uploads/2015/whatever.jpg"/></a>
  342. * ```
  343. */
  344. public function link() {
  345. if ( strlen($this->abs_url) ) {
  346. return $this->abs_url;
  347. }
  348. return get_permalink($this->ID);
  349. }
  350. /**
  351. * @api
  352. * @return bool|TimberPost
  353. */
  354. public function parent() {
  355. if ( !$this->post_parent ) {
  356. return false;
  357. }
  358. return new $this->PostClass($this->post_parent);
  359. }
  360. /**
  361. * @api
  362. * @example
  363. * ```twig
  364. * <img src="{{ image.path }}" />
  365. * ```
  366. * ```html
  367. * <img src="/wp-content/uploads/2015/08/pic.jpg" />
  368. * ```
  369. * @return string the /relative/path/to/the/file
  370. */
  371. public function path() {
  372. return TimberURLHelper::get_rel_path($this->src());
  373. }
  374. /**
  375. * @param string $size a size known to WordPress (like "medium")
  376. * @api
  377. * @example
  378. * ```twig
  379. * <h1>{{post.title}}</h1>
  380. * <img src="{{post.thumbnail.src}}" />
  381. * ```
  382. * ```html
  383. * <img src="http://example.org/wp-content/uploads/2015/08/pic.jpg" />
  384. * ```
  385. * @return bool|string
  386. */
  387. public function src($size = '') {
  388. if ( isset($this->abs_url) ) {
  389. return $this->_maybe_secure_url($this->abs_url);
  390. }
  391. if ( $size && is_string($size) && isset($this->sizes[$size]) ) {
  392. $image = image_downsize($this->ID, $size);
  393. return $this->_maybe_secure_url(reset($image));
  394. }
  395. if ( !isset($this->file) && isset($this->_wp_attached_file) ) {
  396. $this->file = $this->_wp_attached_file;
  397. }
  398. if ( !isset($this->file) ) {
  399. return false;
  400. }
  401. $dir = self::wp_upload_dir();
  402. $base = $dir['baseurl'];
  403. $src = trailingslashit($this->_maybe_secure_url($base)) . $this->file;
  404. $src = apply_filters('timber/image/src', $src, $this->ID);
  405. return apply_filters('timber_image_src', $src, $this->ID);
  406. }
  407. /**
  408. * @deprecated use src() instead
  409. * @return string
  410. */
  411. function url() {
  412. return $this->get_src();
  413. }
  414. /**
  415. * @api
  416. * @example
  417. * ```twig
  418. * <img src="{{ image.src }}" width="{{ image.width }}" />
  419. * ```
  420. * ```html
  421. * <img src="http://example.org/wp-content/uploads/2015/08/pic.jpg" width="1600" />
  422. * ```
  423. * @return int
  424. */
  425. public function width() {
  426. return $this->get_dimensions('width');
  427. }
  428. /**
  429. * @deprecated 0.21.9 use TimberImage::width() instead
  430. * @internal
  431. * @return int
  432. */
  433. function get_width() {
  434. return $this->width();
  435. }
  436. /**
  437. * @deprecated 0.21.9 use TimberImage::height() instead
  438. * @internal
  439. * @return int
  440. */
  441. function get_height() {
  442. return $this->height();
  443. }
  444. /**
  445. * @deprecated 0.21.9 use TimberImage::src
  446. * @internal
  447. * @param string $size
  448. * @return bool|string
  449. */
  450. function get_src( $size = '' ) {
  451. return $this->src( $size );
  452. }
  453. /**
  454. * @deprecated 0.21.9 use TimberImage::path()
  455. * @internal
  456. * @return string
  457. */
  458. function get_path() {
  459. return $this->link();
  460. }
  461. /**
  462. * @deprecated use src() instead
  463. * @return string
  464. */
  465. function get_url() {
  466. return $this->get_src();
  467. }
  468. /**
  469. * @internal
  470. * @deprecated 0.21.8
  471. * @return bool|TimberPost
  472. */
  473. function get_parent() {
  474. return $this->parent();
  475. }
  476. /**
  477. * @internal
  478. * @deprecated 0.21.9
  479. * @see TimberImage::alt
  480. * @return string
  481. */
  482. function get_alt() {
  483. return $this->alt();
  484. }
  485. }