response.class.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <?php
  2. namespace Juju\Http;
  3. require_once(realpath(dirname(__DIR__).'/events.trait.php'));
  4. require_once(realpath(dirname(__DIR__).'/msgfmt.class.php'));
  5. require_once(realpath(dirname(__DIR__).'/App/router.class.php'));
  6. use \Juju\{App\Router, Events, MsgFmt};
  7. class Response implements \JsonSerializable {
  8. use Events;
  9. public $output = '';
  10. public $body = '';
  11. private $code = 200;
  12. public $headers = [];
  13. protected $open = true;
  14. protected $shutdown = false;
  15. public function __construct(){}
  16. public function __toString(){
  17. return $this->body;
  18. }
  19. public function jsonSerialize(){
  20. return [
  21. 'code'=> $this->code,
  22. 'headers'=> $this->headers,
  23. 'body'=> $this->body
  24. ];
  25. }
  26. public function clear(){
  27. if($this->open){
  28. $this->fire('clear');
  29. $this->body = '';
  30. }
  31. return $this;
  32. }
  33. public function clear_headers(){
  34. if($this->open){
  35. $this->fire('clear_headers');
  36. $this->headers = [];
  37. }
  38. return $this;
  39. }
  40. public function clear_header(string $name){
  41. foreach($this->headers as $key => $header){
  42. if($header[0] == $name){
  43. $this->fire('clear_header', $name);
  44. array_splice($this->headers, $key, 1);
  45. }
  46. }
  47. return $this;
  48. }
  49. public function write(string $chunk){
  50. $this->fire('write', $chunk);
  51. if($this->open){
  52. $this->body .= $chunk;
  53. $this->clear_header('Content-Length')
  54. ->header('Content-Length', strlen($this->body));
  55. }
  56. return $this;
  57. }
  58. public function json($json){
  59. if(is_array($json)){
  60. array_walk_recursive($json, function(&$item, $key){
  61. if(!mb_detect_encoding($item, 'utf-8', true)){
  62. $item = utf8_encode($item);
  63. }
  64. });
  65. }
  66. $this->fire('json', $json);
  67. $this->write(json_encode($json));
  68. if(json_last_error() != JSON_ERROR_NONE){
  69. throw new \Exception(json_last_error_msg());
  70. }
  71. return $this;
  72. }
  73. public function header(string $name, string $value = null){
  74. if(is_null($value)){
  75. $headers = array_filter($this->headers,function($header) use($name){
  76. return $header[0] == $name;
  77. });
  78. return end($headers)[1];
  79. }else{
  80. if($this->open){
  81. if($this->fire('header', $name, $value) !== false){
  82. $this->headers[] = [
  83. $name,
  84. $value
  85. ];
  86. }
  87. }
  88. }
  89. return $this;
  90. }
  91. public function redirect(string $url){
  92. $this->fire('redirect', $url);
  93. $this->header('Location', Router::url($url));
  94. return $this;
  95. }
  96. public function end(string $chunk=''){
  97. if($this->open){
  98. $this->write($chunk);
  99. $this->fire('end');
  100. $this->open = false;
  101. }
  102. return $this;
  103. }
  104. public function img(Image $img, string $type = null){
  105. if(!$type){
  106. $type = $img->type;
  107. }
  108. $this->fire('image', $img, $type);
  109. $this->clear_header('Content-Type')
  110. ->header('Content-Type', 'image/'.$type);
  111. if(!is_a($img, 'Image')){
  112. $img = new Image(100, 20);
  113. $img->text('Invalid Image',0,0,'black',12);
  114. }
  115. ob_start();
  116. $img();
  117. $this->write(ob_get_contents());
  118. ob_end_clean();
  119. return $this;
  120. }
  121. public function code(int $code=null){
  122. if(is_null($code)){
  123. return $this->code;
  124. }
  125. $this->fire('code', $code);
  126. $this->code = $code;
  127. return $this;
  128. }
  129. public function shutdown(){
  130. if(!$this->shutdown){
  131. $this->shutdown = true;
  132. $this->fire('beforeshutdown');
  133. if($this->open){
  134. $this->end();
  135. }
  136. $this->fire('shutdown');
  137. http_response_code($this->code);
  138. foreach($this->headers as $k => $header){
  139. header("{$header[0]}: $header[1]", false);
  140. }
  141. echo $this->body;
  142. flush();
  143. $this->fire('aftershutdown');
  144. }
  145. return $this;
  146. }
  147. public function open(bool $force = false){
  148. if($force){
  149. $this->shutdown = false;
  150. }
  151. if(!$this->shutdown){
  152. $this->open = true;
  153. }
  154. return $this;
  155. }
  156. public static function gen_locale(string $folder){
  157. $path = realpath($folder);
  158. if($path !== false){
  159. foreach(glob("{$path}/*/LC_MESSAGES/*.po") as $po_file){
  160. $mo_file = dirname($po_file).'/'.basename($po_file, '.po').'.mo';
  161. if(!file_exists($mo_file)){
  162. MsgFmt::convert($po_file, $mo_file);
  163. }
  164. }
  165. }else{
  166. trigger_error("Locale folder {$folder} missing", E_USER_WARNING);
  167. }
  168. }
  169. public static function locale(string $locale, string $domain, string $folder, string $codeset = 'UTF-8'){
  170. $path = realpath($folder);
  171. if($path !== false){
  172. bindtextdomain($domain, $path);
  173. textdomain($domain);
  174. bind_textdomain_codeset($domain, $codeset);
  175. $orig_locale = $locale;
  176. self::gen_locale($path);
  177. if(!file_exists("{$path}/$locale/LC_MESSAGES/{$domain}.mo")){
  178. $locale = \Locale::getPrimaryLanguage($locale);
  179. if(!file_exists("{$path}/$locale/LC_MESSAGES/{$domain}.mo")){
  180. $locale = \Locale::getDefault();
  181. if(!file_exists("{$path}/$locale/LC_MESSAGES/{$domain}.mo")){
  182. $dir = dir("{$path}/");
  183. do{
  184. $locale = $dir->read();
  185. if($locale === false){
  186. trigger_error("Unable to find fallback locale for {$orig_locale}", E_USER_WARNING);
  187. break;
  188. }
  189. }while(!file_exists("{$path}/$locale/LC_MESSAGES/{$domain}.mo"));
  190. }
  191. }
  192. }
  193. }else{
  194. trigger_error("Locale folder {$folder} missing", E_USER_WARNING);
  195. }
  196. \Locale::setDefault($locale);
  197. }
  198. }
  199. ?>