2
0

Boot.hx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. /*
  2. * Copyright (C)2005-2019 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package php;
  23. import haxe.PosInfos;
  24. import haxe.extern.EitherType;
  25. using php.Global;
  26. /**
  27. Various Haxe->PHP compatibility utilities.
  28. You should not use this class directly.
  29. **/
  30. @:keep
  31. @:dox(hide)
  32. class Boot {
  33. /** List of Haxe classes registered by their PHP class names */
  34. @:protected static var aliases = new NativeAssocArray<String>();
  35. /** Cache of HxClass instances */
  36. @:protected static var classes = new NativeAssocArray<HxClass>();
  37. /** List of getters (for Reflect) */
  38. @:protected static var getters = new NativeAssocArray<NativeAssocArray<Bool>>();
  39. /** List of setters (for Reflect) */
  40. @:protected static var setters = new NativeAssocArray<NativeAssocArray<Bool>>();
  41. /** Metadata storage */
  42. @:protected static var meta = new NativeAssocArray<{}>();
  43. /** Cache for closures created of static methods */
  44. @:protected static var staticClosures = new NativeAssocArray<NativeAssocArray<HxClosure>>();
  45. /**
  46. Initialization stuff.
  47. This method is called once before invoking any Haxe-generated user code.
  48. **/
  49. static function __init__() {
  50. Global.mb_internal_encoding('UTF-8');
  51. if (!Global.defined('HAXE_CUSTOM_ERROR_HANDLER') || !Const.HAXE_CUSTOM_ERROR_HANDLER) {
  52. var previousLevel = Global.error_reporting(Const.E_ALL & ~Const.E_DEPRECATED);
  53. var previousHandler = Global.set_error_handler(function(errno:Int, errstr:String, errfile:String, errline:Int) {
  54. if (Global.error_reporting() & errno == 0) {
  55. return false;
  56. }
  57. /*
  58. * Division by zero should not throw
  59. * @see https://github.com/HaxeFoundation/haxe/issues/7034#issuecomment-394264544
  60. */
  61. if (errno == Const.E_WARNING && errstr == 'Division by zero') {
  62. return true;
  63. }
  64. throw new ErrorException(errstr, 0, errno, errfile, errline);
  65. });
  66. // Already had user-defined handler. Return it.
  67. if (previousHandler != null) {
  68. Global.error_reporting(previousLevel);
  69. Global.set_error_handler(previousHandler);
  70. }
  71. }
  72. }
  73. /**
  74. Returns root namespace based on a value of `-D php-prefix=value` compiler flag.
  75. Returns empty string if no `-D php-prefix=value` provided.
  76. **/
  77. public static function getPrefix():String {
  78. return Syntax.code('self::PHP_PREFIX');
  79. }
  80. /**
  81. Register list of getters to be able to call getters using reflection
  82. **/
  83. public static function registerGetters(phpClassName:String, list:NativeAssocArray<Bool>):Void {
  84. getters[phpClassName] = list;
  85. }
  86. /**
  87. Register list of setters to be able to call getters using reflection
  88. **/
  89. public static function registerSetters(phpClassName:String, list:NativeAssocArray<Bool>):Void {
  90. setters[phpClassName] = list;
  91. }
  92. /**
  93. Check if specified property has getter
  94. **/
  95. public static function hasGetter(phpClassName:String, property:String):Bool {
  96. if(!ensureLoaded(phpClassName))
  97. return false;
  98. var has = false;
  99. var phpClassName:haxe.extern.EitherType<Bool, String> = phpClassName;
  100. do {
  101. has = Global.isset(getters[phpClassName][property]);
  102. phpClassName = Global.get_parent_class(phpClassName);
  103. } while (!has && phpClassName != false && Global.class_exists(phpClassName));
  104. return has;
  105. }
  106. /**
  107. Check if specified property has setter
  108. **/
  109. public static function hasSetter(phpClassName:String, property:String):Bool {
  110. if(!ensureLoaded(phpClassName))
  111. return false;
  112. var has = false;
  113. var phpClassName:haxe.extern.EitherType<Bool, String> = phpClassName;
  114. do {
  115. has = Global.isset(setters[phpClassName][property]);
  116. phpClassName = Global.get_parent_class(phpClassName);
  117. } while (!has && phpClassName != false && Global.class_exists(phpClassName));
  118. return has;
  119. }
  120. /**
  121. Save metadata for specified class
  122. **/
  123. public static function registerMeta(phpClassName:String, data:Dynamic):Void {
  124. meta[phpClassName] = data;
  125. }
  126. /**
  127. Retrieve metadata for specified class
  128. **/
  129. public static function getMeta(phpClassName:String):Null<Dynamic> {
  130. if(!ensureLoaded(phpClassName)) return null;
  131. return Global.isset(meta[phpClassName]) ? meta[phpClassName] : null;
  132. }
  133. /**
  134. Associate PHP class name with Haxe class name
  135. **/
  136. public static function registerClass(phpClassName:String, haxeClassName:String):Void {
  137. aliases[phpClassName] = haxeClassName;
  138. }
  139. /**
  140. Returns a list of currently loaded haxe-generated classes.
  141. **/
  142. public static function getRegisteredClasses():Array<Class<Dynamic>> {
  143. var result = [];
  144. Syntax.foreach(aliases, function(phpName, haxeName) {
  145. result.push(cast getClass(phpName));
  146. });
  147. return result;
  148. }
  149. /**
  150. Returns a list of phpName=>haxeName for currently loaded haxe-generated classes.
  151. **/
  152. public static function getRegisteredAliases():NativeAssocArray<String> {
  153. return aliases;
  154. }
  155. /**
  156. Get Class<T> instance for PHP fully qualified class name (E.g. '\some\pack\MyClass')
  157. It's always the same instance for the same `phpClassName`
  158. **/
  159. public static function getClass(phpClassName:String):HxClass {
  160. if (phpClassName.charAt(0) == '\\') {
  161. phpClassName = phpClassName.substr(1);
  162. }
  163. if (!Global.isset(classes[phpClassName])) {
  164. classes[phpClassName] = new HxClass(phpClassName);
  165. }
  166. return classes[phpClassName];
  167. }
  168. /**
  169. Returns Class<HxAnon>
  170. **/
  171. public static inline function getHxAnon():HxClass {
  172. return cast HxAnon;
  173. }
  174. /**
  175. Check if provided value is an anonymous object
  176. **/
  177. public static inline function isAnon(v:Any):Bool {
  178. return Std.isOfType(v, HxAnon);
  179. }
  180. /**
  181. Returns Class<HxClass>
  182. **/
  183. public static inline function getHxClass():HxClass {
  184. return cast HxClass;
  185. }
  186. /**
  187. Returns either Haxe class name for specified `phpClassName` or (if no such Haxe class registered) `phpClassName`.
  188. **/
  189. public static function getClassName(phpClassName:String):String {
  190. var hxClass = getClass(phpClassName);
  191. var name = getHaxeName(hxClass);
  192. return (name == null ? hxClass.phpClassName : name);
  193. }
  194. /**
  195. Returns original Haxe fully qualified class name for this type (if exists)
  196. **/
  197. public static function getHaxeName(hxClass:HxClass):Null<String> {
  198. switch (hxClass.phpClassName) {
  199. case 'Int':
  200. return 'Int';
  201. case 'String':
  202. return 'String';
  203. case 'Bool':
  204. return 'Bool';
  205. case 'Float':
  206. return 'Float';
  207. case 'Class':
  208. return 'Class';
  209. case 'Enum':
  210. return 'Enum';
  211. case 'Dynamic':
  212. return 'Dynamic';
  213. case _:
  214. }
  215. inline function exists()
  216. return Global.isset(aliases[hxClass.phpClassName]);
  217. if (exists()) {
  218. return aliases[hxClass.phpClassName];
  219. } else if (Global.class_exists(hxClass.phpClassName) && exists()) {
  220. return aliases[hxClass.phpClassName];
  221. } else if (Global.interface_exists(hxClass.phpClassName) && exists()) {
  222. return aliases[hxClass.phpClassName];
  223. }
  224. return null;
  225. }
  226. /**
  227. Find corresponding PHP class name.
  228. Returns `null` if specified class does not exist.
  229. **/
  230. public static function getPhpName(haxeName:String):Null<String> {
  231. var prefix = getPrefix();
  232. var phpParts = (Global.strlen(prefix) == 0 ? [] : [prefix]);
  233. var haxeParts = haxeName.split('.');
  234. for (part in haxeParts) {
  235. if (isPhpKeyword(part)) {
  236. part += '_hx';
  237. }
  238. phpParts.push(part);
  239. }
  240. return phpParts.join('\\');
  241. }
  242. /**
  243. Check if the value of `str` is a reserved keyword in PHP
  244. @see https://www.php.net/manual/en/reserved.keywords.php
  245. **/
  246. @:pure(false)
  247. static public function isPhpKeyword(str:String):Bool {
  248. //The body of this method is generated by the compiler
  249. return false;
  250. }
  251. /**
  252. Unsafe cast to HxClosure
  253. **/
  254. public static inline function castClosure(value:Dynamic):HxClosure {
  255. return value;
  256. }
  257. /**
  258. Unsafe cast to HxClass
  259. **/
  260. public static inline function castClass(cls:Class<Dynamic>):HxClass {
  261. return cast cls;
  262. }
  263. /**
  264. Unsafe cast to HxEnum
  265. **/
  266. public static inline function castEnumValue(enm:EnumValue):HxEnum {
  267. return cast enm;
  268. }
  269. /**
  270. Returns `Class<T>` for `HxClosure`
  271. **/
  272. public static inline function closureHxClass():HxClass {
  273. return cast HxClosure;
  274. }
  275. /**
  276. Implementation for `cast(value, Class<Dynamic>)`
  277. @throws haxe.ValueError if `value` cannot be casted to this type
  278. **/
  279. public static function typedCast(hxClass:HxClass, value:Dynamic):Dynamic {
  280. if (value == null)
  281. return null;
  282. switch (hxClass.phpClassName) {
  283. case 'Int':
  284. if (Boot.isNumber(value)) {
  285. return Global.intval(value);
  286. }
  287. case 'Float':
  288. if (Boot.isNumber(value)) {
  289. return value.floatval();
  290. }
  291. case 'Bool':
  292. if (value.is_bool()) {
  293. return value;
  294. }
  295. case 'String':
  296. if (value.is_string()) {
  297. return value;
  298. }
  299. case 'array':
  300. if (value.is_array()) {
  301. return value;
  302. }
  303. case _:
  304. if (value.is_object() && Std.isOfType(value, cast hxClass)) {
  305. return value;
  306. }
  307. }
  308. throw 'Cannot cast ' + Std.string(value) + ' to ' + getClassName(hxClass.phpClassName);
  309. }
  310. /**
  311. Returns string representation of `value`
  312. **/
  313. public static function stringify(value:Dynamic, maxRecursion:Int = 10):String {
  314. if (maxRecursion <= 0) {
  315. return '<...>';
  316. }
  317. if (value == null) {
  318. return 'null';
  319. }
  320. if (value.is_string()) {
  321. return value;
  322. }
  323. if (value.is_int() || value.is_float()) {
  324. return Syntax.string(value);
  325. }
  326. if (value.is_bool()) {
  327. return value ? 'true' : 'false';
  328. }
  329. if (value.is_array()) {
  330. var strings = Syntax.arrayDecl();
  331. Syntax.foreach(value, function(key:Dynamic, item:Dynamic) {
  332. strings.push(Syntax.string(key) + ' => ' + stringify(item, maxRecursion - 1));
  333. });
  334. return '[' + Global.implode(', ', strings) + ']';
  335. }
  336. if (value.is_object()) {
  337. if (Std.isOfType(value, Array)) {
  338. return inline stringifyNativeIndexedArray(value.arr, maxRecursion - 1);
  339. }
  340. if (Std.isOfType(value, HxEnum)) {
  341. var e:HxEnum = value;
  342. var result = e.tag;
  343. if (Global.count(e.params) > 0) {
  344. var strings = Global.array_map(function(item) return Boot.stringify(item, maxRecursion - 1), e.params);
  345. result += '(' + Global.implode(',', strings) + ')';
  346. }
  347. return result;
  348. }
  349. if (value.method_exists('toString')) {
  350. return value.toString();
  351. }
  352. if (value.method_exists('__toString')) {
  353. return value.__toString();
  354. }
  355. if (Std.isOfType(value, StdClass)) {
  356. if (Global.isset(Syntax.field(value, 'toString')) && value.toString.is_callable()) {
  357. return value.toString();
  358. }
  359. var result = new NativeIndexedArray<String>();
  360. var data = Global.get_object_vars(value);
  361. for (key in data.array_keys()) {
  362. result.array_push('$key : ' + stringify(data[key], maxRecursion - 1));
  363. }
  364. return '{ ' + Global.implode(', ', result) + ' }';
  365. }
  366. if (isFunction(value)) {
  367. return '<function>';
  368. }
  369. if (Std.isOfType(value, HxClass)) {
  370. return '[class ' + getClassName((value : HxClass).phpClassName) + ']';
  371. } else {
  372. return '[object ' + getClassName(Global.get_class(value)) + ']';
  373. }
  374. }
  375. throw "Unable to stringify value";
  376. }
  377. static public function stringifyNativeIndexedArray<T>(arr:NativeIndexedArray<T>, maxRecursion:Int = 10):String {
  378. var strings = Syntax.arrayDecl();
  379. Syntax.foreach(arr, function(index:Int, value:T) {
  380. strings[index] = Boot.stringify(value, maxRecursion - 1);
  381. });
  382. return '[' + Global.implode(',', strings) + ']';
  383. }
  384. static public inline function isNumber(value:Dynamic) {
  385. return value.is_int() || value.is_float();
  386. }
  387. /**
  388. Check if specified values are equal
  389. **/
  390. public static function equal(left:Dynamic, right:Dynamic):Bool {
  391. if (isNumber(left) && isNumber(right)) {
  392. return Syntax.equal(left, right);
  393. }
  394. if (Std.isOfType(left, HxClosure) && Std.isOfType(right, HxClosure)) {
  395. return (left : HxClosure).equals(right);
  396. }
  397. return Syntax.strictEqual(left, right);
  398. }
  399. /**
  400. Concat `left` and `right` if both are strings or string and null.
  401. Otherwise return sum of `left` and `right`.
  402. **/
  403. public static function addOrConcat(left:Dynamic, right:Dynamic):Dynamic {
  404. if (left.is_string() || right.is_string()) {
  405. return (left : String) + (right : String);
  406. }
  407. return Syntax.add(left, right);
  408. }
  409. @:deprecated('php.Boot.is() is deprecated. Use php.Boot.isOfType() instead')
  410. public static inline function is(value:Dynamic, type:HxClass):Bool {
  411. return isOfType(value, type);
  412. }
  413. /**
  414. `Std.isOfType()` implementation
  415. **/
  416. public static function isOfType(value:Dynamic, type:HxClass):Bool {
  417. if (type == null)
  418. return false;
  419. var phpType = type.phpClassName;
  420. #if php_prefix
  421. var prefix = getPrefix();
  422. if (Global.substr(phpType, 0, Global.strlen(prefix) + 1) == '$prefix\\') {
  423. phpType = Global.substr(phpType, Global.strlen(prefix) + 1);
  424. }
  425. #end
  426. switch (phpType) {
  427. case 'Dynamic':
  428. return value != null;
  429. case 'Int':
  430. return (value.is_int() || (value.is_float() && Syntax.equal(Syntax.int(value), value) && !Global.is_nan(value)))
  431. && Global.abs(value) <= 2147483648;
  432. case 'Float':
  433. return value.is_float() || value.is_int();
  434. case 'Bool':
  435. return value.is_bool();
  436. case 'String':
  437. return value.is_string();
  438. case 'php\\NativeArray', 'php\\_NativeArray\\NativeArray_Impl_':
  439. return value.is_array();
  440. case 'Enum' | 'Class':
  441. if (Std.isOfType(value, HxClass)) {
  442. var valuePhpClass = (cast value : HxClass).phpClassName;
  443. var enumPhpClass = (cast HxEnum : HxClass).phpClassName;
  444. var isEnumType = Global.is_subclass_of(valuePhpClass, enumPhpClass);
  445. return (phpType == 'Enum' ? isEnumType : !isEnumType);
  446. }
  447. case _:
  448. if (value.is_object()) {
  449. var type:Class<Dynamic> = cast type;
  450. return Syntax.instanceof(value, type);
  451. }
  452. }
  453. return false;
  454. }
  455. /**
  456. Check if `value` is a `Class<T>`
  457. **/
  458. public static inline function isClass(value:Dynamic):Bool {
  459. return Std.isOfType(value, HxClass);
  460. }
  461. /**
  462. Check if `value` is an enum constructor instance
  463. **/
  464. public static inline function isEnumValue(value:Dynamic):Bool {
  465. return Std.isOfType(value, HxEnum);
  466. }
  467. /**
  468. Check if `value` is a function
  469. **/
  470. public static inline function isFunction(value:Dynamic):Bool {
  471. return Std.isOfType(value, Closure) || Std.isOfType(value, HxClosure);
  472. }
  473. /**
  474. Check if `value` is an instance of `HxClosure`
  475. **/
  476. public static inline function isHxClosure(value:Dynamic):Bool {
  477. return Std.isOfType(value, HxClosure);
  478. }
  479. /**
  480. Performs `left >>> right` operation
  481. **/
  482. public static function shiftRightUnsigned(left:Int, right:Int):Int {
  483. if (right == 0) {
  484. return left;
  485. } else if (left >= 0) {
  486. return (left >> right) & ~((1 << (8 * Const.PHP_INT_SIZE - 1)) >> (right - 1));
  487. } else {
  488. return (left >> right) & (0x7fffffff >> (right - 1));
  489. }
  490. }
  491. /**
  492. Helper method to avoid "Cannot use temporary expression in write context" error for expressions like this:
  493. ```haxe
  494. (new MyClass()).fieldName = 'value';
  495. ```
  496. **/
  497. static public function deref(value:Dynamic):Dynamic {
  498. return value;
  499. }
  500. /**
  501. Create Haxe-compatible anonymous structure of `data` associative array
  502. **/
  503. static public inline function createAnon(data:NativeArray):Dynamic {
  504. return new HxAnon(data);
  505. }
  506. /**
  507. Make sure specified class is loaded
  508. **/
  509. static public inline function ensureLoaded(phpClassName:String):Bool {
  510. return Global.class_exists(phpClassName) || Global.interface_exists(phpClassName);
  511. }
  512. /**
  513. Get `field` of a dynamic `value` in a safe manner (avoid exceptions on trying to get a method)
  514. **/
  515. static public function dynamicField(value:Dynamic, field:String):Dynamic {
  516. if (Global.method_exists(value, field)) {
  517. return closure(value, field);
  518. }
  519. if (Global.is_string(value)) {
  520. value = @:privateAccess new HxDynamicStr(value);
  521. }
  522. return Syntax.field(value, field);
  523. }
  524. public static function dynamicString(str:String):HxDynamicStr {
  525. return @:privateAccess new HxDynamicStr(str);
  526. }
  527. /**
  528. Creates Haxe-compatible closure of an instance method.
  529. @param obj - any object
  530. **/
  531. public static function getInstanceClosure(obj:{?__hx_closureCache:NativeAssocArray<HxClosure>}, methodName:String):Null<HxClosure> {
  532. var result = Syntax.coalesce(obj.__hx_closureCache[methodName], null);
  533. if (result != null) {
  534. return result;
  535. }
  536. if(!Global.method_exists(obj, methodName) && !Global.isset(Syntax.field(obj, methodName))) {
  537. return null;
  538. }
  539. result = new HxClosure(obj, methodName);
  540. if (!Global.property_exists(obj, '__hx_closureCache')) {
  541. obj.__hx_closureCache = new NativeAssocArray();
  542. }
  543. obj.__hx_closureCache[methodName] = result;
  544. return result;
  545. }
  546. /**
  547. Creates Haxe-compatible closure of a static method.
  548. **/
  549. public static function getStaticClosure(phpClassName:String, methodName:String) {
  550. var result = Syntax.coalesce(staticClosures[phpClassName][methodName], null);
  551. if (result != null) {
  552. return result;
  553. }
  554. result = new HxClosure(phpClassName, methodName);
  555. if (!Global.array_key_exists(phpClassName, staticClosures)) {
  556. staticClosures[phpClassName] = new NativeAssocArray();
  557. }
  558. staticClosures[phpClassName][methodName] = result;
  559. return result;
  560. }
  561. /**
  562. Creates Haxe-compatible closure.
  563. @param type `this` for instance methods; full php class name for static methods
  564. @param func Method name
  565. **/
  566. public static inline function closure(target:Dynamic, func:String):HxClosure {
  567. return target.is_string() ? getStaticClosure(target, func) : getInstanceClosure(target, func);
  568. }
  569. /**
  570. Get UTF-8 code of the first character in `s` without any checks
  571. **/
  572. static public inline function unsafeOrd(s:NativeString):Int {
  573. var code = Global.ord(s[0]);
  574. if (code < 0xC0) {
  575. return code;
  576. } else if (code < 0xE0) {
  577. return ((code - 0xC0) << 6) + Global.ord(s[1]) - 0x80;
  578. } else if (code < 0xF0) {
  579. return ((code - 0xE0) << 12) + ((Global.ord(s[1]) - 0x80) << 6) + Global.ord(s[2]) - 0x80;
  580. } else {
  581. return ((code - 0xF0) << 18) + ((Global.ord(s[1]) - 0x80) << 12) + ((Global.ord(s[2]) - 0x80) << 6) + Global.ord(s[3]) - 0x80;
  582. }
  583. }
  584. static public function divByZero(value:Float):Float {
  585. return value == 0 ? Const.NAN : (value < 0 ? -Const.INF : Const.INF);
  586. }
  587. }
  588. /**
  589. Class<T> implementation for Haxe->PHP internals.
  590. **/
  591. @:keep
  592. @:dox(hide)
  593. private class HxClass {
  594. public var phpClassName(default, null):String;
  595. public function new(phpClassName:String):Void {
  596. this.phpClassName = phpClassName;
  597. }
  598. /**
  599. Magic method to call static methods of this class, when `HxClass` instance is in a `Dynamic` variable.
  600. **/
  601. @:phpMagic
  602. function __call(method:String, args:NativeArray):Dynamic {
  603. var callback = (phpClassName == 'String' ? Syntax.nativeClassName(HxString) : phpClassName) + '::' + method;
  604. return Global.call_user_func_array(callback, args);
  605. }
  606. /**
  607. Magic method to get static vars of this class, when `HxClass` instance is in a `Dynamic` variable.
  608. **/
  609. @:phpMagic
  610. function __get(property:String):Dynamic {
  611. if (Global.defined('$phpClassName::$property')) {
  612. return Global.constant('$phpClassName::$property');
  613. } else if (Boot.hasGetter(phpClassName, property)) {
  614. return Syntax.staticCall(phpClassName, 'get_$property');
  615. } else if (phpClassName.method_exists(property)) {
  616. return Boot.getStaticClosure(phpClassName, property);
  617. } else {
  618. return Syntax.getStaticField(phpClassName, property);
  619. }
  620. }
  621. /**
  622. Magic method to set static vars of this class, when `HxClass` instance is in a `Dynamic` variable.
  623. **/
  624. @:phpMagic
  625. function __set(property:String, value:Dynamic):Void {
  626. if (Boot.hasSetter(phpClassName, property)) {
  627. Syntax.staticCall(phpClassName, 'set_$property', value);
  628. } else {
  629. Syntax.setStaticField(phpClassName, property, value);
  630. }
  631. }
  632. }
  633. /**
  634. Base class for enum types
  635. **/
  636. @:keep
  637. @:dox(hide)
  638. @:allow(php.Boot.stringify)
  639. @:allow(Type)
  640. private class HxEnum {
  641. final tag:String;
  642. final index:Int;
  643. final params:NativeArray;
  644. public function new(tag:String, index:Int, arguments:NativeArray = null):Void {
  645. this.tag = tag;
  646. this.index = index;
  647. params = (arguments == null ? new NativeArray() : arguments);
  648. }
  649. /**
  650. Get string representation of this `Class`
  651. **/
  652. public function toString():String {
  653. return __toString();
  654. }
  655. /**
  656. PHP magic method to get string representation of this `Class`
  657. **/
  658. @:phpMagic
  659. public function __toString():String {
  660. return Boot.stringify(this);
  661. }
  662. extern public static function __hx__list():Array<String>;
  663. }
  664. /**
  665. `String` implementation
  666. **/
  667. @:keep
  668. @:dox(hide)
  669. private class HxString {
  670. public static function toUpperCase(str:String):String {
  671. return Global.mb_strtoupper(str);
  672. }
  673. public static function toLowerCase(str:String):String {
  674. return Global.mb_strtolower(str);
  675. }
  676. public static function charAt(str:String, index:Int):String {
  677. return index < 0 ? '' : Global.mb_substr(str, index, 1);
  678. }
  679. public static function charCodeAt(str:String, index:Int):Null<Int> {
  680. if (index < 0 || str == '') {
  681. return null;
  682. }
  683. if (index == 0) {
  684. return Boot.unsafeOrd(str);
  685. }
  686. var char = Global.mb_substr(str, index, 1);
  687. return char == '' ? null : Boot.unsafeOrd(char);
  688. }
  689. public static function indexOf(str:String, search:String, startIndex:Int = null):Int {
  690. if (startIndex == null) {
  691. startIndex = 0;
  692. } else {
  693. var length = str.length;
  694. if (startIndex < 0) {
  695. startIndex += length;
  696. if (startIndex < 0) {
  697. startIndex = 0;
  698. }
  699. }
  700. if (startIndex >= length && search != '') {
  701. return -1;
  702. }
  703. }
  704. var index:EitherType<Int, Bool> = if (search == '') {
  705. var length = str.length;
  706. startIndex > length ? length : startIndex;
  707. } else {
  708. Global.mb_strpos(str, search, startIndex);
  709. }
  710. return (index == false ? -1 : index);
  711. }
  712. public static function lastIndexOf(str:String, search:String, startIndex:Int = null):Int {
  713. var start = startIndex;
  714. if (start == null) {
  715. start = 0;
  716. } else {
  717. var length = str.length;
  718. if (start >= 0) {
  719. start = start - length;
  720. if (start > 0) {
  721. start = 0;
  722. }
  723. } else if (start < -length) {
  724. start = -length;
  725. }
  726. }
  727. var index:EitherType<Int, Bool> = if (search == '') {
  728. var length = str.length;
  729. startIndex == null || startIndex > length ? length : startIndex;
  730. } else {
  731. Global.mb_strrpos(str, search, start);
  732. }
  733. if (index == false) {
  734. return -1;
  735. } else {
  736. return index;
  737. }
  738. }
  739. public static function split(str:String, delimiter:String):Array<String> {
  740. var arr:NativeArray = if (delimiter == '') {
  741. Global.preg_split('//u', str, -1, Const.PREG_SPLIT_NO_EMPTY);
  742. } else {
  743. delimiter = Global.preg_quote(delimiter, '/');
  744. Global.preg_split('/$delimiter/', str);
  745. }
  746. return @:privateAccess Array.wrap(arr);
  747. }
  748. public static function substr(str:String, pos:Int, ?len:Int):String {
  749. return Global.mb_substr(str, pos, len);
  750. }
  751. public static function substring(str:String, startIndex:Int, ?endIndex:Int):String {
  752. if (endIndex == null) {
  753. if (startIndex < 0) {
  754. startIndex = 0;
  755. }
  756. return Global.mb_substr(str, startIndex);
  757. }
  758. if (endIndex < 0) {
  759. endIndex = 0;
  760. }
  761. if (startIndex < 0) {
  762. startIndex = 0;
  763. }
  764. if (startIndex > endIndex) {
  765. var tmp = endIndex;
  766. endIndex = startIndex;
  767. startIndex = tmp;
  768. }
  769. return Global.mb_substr(str, startIndex, endIndex - startIndex);
  770. }
  771. public static function toString(str:String):String {
  772. return str;
  773. }
  774. public static function fromCharCode(code:Int):String {
  775. return Global.mb_chr(code);
  776. }
  777. }
  778. /**
  779. For Dynamic access which looks like String.
  780. Instances of this class should not be saved anywhere.
  781. Instead it should be used to immediately invoke a String field right after instance creation one time only.
  782. **/
  783. @:dox(hide)
  784. @:keep
  785. private class HxDynamicStr extends HxClosure {
  786. static var hxString:String = (cast HxString : HxClass).phpClassName;
  787. /**
  788. Returns HxDynamicStr instance if `value` is a string.
  789. Otherwise returns `value` as-is.
  790. **/
  791. static function wrap(value:Dynamic):Dynamic {
  792. if (value.is_string()) {
  793. return new HxDynamicStr(value);
  794. } else {
  795. return value;
  796. }
  797. }
  798. static inline function invoke(str:String, method:String, args:NativeArray):Dynamic {
  799. Global.array_unshift(args, str);
  800. return Global.call_user_func_array(hxString + '::' + method, args);
  801. }
  802. function new(str:String) {
  803. super(str, null);
  804. }
  805. @:phpMagic
  806. function __get(field:String):Dynamic {
  807. switch (field) {
  808. case 'length':
  809. return (target : String).length;
  810. case _:
  811. func = field;
  812. return this;
  813. }
  814. }
  815. @:phpMagic
  816. function __call(method:String, args:NativeArray):Dynamic {
  817. return invoke(target, method, args);
  818. }
  819. /**
  820. @see http://php.net/manual/en/language.oop5.magic.php#object.invoke
  821. **/
  822. @:phpMagic
  823. override public function __invoke() {
  824. return invoke(target, func, Global.func_get_args());
  825. }
  826. /**
  827. Generates callable value for PHP
  828. **/
  829. override public function getCallback(eThis:Dynamic = null):NativeIndexedArray<Dynamic> {
  830. if (eThis == null) {
  831. return Syntax.arrayDecl((this : Dynamic), func);
  832. }
  833. return Syntax.arrayDecl((new HxDynamicStr(eThis) : Dynamic), func);
  834. }
  835. /**
  836. Invoke this closure with `newThis` instead of `this`
  837. **/
  838. override public function callWith(newThis:Dynamic, args:NativeArray):Dynamic {
  839. if (newThis == null) {
  840. newThis = target;
  841. }
  842. return invoke(newThis, func, args);
  843. }
  844. }
  845. /**
  846. Anonymous objects implementation
  847. **/
  848. @:keep
  849. @:dox(hide)
  850. private class HxAnon extends StdClass {
  851. public function new(fields:NativeArray = null) {
  852. super();
  853. if (fields != null) {
  854. Syntax.foreach(fields, function(name, value) Syntax.setField(this, name, value));
  855. }
  856. }
  857. @:phpMagic
  858. function __get(name:String) {
  859. return null;
  860. }
  861. @:phpMagic
  862. function __call(name:String, args:NativeArray):Dynamic {
  863. return Syntax.code("($this->{0})(...{1})", name, args);
  864. }
  865. }
  866. /**
  867. Closures implementation
  868. **/
  869. @:keep
  870. @:dox(hide)
  871. private class HxClosure {
  872. /** `this` for instance methods; php class name for static methods */
  873. var target:Dynamic;
  874. /** Method name for methods */
  875. var func:String;
  876. /** A callable value, which can be invoked by PHP */
  877. var callable:Any;
  878. public function new(target:Dynamic, func:String):Void {
  879. this.target = target;
  880. this.func = func;
  881. // Force runtime error if trying to create a closure of an instance which happen to be `null`
  882. if (target.is_null()) {
  883. throw "Unable to create closure on `null`";
  884. }
  885. callable = Std.isOfType(target, HxAnon) ? Syntax.field(target, func) : Syntax.arrayDecl(target, func);
  886. }
  887. /**
  888. @see http://php.net/manual/en/language.oop5.magic.php#object.invoke
  889. **/
  890. @:phpMagic
  891. public function __invoke() {
  892. return Global.call_user_func_array(callable, Global.func_get_args());
  893. }
  894. /**
  895. Generates callable value for PHP
  896. **/
  897. public function getCallback(eThis:Dynamic = null):NativeIndexedArray<Dynamic> {
  898. if (eThis == null) {
  899. eThis = target;
  900. }
  901. if (Std.isOfType(eThis, HxAnon)) {
  902. return Syntax.field(eThis, func);
  903. }
  904. return Syntax.arrayDecl(eThis, func);
  905. }
  906. /**
  907. Check if this is the same closure
  908. **/
  909. public function equals(closure:HxClosure):Bool {
  910. return (target == closure.target && func == closure.func);
  911. }
  912. /**
  913. Invoke this closure with `newThis` instead of `this`
  914. **/
  915. public function callWith(newThis:Dynamic, args:NativeArray):Dynamic {
  916. return Global.call_user_func_array(getCallback(newThis), args);
  917. }
  918. }