Boot.hx 28 KB

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