├── README.md ├── composer.json ├── example.php └── src └── gburtini └── HumanizePHP └── HumanizePHP.php /README.md: -------------------------------------------------------------------------------- 1 | Humanize PHP 2 | ============ 3 | _A quick port of the useful aspects of [Django's Humanize library](http://docs.djangoproject.com/en/1.3/ref/contrib/humanize/)._ 4 | 5 | Installation 6 | ------------ 7 | This package is available in Packagist/Composer as ``gburtini/humanize-php``. 8 | 9 | Features 10 | -------- 11 | 12 | * ``naturaltime($timestamp, $depth=1)`` returns "5 minutes ago" type responses from timestamp. ``$depth`` indicates how many units we should break your timestamp in to. 13 | * ``apnumber($n)`` returns the "Associated Press style" number, ``$n``, where numbers 1 thru 9 are returned as a word. 14 | * ``intcomma($n)`` comma-separates an integer, unlike Django, it does not respect format localization. 15 | * ``intword($n)`` returns the "word" for a given large number, for example, 1000000 becomes 1.0 million. 16 | * ``naturalday($timestamp, $format)`` returns "today", "yesterday" or "tomorrow" from a given ``$timestamp``, alternatively returns the date in ``$format``. 17 | * ``ordinal($n)`` converts an integer in to its ordinal string (1st, 2nd, ...) 18 | * ``checkize($n)`` converts a number in to its check (cheque) ready word form. 65535 becomes "sixty five thousand, five hundred and thirty five." *not in Django* 19 | 20 | 21 | License 22 | ------- 23 | Copyright (C) 2011-2015 Giuseppe Burtini 24 | 25 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this library except in compliance with the License. You may obtain a copy of the License at 26 | 27 | http://www.apache.org/licenses/LICENSE-2.0 28 | 29 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 30 | 31 | _Django is a registered trademark of the Django Software Foundation. The Humanize-PHP project has no association with the Django Software Foundation._ 32 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gburtini/humanize-php", 3 | "type": "library", 4 | "description": "Provides functions for converting numbers and timestamps in to human friendly forms. Based on the Django Humanize API.", 5 | "keywords": ["numbers", "readable", "human-ready", "timestamps", "human-friendly", "Django", "Humanize"], 6 | "homepage": "http://github.com/gburtini/HumanizePHP", 7 | "license": "Apache", 8 | "authors": [ 9 | { 10 | "name": "Giuseppe Burtini", 11 | "email": "joe@truephp.com", 12 | "homepage": "http://giuseppeburtini.com/", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.0.0" 18 | }, 19 | "autoload": { 20 | "psr-0": { 21 | "gburtini\\HumanizePHP": "src" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /src/gburtini/HumanizePHP/HumanizePHP.php: -------------------------------------------------------------------------------- 1 | 1) { 58 | // recurse in if we wish to fill out the remainder 59 | $next = (HumanizePHP::naturaltime( 60 | // get a time in seconds relative to zero for whatever is leftover after what we've rendered so far 61 | (($original < 0) ? (1) : (-1)) * ($original % (abs($age) * $totalfactor)), 62 | $depth-1, 63 | 0, // reference time of zero 64 | false // don't wrap with "in" and "ago" as we're still building the string. 65 | )); 66 | 67 | if($next != null) { // we return null if there were none of this subelement. I think this should probably recurse one deeper (we may have to skip units), but rounding here is clean sometimes too (you probably don't want to say "1 decade and 4 seconds.") 68 | if($depth > 2) // deal with commas. there's probably a better (read: more language agnostic) way to do this (produce a list of $nexts and join them). 69 | $response = $response . __(", ") . $next; 70 | else 71 | $response = $response . __(" and ") . $next; 72 | } 73 | } 74 | 75 | if($wrap_string) { 76 | if($original < 0) { 77 | $response = sprintf(__("in %s"), $response); 78 | } else { 79 | $response = sprintf(__("%s ago"), $response); 80 | } 81 | } 82 | 83 | return $response; 84 | } 85 | 86 | 87 | static function apnumber($number) { 88 | $replace = array(0=>"zero", 1=>"one", 2=>"two", 3=>"three", 89 | 4=>"four", 5=>"five", 6=>"six", 7=>"seven", 90 | 8=>"eight", 9=>"nine"); 91 | 92 | $num = intval($number); // warning, intval returns 0 on non-integers. 93 | if($num == 0) { 94 | if(!strstr('0', $number)) return $number; // not a zero 95 | } 96 | 97 | if(array_key_exists($num, $replace)) { 98 | return $replace[$num]; 99 | } else { 100 | return $number; 101 | } 102 | } 103 | 104 | static function intcomma($number, $decimals=0, $decimal='.', $separator=',') { 105 | return number_format($number, $decimals, $decimal, $separator); 106 | } 107 | 108 | 109 | // smallestAccepted is set to 1 million by default for 110 | // adherence to the Django Humanize API. 111 | static function intword($number, $smallestAccepted=1000000, $decimals = 1) { 112 | $number = intval($number); 113 | if($number < $smallestAccepted) return $number; 114 | 115 | if($number < 100) { 116 | return HumanizePHP::intcomma($number, $decimals); 117 | } 118 | 119 | if($number < 1000) { 120 | $newValue = $number / 100; 121 | return HumanizePHP::intcomma($newValue, $decimals) . " hundred"; 122 | } 123 | 124 | if($number < 100000) { 125 | $newValue = $number / 1000.0; 126 | return HumanizePHP::intcomma($newValue, $decimals) . " thousand"; 127 | } 128 | 129 | if($number < 1000000) { 130 | $newValue = $number / 100000.0; 131 | return HumanizePHP::intcomma($newValue, $decimals) . " hundred thousand"; 132 | } 133 | 134 | if($number < 1000000000) { 135 | $newValue = $number / 1000000.0; 136 | return HumanizePHP::intcomma($newValue, $decimals) . " million"; 137 | } 138 | 139 | // senseless on a 32 bit system probably. 140 | if($number < 1000000000000) { 141 | $newValue = $number / 1000000000.0; 142 | return HumanizePHP::intcomma($newValue, $decimals) . " billion"; 143 | } 144 | 145 | if($number < 1000000000000000) { 146 | $newValue = $number / 1000000000000.0; 147 | return HumanizePHP::intcomma($newValue, $decimals) . " trillion"; 148 | } 149 | 150 | return $number; // too big. 151 | } 152 | 153 | static function naturalday($timestamp, $format='F j, Y') { 154 | // this -60 deals with a bug in strtotime on (some?) PHP builds. 155 | $end_tomorrow = strtotime("+2 days 12:01am")-60; 156 | $tomorrow = strtotime("tomorrow 12:01am")-60; 157 | $yesterday = strtotime("yesterday 12:01am")-60; 158 | $today = strtotime("today 12:01am")-60; 159 | 160 | if($timestamp > $yesterday && $timestamp < $today) return "yesterday"; 161 | if($timestamp > $today && $timestamp < $tomorrow) return "today"; 162 | if($timestamp > $tomorrow && $timestamp < $end_tomorrow) return "tomorrow"; 163 | 164 | return date($format, $timestamp); 165 | } 166 | 167 | static function ordinal($value) { 168 | $number = intval($value); 169 | if($number == 0) return $value; // could be a bad string or just a 0. 170 | 171 | $specialCheck = $number % 100; 172 | if($specialCheck == 11 || $specialCheck == 12 || $specialCheck == 13) { return $number . "th"; } 173 | 174 | $leastSignificant = $number % 10; 175 | switch($leastSignificant) { 176 | case 1: 177 | $end = "st"; 178 | break; 179 | case 2: 180 | $end = "nd"; 181 | break; 182 | case 3: 183 | $end = "rd"; 184 | break; 185 | default: 186 | $end = "th"; 187 | break; 188 | } 189 | return $number . $end; 190 | } 191 | 192 | // not part of the Django API. 193 | // takes a number and turns it in to a string viable for writing on a cheque 194 | // 124 -> one hundred and twenty four. 195 | // 65535 -> sixty five thousand, five hundred and thirty five 196 | static function checkize($number) { 197 | $singles = array(0=>"zero", 1=>"one", 2=>"two", 198 | 3=>"three", 4=>"four", 5=>"five", 199 | 6=>"six", 7=>"seven", 8=>"eight", 200 | 9=>"nine"); 201 | $ten_singles = array(0=>"ten", 1=>"eleven", 2=>"twelve", 202 | 3=>"thirteen", 4=>"fourteen", 5=>"fifteen", 203 | 6=>"sixteen", 7=>"seventeen", 8=>"eighteen", 204 | 9=>"nineteen"); // special case. 205 | $tens = array(2=>"twenty", 3=>"thirty", 4=>"fourty", 5=>"fifty", 6=>"seventy", 206 | 8=>"eighty", 9=>"ninety"); 207 | $thousands = array("thousand", "million", "billion", "trillion", "quadrillion"); 208 | 209 | $number = strval(intval($number)); 210 | $parts = array(); 211 | 212 | // check the special "teens" case. 213 | $specialCheck = $number % 100; 214 | if($specialCheck <= 19 && $specialCheck >= 10) { 215 | $parts[] = $ten_singles[$number[strlen($number)-1]]; 216 | } else { 217 | $single = $number[strlen($number)-1]; 218 | if($single != 0 || $number < 10) 219 | $parts[] = $singles[$single]; 220 | 221 | if($number > 10 && $specialCheck != 0) { 222 | $parts[] = $tens[ $number[strlen($number)-2] ] . " -"; 223 | } 224 | } 225 | 226 | // special hundreds case (not a multiple of 3). 227 | if($number >= pow(10, 2)) { 228 | $hundredsCount = $number[strlen($number)-3]; 229 | if($hundredsCount != 0) { 230 | $parts[] = $singles[$hundredsCount] . " hundred"; 231 | } 232 | } 233 | 234 | $offset = 3; 235 | foreach($thousands as $frag) { 236 | if($number < pow(10,$offset+1)) break; 237 | $part = substr($number, strlen($number)-$offset-3, 3); 238 | $parts[] = HumanizePHP::checkize($part) . " {$frag},"; 239 | $offset+=3; 240 | } 241 | 242 | return str_replace(" - ", "-", implode(" ", array_reverse($parts))); 243 | } 244 | 245 | } 246 | ?> 247 | --------------------------------------------------------------------------------