Skip to content

Commit 39e2e4a

Browse files
committed
Releasing 1.1.4
2 parents d7b5995 + 2ea2b35 commit 39e2e4a

File tree

11 files changed

+282
-13
lines changed

11 files changed

+282
-13
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ with `{{objectKey.attributeName}}`, e.g. `{{user.name}}`.
233233

234234
**Snippets** are more complex replacements that will be inserted in your template. This is useful when you need the same complex
235235
text structure in multiple template generations, e.g. for a footer or a header text. Snippets are referenced in a template by
236-
their keys only: `{{snippetKey}}`. A snippet is implemented by the interface [`Snippet`](https://github.com/technicalguru/php-utils/blob/main/src/TgUtils/Templating/Snippet.php).
236+
their keys only: `{{snippetKey}}`. A snippet is implemented by the interface [`Snippet`](https://github.com/technicalguru/php-utils/blob/main/src/TgUtils/Templating/Snippet.php). A snippet can take parameters as `{{snippetKey:param1:param2...}}`.
237237

238238
**Formatters** can be used to format an object's attribute. Formatters can take parameters to further customize the formatting. A good example
239239
is the [`DateFormatter`](https://github.com/technicalguru/php-utils/blob/main/src/TgUtils/Templating/DateFormatter.php). The formatter

nbproject/project.properties

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include.path=${php.global.include.path}
2+
php.version=PHP_73
3+
source.encoding=UTF-8
4+
src.dir=.
5+
tags.asp=false
6+
tags.short=false
7+
web.root=.

nbproject/project.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://www.netbeans.org/ns/project/1">
3+
<type>org.netbeans.modules.php.project</type>
4+
<configuration>
5+
<data xmlns="http://www.netbeans.org/ns/php-project/1">
6+
<name>php-utils</name>
7+
</data>
8+
</configuration>
9+
</project>

src/TgUtils/ImageUtils.php

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace TgUtils;
4+
5+
class ImageUtils {
6+
7+
/**
8+
* Creates a thumbnail image from the given image at the path.
9+
* @param string $imagepath - path of original image
10+
* @param int $maxWidth - maximum new width
11+
* @param int $maxHeight - maximum new height
12+
* @param string $targetDir - target directory
13+
* @return path of the new image file or NULL if it could not be created.
14+
*/
15+
public static function createThumbnail($imagePath, $maxWidth, $maxHeight, $targetDir = NULL) {
16+
$pathinfo = pathinfo($imagePath);
17+
$targetPath = '.';
18+
if ($targetDir == NULL) {
19+
$targetPath = $pathinfo['dirname'].'/'.$pathinfo['filename'].'.thumbnail.';
20+
} else {
21+
$targetPath = $targetDir.'/'.$pathinfo['filename'].'.thumbnail.';
22+
}
23+
if (class_exists('Imagick')) {
24+
$image = new \Imagick($imagePath);
25+
$image->thumbnailImage($maxWidth, $maxHeight, TRUE);
26+
$targetPath .= 'png';
27+
if (!$image->writeImage($targetPath)) {
28+
$targetPath = NULL;
29+
}
30+
} else {
31+
// We use GD
32+
$imageDetails = getimagesize($imagePath);
33+
$image = self::readImage($imagePath, $imageDetails);
34+
if ($image != NULL) {
35+
// Compute new dimensions
36+
$size = self::computeNewSize($imageDetails[0], $imageDetails[1], $maxWidth, $maxHeight);
37+
38+
// Scale it
39+
$thumbnail = imagecreatetruecolor($size->width, $size-height);
40+
imagecopyresized($thumbnail, $image, 0, 0, 0, 0, $size->width, $size->height, $imageDetails[0], $imageDetails[1]);
41+
// Writing it
42+
$gdInfo = gd_info();
43+
// Preferential order PNG, JPEG, WEBP, XPM, WBMP, GIF, BMP
44+
if ($gdInfo['PNG Support']) {
45+
$targetPath .= 'png';
46+
if (!imagepng($thumbnail, $targetPath)) $targetPath = NULL;
47+
} else if ($gdInfo['JPEG Support']) {
48+
$targetPath .= 'jpg';
49+
if (!imagejpeg($thumbnail, $targetPath)) $targetPath = NULL;
50+
} else if ($gdInfo['WebP Support']) {
51+
$targetPath .= 'webp';
52+
if (!imagewebp($thumbnail, $targetPath)) $targetPath = NULL;
53+
} else if ($gdInfo['XPM Support']) {
54+
$targetPath .= 'xpm';
55+
if (!imagexbm($thumbnail, $targetPath)) $targetPath = NULL;
56+
} else if ($gdInfo['WBMP Support']) {
57+
$targetPath .= 'wbmp';
58+
if (!imagewbmp($thumbnail, $targetPath)) $targetPath = NULL;
59+
} else if ($gdInfo['GIF Create Support']) {
60+
$targetPath .= 'gif';
61+
if (!imagegif($thumbnail, $targetPath)) $targetPath = NULL;
62+
} else if ($gdInfo['BMP Support']) {
63+
$targetPath .= 'bmp';
64+
if (!imagebmp($thumbnail, $targetPath)) $targetPath = NULL;
65+
} else {
66+
$targetPath = NULL;
67+
}
68+
imagedestroy($thumbnail);
69+
imagedestroy($image);
70+
} else {
71+
$targetPath = NULL;
72+
}
73+
}
74+
return $targetPath;
75+
}
76+
77+
/**
78+
* Reads an image with GD library.
79+
* @param string $imagePath - where the image is stored.
80+
* @param array $imageDetails - the result from getimagesize() call when executed before.
81+
* @return resource form GD library
82+
*/
83+
public static function readImage($imagePath, $imageDetails = NULL) {
84+
if (file_exists($imagePath)) {
85+
if ($imageDetails == NULL) $imageDetails = getimagesize($imagePath);
86+
if ($imageDetails !== FALSE) {
87+
$gdInfo = gd_info();
88+
$image = NULL;
89+
switch ($imageDetails[2]) {
90+
case IMAGETYPE_BMP:
91+
if ($gdInfo['BMP Support']) {
92+
$image = imagecreatefrombmp($imagePath);
93+
}
94+
break;
95+
case IMAGETYPE_GIF:
96+
if ($gdInfo['GIF Read Support']) {
97+
$image = imagecreatefromgif($imagePath);
98+
}
99+
break;
100+
case IMG_JPEG:
101+
case IMG_JPEG:
102+
if ($gdInfo['JPEG Support']) {
103+
$image = imagecreatefromjpeg($imagePath);
104+
}
105+
break;
106+
case IMAGETYPE_PNG:
107+
if ($gdInfo['PNG Support']) {
108+
$image = imagecreatefrompng($imagePath);
109+
}
110+
break;
111+
case IMAGETYPE_WBMP:
112+
if ($gdInfo['WBMP Support']) {
113+
$image = imagecreatefromwbmp($imagePath);
114+
}
115+
break;
116+
case IMG_XPM:
117+
if ($gdInfo['XPM Support']) {
118+
$image = imagecreatefromxpm($imagePath);
119+
}
120+
break;
121+
case IMAGETYPE_WEBP:
122+
if ($gdInfo['WebP Support']) {
123+
$image = imagecreatefromwebp($imagePath);
124+
}
125+
break;
126+
}
127+
}
128+
return $image;
129+
}
130+
return NULL;
131+
}
132+
133+
/**
134+
* Returns an object with width and height attributes to resize.
135+
* @param int $origWidth - original width
136+
* @param int $origHeight - original height
137+
* @param int $maxWidth - maximum new width
138+
* @param int $maxHeight - maximum new height
139+
* @return object with width an height attribute
140+
*/
141+
public static function computeNewSize($origWidth, $origHeight, $maxWidth, $maxHeight) {
142+
// Compute new dimensions
143+
$rc = new \stdClass;
144+
$rc->width = $maxWidth;
145+
$rc->height = $maxHeight;
146+
if ($origWidth < $origHeight) {
147+
// Portrait
148+
$rc->width = intval($origWidth * $rc->height / $origHeight);
149+
} else {
150+
// Landscape or squared
151+
$rc->height = intval($origHeight * $rc->width / $origWidth);
152+
}
153+
return $rc;
154+
}
155+
}
156+

src/TgUtils/Request.php

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public static function getRequest() {
3232
public $headers;
3333
/** The host as the user requested it (can differ from $httpHost in reverse proxy setups) */
3434
public $host;
35+
/** The URI to the root of the server, combination of protocol and host. */
36+
public $rootUri;
3537
/** The HTTP host - the host mentioned in Host: header */
3638
public $httpHost;
3739
/** The URI which includes the parameters */
@@ -40,6 +42,8 @@ public static function getRequest() {
4042
public $path;
4143
/** The path of the original request (requested at proxy). Does not include parameters */
4244
public $originalPath;
45+
/** The original request (requested at proxy). Includes parameters */
46+
public $originalUri;
4347
/** The path split in its elements */
4448
public $pathElements;
4549
/** The parameters as a string */
@@ -73,10 +77,11 @@ public static function getRequest() {
7377
public function __construct() {
7478
// Sequence matters!
7579
$this->method = $_SERVER['REQUEST_METHOD'];
76-
$this->headers = getallheaders();
80+
$this->headers = $this->initHeaders();
7781
$this->protocol = $this->initProtocol();
7882
$this->httpHost = $_SERVER['HTTP_HOST'];
7983
$this->host = $this->initHost();
84+
$this->rootUri = $this->initRootUri();
8085
if (isset($_SERVER['REQUEST_URI'])) {
8186
$this->uri = $_SERVER['REQUEST_URI'];
8287
} else {
@@ -92,6 +97,7 @@ public function __construct() {
9297
$this->documentRoot = $this->initDocumentRoot();
9398
$this->webRoot = $this->initWebRoot(TRUE);
9499
$this->originalPath = $this->initOriginalPath();
100+
$this->originalUri = $this->initOriginalUri();
95101
$this->localWebRoot = $this->initWebRoot(FALSE);
96102
$this->webRootUri = $this->initWebRootUri();
97103
$this->appRoot = $this->documentRoot;
@@ -103,6 +109,18 @@ public function __construct() {
103109
$this->langCode = 'en';
104110
}
105111

112+
/**
113+
* Different products return different keys in headers. We make all keys lowercase here.
114+
*/
115+
protected function initHeaders() {
116+
$headers = getallheaders();
117+
$rc = array();
118+
foreach ($headers AS $key => $value) {
119+
$rc[strtolower($key)] = $value;
120+
}
121+
return $rc;
122+
}
123+
106124
/**
107125
* Returns the server hostname that was requested.
108126
* <p>The host is extracted from HTTP_X_FORWARDED_HOST or when not set
@@ -136,6 +154,13 @@ protected function initProtocol() {
136154
return $_SERVER['REQUEST_SCHEME'];
137155
}
138156

157+
/**
158+
* Returns the base for the URI - protocol and host.
159+
*/
160+
protected function initRootUri() {
161+
return $this->protocol.'://'.$this->host;
162+
}
163+
139164
/**
140165
* Returns all path elements with .html stripped of if detected.
141166
* <p>E.g. /my/path/index.html will return three elements: my, path and index.</p>
@@ -205,10 +230,10 @@ public function getPostParam($key, $default = NULL) {
205230
public function getPostParams() {
206231
if ($this->postParams == NULL) {
207232
$this->postParams = array();
208-
$headers = $this->headers;
209233
// Check that we have content-length
210-
if (isset($headers['Content-Length'])) {
211-
$len = intval($headers['Content-Length']);
234+
$len = $this->getHeader('Content-Length');
235+
if ($len) {
236+
$len = intval($len);
212237
// Check that we have a valid content-length
213238
if (($len>0) && ($len<10000)) {
214239
$this->postParams = $_POST;
@@ -250,7 +275,7 @@ public static function parseQueryString($s) {
250275
* @return string the value of the header.
251276
*/
252277
public function getHeader($key) {
253-
if (isset($this->headers[$key])) return $this->headers[$key];
278+
if (isset($this->headers[strtolower($key)])) return $this->headers[strtolower($key)];
254279
return NULL;
255280
}
256281

@@ -306,6 +331,19 @@ protected function initOriginalPath() {
306331
return $rc;
307332
}
308333

334+
/**
335+
* Returns the original URI as request by the end user.
336+
* The path might be different from $this->path as
337+
* a webroot mapping might be involved.
338+
*/
339+
protected function initOriginalUri() {
340+
$rc = $this->originalPath;
341+
if ($this->params) {
342+
$rc .= '?'.$this->params;
343+
}
344+
return $rc;
345+
}
346+
309347
/**
310348
* Returns the web root - that is the web path where the current
311349
* script is rooted and usually the base path for an application.

src/TgUtils/Templating/Processor.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@ protected function getVar($s) {
7575
if (is_string($object)) return $object;
7676

7777
// Try a snippet
78-
$snippet = $this->getSnippet($objectKey);
78+
$parts = explode(':', $objectKey);
79+
$snippetKey = array_shift($parts);
80+
$snippet = $this->getSnippet($snippetKey);
7981
if ($snippet != NULL) {
8082
if (is_string($snippet)) return $snippet;
8183
if (is_array($snippet)) return I18N::_($snippet, $this->language);
82-
return $snippet->getOutput($this);
84+
return $snippet->getOutput($this, $parts);
8385
}
8486
}
8587
return '[Not defined: '.$s.']';

src/TgUtils/Templating/Snippet.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
/**
66
* A snippet is a fixed, software-defined piece of text that can be inserted in
7-
* templates via the {{template-name}} variable.
7+
* templates via the {{snippet-name:params}} variable.
88
*/
99
interface Snippet {
1010

1111
/**
1212
* Return the output of the template, using the given processor for objects and formatters.
13+
* Parameters can be given
1314
*/
14-
public function getOutput(Processor $processor);
15+
public function getOutput(Processor $processor, $params);
1516

1617
}

src/TgUtils/Utils.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,15 @@ public static function findBy($list, $attr, $value) {
101101
}
102102
return $rc;
103103
}
104-
}
104+
105+
/**
106+
* Test whether the given string is NULL or empty after trimming.
107+
* @param mixed $s - string or NULL to be tested
108+
* @return boolean TRUE when string is empty
109+
*/
110+
public static function isEmpty($s) {
111+
if ($s == NULL) return TRUE;
112+
return strlen(trim($s)) == 0;
113+
}
114+
115+
}

test.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Test script
2+
3+
composer update
4+
RC=./vendor/phpunit/phpunit/phpunit tests
5+
rm -rf vendor composer.lock
6+
exit $RC
7+

tests/TgUtils/Templating/ProcessorTest.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ public function testSnippetWithSnippet(): void {
2525
$this->assertEquals('This is the output: This is my-snippet.', $result);
2626
}
2727

28+
public function testSnippetWithParameter(): void {
29+
$processor = self::createProcessor();
30+
$template = 'This is the output: {{mySnippet:aParameter}}';
31+
$result = $processor->process($template);
32+
$this->assertEquals('This is the output: This is my-snippet.aParameter', $result);
33+
}
34+
2835
public function testSimpleAttribute(): void {
2936
$processor = self::createProcessor();
3037
$template = 'This is the output: {{testObject.name}}';
@@ -60,7 +67,7 @@ protected static function createProcessor(): Processor {
6067

6168
class TestSnippet implements Snippet {
6269

63-
public function getOutput($processor) {
64-
return 'This is my-snippet.';
70+
public function getOutput($processor, $params) {
71+
return 'This is my-snippet.'.(count($params) > 0 ? $params[0] : '');
6572
}
6673
}

0 commit comments

Comments
 (0)