From 10802b97627afea02f2466c67da7bcfa436a2132 Mon Sep 17 00:00:00 2001 From: Ivan Ponomarev Date: Mon, 19 Dec 2022 21:13:18 +0100 Subject: [PATCH 1/3] php-simputils-130 UrlObject improve usability --- src/models/UrlObject.php | 181 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 2 deletions(-) diff --git a/src/models/UrlObject.php b/src/models/UrlObject.php index 0cbca9a..77d26bf 100644 --- a/src/models/UrlObject.php +++ b/src/models/UrlObject.php @@ -3,6 +3,7 @@ namespace spaf\simputils\models; use spaf\simputils\attributes\DebugHide; +use spaf\simputils\attributes\markers\Shortcut; use spaf\simputils\attributes\Property; use spaf\simputils\exceptions\NotImplementedYet; use spaf\simputils\generic\SimpleObject; @@ -21,6 +22,11 @@ use function spaf\simputils\basic\ic; /** + * + * **Important**: `cs` prefixed methods here are "chaining setting methods" to be used + * instead of properties of the same name. For changing multiple aspects of your URL properties + * might be not the most comfortable solution. + * * @property-read UrlCompatible|string|Box|array|null $orig Contains original value of the "host". * really useful in case of parsing of * an url. @@ -48,6 +54,177 @@ class UrlObject extends SimpleObject { 'https' => HttpProtocolProcessor::class, ]; + /** + * Chained Setting of Protocol + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param ?string $protocol + * + * @return $this + */ + #[Shortcut('$this->protocol')] + function csProto(?string $protocol): self { + $this->protocol = $protocol; + return $this; + } + + /** + * Chained Setting of Host + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param ?string $host + * + * @return $this + */ + #[Shortcut('$this->host')] + function csHost(?string $host): self { + $this->host = $host; + return $this; + } + + /** + * Chained Setting of Port + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param ?int $port + * + * @return $this + */ + #[Shortcut('$this->port')] + function csPort(?int $port): self { + $this->port = $port; + return $this; + } + + /** + * Chained Setting of User + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param ?string $user + * + * @return $this + */ + #[Shortcut('$this->user')] + function csUser(?string $user): self { + $this->user = $user; + return $this; + } + + /** + * Chained Setting of Password + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param ?string $password + * + * @return $this + */ + #[Shortcut('$this->password')] + function csPass(?string $password): self { + $this->password = $password; + return $this; + } + + /** + * Chained Setting of Sharpy + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param ?string $sharpy + * + * @return $this + */ + #[Shortcut('$this->sharpy')] + function csSharpy(?string $sharpy): self { + $this->sharpy = $sharpy; + return $this; + } + + /** + * Chained Setting of Path + * + * CS - in this context stands for `Chained Setting` + * + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, + * but allows chaining due to native functions/methods nature. + * + * @param null|Box|array|string $path + * @param bool $extend + * + * @return $this + */ + #[Shortcut('$this->sharpy')] + function csPath(null|Box|array|string $path, $extend = true): self { + $this->path = $path; + return $this; + } + + /** + * Chain set method for PArams + * + * This method can be used the same way as property, + * but allows chaining due to native functions/methods nature. + * + * @param \spaf\simputils\models\Box|array|null $params + * @param bool $extend Extend (by default) or Replace + * + * @return $this + */ + function setParams(null|Box|array $params, $extend = true): self { + $this->params = $params; + return $this; + } + #[Property] protected ?string $_processor = null; @@ -74,7 +251,7 @@ class UrlObject extends SimpleObject { protected ?Box $_params = null; #[Property('path')] - protected function setPath($val) { + protected function setPathProperty($val) { if ($val instanceof Box) { $val->pathAlike(); $this->_path = $val; @@ -100,7 +277,7 @@ protected function getSharpy(): ?string { } #[Property('sharpy')] - protected function setSharpy($val) { + protected function setSharpyProperty($val) { $this->data['sharpy'] = $val; } From bdc398e642fc79c2dc0265d734ab8fc7184aea8c Mon Sep 17 00:00:00 2001 From: Ivan Ponomarev Date: Sat, 4 Mar 2023 18:14:40 +0100 Subject: [PATCH 2/3] php-simputils-130 UrlObject improve usability. Some polishings --- docs/images/UrlObject url handling explained | 1 + .../UrlObject url handling explained.png | Bin 0 -> 83405 bytes src/PHP.php | 43 ++++--- src/models/UrlObject.php | 107 +++++++++++++++--- 4 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 docs/images/UrlObject url handling explained create mode 100644 docs/images/UrlObject url handling explained.png diff --git a/docs/images/UrlObject url handling explained b/docs/images/UrlObject url handling explained new file mode 100644 index 0000000..ee7c4da --- /dev/null +++ b/docs/images/UrlObject url handling explained @@ -0,0 +1 @@ +7Vlbc5s6EP41fowHIa6PidPLQzvTmWSmzwrIoKlARJZrO7++KyRsMFDbPaHNae1kjFhJu9JePn3gGV4U2w+SVPlnkVI+c510O8P3M9dFgR/ARUt2RhJ7nhFkkqV20EHwwF6oFTpWumYpXXUGKiG4YlVXmIiypInqyIiUYtMdthS8a7UiGe0JHhLC+9KvLFW5kUZueJB/pCzLG8soiE1PQZrBdiernKRi0xLhdzO8kEIo0yq2C8q18xq/mHnvR3r3C5O0VOdMeMYvbPn0gT0+foqrr3KL0HNxY4PxnfC13bBdrNo1HsikWFd2GJWKbof8Tp6a4U5/XWi/W0gTKgqq5A6GWEWenbHr3m4O3g59K8vbno6tkNgIZ3vNBydAw/rhAp+gAZ8EHMzeLQVsCZKIJKYjeF7r6N0VohSrSkv3Imhl9lpPfToWWF2J4EJ2lM1c7DheEsd9ZblSkPZWAezN6LB68a3etv4/x0AQOE7fAN2SouJ0nojiZ2ZOG0iSYQMRlM6Y5rPU6pUPefl9yiQyF9dc8OgOznJRDJ+hHRCZIRgL2QHmAnCXlplvmbmmZ3yL+BzTAXyWy75pQBBZ7U64D8S9XOsNPSpxKGqTXgUUyj3SppQU3+jCrq4UJdVrZpwfiQhnWQm3nC61Bg0QDMDz1ooLlqbayN0mZ4o+VKZwNnBUgAxwpUyprkQH7jJOVivb1ou1RwGK9gseQJ82yozjWx96ToELngpb3NN4C1GudDNZS767kyT5RtWIB1sBOzgTnXDmUchyIdkLDCGNJigdAHQmyjrKUvvp9QPwE7B3B+LhThUPPIr1OeqEpalFXSo31oW3uoBh+1QOQLVYqVb5aW3dmqz+k/qPtXqnd7pA3bIyG8YCJyGlObEVYbpFav+onDYeaJ8t1QRooc9IvTx87x/uHgWk8v2N64wkuYC0W/KaOeUAJ7S8CDqOsv3VE9lqccMuiwn6iY2iIRYzGdL4f57Z4ajjEzc6k9phZyqnBH+c2sVU/41Ru4bDtXkYvr2Spitp+p2kyf810uSFcxxEHjy6+26AkR9NVMThv8ahRuLRHD3B0QO0DkMYxV5oooF6oOsFQ6CL5n5rluP7E4UvmopyVfqNy3SU60ut/kLKBWU/MIVISXYjM45Jmij57pil1es0212YBCLFSseqTM2Lpi7iXancJfXkOd16CqN57J/J5kI0d6aqm/jNETp/gOX+ZkLXvCb+XzK6Ifp14Fm/wPSu9OtvoF/x26ZfaOj9+F/Nv0YC0pwXuM+/wh4uuvEI55osTEOvGl+JZxm6MSHTsnzmDXGtK8N6xYqJ/B7D8lrQFZzHtlzHn9dI1zy2XM684PbwA2zd1/oZG7/7AQ== \ No newline at end of file diff --git a/docs/images/UrlObject url handling explained.png b/docs/images/UrlObject url handling explained.png new file mode 100644 index 0000000000000000000000000000000000000000..c522157ece909f80c3a653a0f37c8b7712523cd9 GIT binary patch literal 83405 zcmeFYcT`l%wl9oG4iW_+Oj` z0*^qsJIc=4!P(yK?{~sN!VrET;1baXi*i9^g@mqeVgf=?i0R+;tsU)=|7r*-AOtkP zV+;m!K@h-`x--(pANcPN7qt`>xeq+adU&|m8`;~aIeT*nD?%YK0f^}JCu++28tPoa za=^2jv#UMuPs!fa6?OfIou{>*fHMlH4u%N81jK+MIVVq)J5Wgx4)iD>0)|5bz`|hQ z#eY+x2o@3m3qXJ}J!=PRPv`&W?V3n?Z)?ZD3Dj`2QS)?Cwvk8J!gO5i6p(KI@V^Q3 zwfFRL2DtJUkN(~F^~c^A5BtAM?d*M>ZGnLZb3qgU-r6}^J9=8X167>=#XfZ>Ic1o; zr@fX6N=HN%5P^uX=HIO&tpN)DYMM)4h)WjCB@gC;3;%T&x-LQdeV7018U9zf=)cN^ zg#cQwyM!6K+1Nt8L>06}zz!%sWff~tZD%`eb)5iTeRm^0_W(an6_lErtgfo5rxuJ$ z7?24<4WsL&p(t;oZLO?nZ4Y*JQFK!E5mU9-MnW8w3`Got{eVh(hT3jOS3?!x(H068 z(t>M4G{o!_9d!XV%IX?;3+pOwpra!@THLoGWEdkqs`8*QYJqduUI2nQ6}(-WemE~KpKZz7`Yk93d&Lu?Rso|-7I zvX`@~k)oZjlB$uK4axzo?;&a}VgLo&G&Z`{86%(#HAgfWDl4jFq+n!^v{CkixQl2( z9HC-ra9f{YagH~s*VtaTSM%ZxrY>S3U5@8Bkean`{oirLx1wH&nqkgB@!s=yeXly&7yRdgNIg>{tOWs!2m zZXRyF?l$i3rVtD;9TA8CeOY6S5e5eJ&_N(fVFsci@^C#dV=XUDZ9NZvH6^sZv7>cn4_1yjw)Oj zg9>ml@)r};M=7Wy9X)IynwlbVU?m?rc}-J2m?+E@qYu^fHc~-=y%1Ufj`~Prd3OU_ zCya`ntGc|LlBTM&qn)g>jyuN1Q_aBF*F#K4+fM<8xVBdabtio%18r>;Pq3CfTEWBG zP0?5n1p%XsAU-IB6Ho!Hqot#6Xo5Dhfm$0u?A*}-K%pxj4YanmF&b$H^N@wus9==f zCUUA6B{wY_1w#`X4@GNVpc)D(Z(?t)3ik%<=%P@zVk*A+{%{yz9u<9*9gSS!zQUTK zB7UwuCUQbXK%ttomc4d_9HrJe<9+_0vPx z72^O03;F0N+WPw|D7#<)ON(=^o3ck+c>JL&qU>nh7b5JEayLh`N}CjN#{ z6bc1Qb}cVO9Z@-TC=%-F=A#W$5Hav|M|ofj9nhK>h@OF{kk7S&cU4zI7+nuRL&Z=< z&)CF8Lj zYeHdwrYfpJT~&naM6J<)byalHRL}=VP;}HX28L|vs&4EnX9GML2dIgvICy|X6t8Rf zY3uojX?gpi6oDW9!lI@Q)>)KN4IQ1*5UP)3T`sel78 z!p0h+a1m`AKUv`bJHSjUqD++l%c}^J)w#Cz*B4;)6#wlh{&E|@?|*q^h=N4)DB!`_ zaa81G_5Cf@-x7N3w==a1s;t%@%>+{A(B}~2KJk{t!x_#V{e)JB z@;6UC>~}p)fEGJ;DRfiPV*GBJ9gLD};_PiJXyPplwG0!i-D)USubZ2jv+9z6i1&}H z=_yg$OUm7MBcu92%m4X`>;^H&oQQ)R_dhRIrH6>F#W!X&bT_Cd|Km!a{0{wRO^!J< z-d(IDvKwmskD8R%&EEXaHi75=ZT5ee@V83*uO|FItqI#ic(?Q~Nh)E?axconbevlA z;s^EC#rM^VG4QKjHjt0HV~ycP-?62xkB68w%-$M_pjU;i?aQ_kE?Sd_Jh_^@K6KKC z?%>aJU**>#VM>A}_t?b3tkoj2rqYpFj3YeyH+#edX~d}>4i?sqXPk#d$S+$QB-l6g z%zs=Ll)j^|TNS#6CoTk1b4Mg(5kG+L1p(UGVT>bhZy7^)&P6Q?>k# z#pNsmqAE{nA`QaCd?Qa<=#SIl^r_(S^OL&oAE&mS3Ru5;d&V)QRlFaL@7B24O zV%W4iXgxtCEiUf{R|Ot(Y+4>Eov(jNtaAym`=dioyq6mvfNX9af0Qk=xqhTj+IlmA z-EopA9`gdR`n?TWB_7DMxh@qGNRMpcHlV)Sn|b5+Y*DmGVdl*Rsin}x4)W(L5_F;G zJA}7bC8Hic#-Zgireq5_2ANhOS)fn3#}{Vm)5fd4c!Gk*=32sIiOBXLj;N_a4r(sd zBRnPQgHIvnV4BUJwc&Gf52qVL$CxP-DLwrxZrm6v@o^1Jhwn%rXQgkVw%U|m=JL3V zWzgrt&FX6i&kkcdtG)6))<#wE@bNh?yJlT)Yl)-juZW1L!+ z_J1wPx01h$-JGe94!ydda~u%pJXnjQqV!GEU1ylM z`#sz{&)uz-<2Nrs=&wuJ2w z-o<9W3tSs~z3O>sNKl1NS8-ss6f6KO53R@_nnpJ2 z@-G%|MwNn22$)mECPr+pY}{7u^=C>8j6G>k5XY7Jx#05dT5-6?#$*^# z_+Wd?qwOhFf1}(&r&dG+`53$VWaKR9kh#pEFPfBIVD5X48XUcVr}~EP+3v3%+l$i! zq?R6{%iT`v=M8EED+l)SbWQL_z7Ylgqlz}c#&hc5;gj`&enEg;V1OZ+XF|?4@;0;M zHXn0g%{cw+%xKJ}r0I!=f5~pfK`3Pu6w_12XpBTPVHWASoJFm?ved_K$D{Si#X02; zC=%+deA@OjANQ=~E&M2EOQFdTm}16=_mheJ?vEb^Z6Dfy;Jc(Qt7LxmuC@~Gw(Ghr zQ%|F^r~fc(b4`7Ulvi=#aEM&lD2MaKQV@ZxA97)TS_^HzsI`2-TpTBR>awK>Pgx7y z=$H?9pOE{jyz4?OQknbWLpWu)dQ&&AvPB_IzjtQLJBcH+Ln3^OhS*TA!^c{aA39vA z1w+~>gff=u7;=3-@A26kzY~99BpBsST!+T|2}nO(HChvQvY^8__&)b$c|PVojHdlL zO#h6;G46}IJ}YRf$aBo}?Z$;f-3xBSix(4fFQvtBHO)GHYb`Hq&XY!jep=(4dkt^+ z{T$&mQD*PIRez+NEl->-6-dW#`ladjaGp+un~B>@-Ne_I(qV=$6l?4EoW9*1voFNP!D0U_Ke`vnKxDxRfG6%*)3= zkh`h*sa`4a?E%XRZY{4L1t;4Q*y0PoGc0fAd;a=nTps|9xWy8!C4GXI8G0#pdbqK6 zc=s-LTJi7)3uRvT;Hh=&pwD*#HwjPTlkyKut9}pOs}89U*MF1dK*q;Z!WV9653`-%ngG86;gL=-i~A zL6T6MXk50X-UKqJnnU{_?3)D3Y$B)9+}0Lq#@0#U{CJe!-Oeya^>%@IEuDn#x)2*R z)x=lVu>?+~TX(fGn3dvL6uYY-6PR7Pm;}QZR~D_w^S~bPzpxbINQ0JNivF&v(Dd zk?@xxTjml^&42P2BRW#qIH#Vv_TO2#X%Mk6I?2gXwg|Z^Ks8B&Mo<XRBz#CUX@g z(@!W>G|kvtsKesK5M#?5O^F@0az1P(af5OCY$*vPB85GmlRbR&Wm?WX_2;neJGG;V z%L9nwn-Vh_>!Cz7!DIHIwzOTZn{*s!U2nPuf1haEdihu!6`xou{pp`CQFz5EFGyYw z-6_vSWLVA$FZ7|NRLT|89UEjM6oOcmw%qaLUI>e6`{#RUu@icForEwKh%tY%xF1{R zHA4)oc$9x@-g|6fd&-BAc<+N!oDM%a)t2Zb=h9}~s=<4noZH8>qylj#gs?a33JI1t z)OK3#(Kg2))RxzGg~tYdBfWaRTAp@9B}_jEsmN7*{on`qtILS+!bh`e&+!s#xvoHN zErF)6n|dC zdHPrw)>^5jydb%81EyWI{ph2veUQ9f+iTt{V7Z~QePHg6<;2%}aW^G_tB2GD;AZP4hns#jW&xNn+}DTem;|o&KSW<=Y9p*@{o&8cz|~WM$odsQ27m? zlCqOY&zr~u4*d5LPgKNz95YwH;n@LyOR8*$OwnpzyDz*Lpd&JAAhXKux*qiXx^GOm`hytmL&6}` zl&TX$*ECVy^oHLs?R!q)7&Z6uEY)^5x0&U;Ak4e^r;9;HNUI+_`4o!thJ}o`xdsAc zG)c3}?z~()K1l=drV1!pdck`i<`JN?U{-&%EJr6>YfVfJ%$SlO`@{oDE^F z_ojtLj)`m?6im_FWFjZ~%{g)PV*c>w)OPerxQNQ|-16JBxjt!k(=%uP!gE&;qN}r~ zljzgqvm48;=_`v{w5negpT==IPNstpJkAK|AyP#4@iULUt5mUw;DU_?x`AH-0zV|p zs1Tb%QMBI4u%nErmruoe{hWoBgz*Y?3WqR>|<}9gYlh z<}^V_o-dZ#yk9);$a(f%*)o5?OJT{`?%xc_fpVZ%Wg)~%X>8)Ozjwco!!J)Mvf^yV znNg$8afsE^C+6@;&+M=FE^nA6E5~q+BX~k`X{%NOTbDNdxuQ|Xg5i1-VlnOta+rl5 zcJLz2ZyQ*Ac1-D>AJ0(=;z{^8TF|=ImifYLiEk0fZ`YqKuYANNm@9{6+%rsi|JZhu zLQ1dy`Nih9ALL<1NQv9X3{wNvZ7}q_qw4yg|)~gWnXK_ho%>gd^M4#wZ0}2`Kj8EDS<}z|Ge1DAx`{Hi5 z8?vpwZXr!uV7XWF>O8(HvyuGRJ9wsUx1-gXsWpekVf7*^j&icpmPpk757j*L34vTv zs>npxdnHHfiL&Kf_xFlS2_QMKy**&nvwbu>YW}F)p*B(6M`y2Gf^7V9hZJPh8X++L zb&bg%bMg+o*r%LU@oKnQJX(P*^RD9Y&uXuUFB&OjIH}Mn+tZRhIs`4X$TA$w&!d}s z^u6qt_1@_Vu2}T>ah>+chXlJ#vW+ZC5*fatqv`5VN1OI&PMw0}gA};4ZZ=Q5>&9dw z$f!s$8Jf>iWbrd*eZo@yXAK1#^VPYRz;umI3(;OG)G;OGr+t%cHXUMqrG4!+#@cVo zLUwby_?;pDn#9t%H+*-5{DrO*3ulw~ZY)b4t1i0I8qxBIkmL=> z5Z2YPvZ6>qXPCX8wWB8|p{M(Aa6D-dl9`#AJ`ED1{1nG#Q*obJWLU>5$dzA3u{y1t ze{mju{MGda8{1rd@#7Drf~vyj2$^Q1g)fu=IW|Z#)7T7;#mdT#dT;CYPFelb-((#5LB!WtArqv}vf*N^H5+eP_3VDn zt%W_KOmx8gDqifL(ZU#o7&_~pH9jA;n*&_7de6_#d;0_%lkXVyl$0K0(E1rf2 zZtz9h-;Xds4IqlXR6jS$UW_+1?!k)E43c_& zCOJHssaxj4v|mZa5JP+leQUH(@&?z(_GfRxa9w>WM?i?EjWHifN96vQ;3`Uzo$!?H z;j>Gwl372QOaChh5uPD-d7{z_C8%`E_Ht|K&bXrfZAArd{N0iJL=z7<~!hWS|z<8KHec}gr$sO zXTCE_3p*+%f+|Eb{t5^tq5cr;U(WcZ83^s5eH(6PXusR@yeqhmP^O7!V!1QL;3vou z=x>N8YqYfeLkpHRQiyu6qb3#Ec#I>TN^!@H4`6izt z)@nGN!zHt7CiJR`RXXw>c8l#m7p>u!9r}BW8#rgl5DLro|?!QAfgk7PhHeM;}Wd{ zFZa5jpL4TZ%Tf>f^)~T59HBV{W=4iqH!JJZ6733J3hAf4+|neJG8gF=^C@?nlkfl) z_0PZbk<=Z5D}m&jU!JYN5(`vAII%^26A`q6bP!Nf#RmTY{<&XY z{v^A0r}QO*Zx+`dGUDF6*&L~r8GLB+(IN8IMburDGIrYzlDSxD=5y4Qyoy3w&)mu2L$hT`s0b!?NEuUiE% zy4KSk<}e_Dyw#mtnr#BHoc(HCdw7vxmJ@}g^h%)XfPo9lJ~?BQK2^Q9Lm!ER*JSid$GX7`snfICkH2wD2aQi2wNe_mtU$xoBIf3E`&A z%~pyq4hk1H7D=K-=WL^HI;OLuMKsIHcl z4X{e_4ct~3h+{F-3O>a#DHrCWWN?>)wAXIo=BVC~m3=r?^oD)$c$RoWpQ62AFZ9_k z!fEAWE>9vac?DZNR+z0H>MD8C>f&TsZuRjXPIVeC8uz*Iebvdk5S`}%wJ*ei7Q;w+ zW=8_bAg6Ct{jYMe;eP^eVkAsU$DpbX3IoeII@QYe?rx*~?dF!gc`FgQ&o;2|z0j@? z@G1B(AWF?79R!!YJhsg%{Za=i22Y=|XI>n-iQL~C^fN241h0$Lb`nvNP))6V^+zwo zfRO$H_7(fnHhA&xH^l2nUyf?F^bbp*N46ep68?ZrkPA7LS1tNSxg1O$U%=wmRs=0xRcCFX=)sq^s(Jf!o zZ(V&`z5^HiZLl~zV4P1{24$i9%+flg*}sZ)oeCvA2zZfhpLnz}K}AyVQl>F7=IS{< z5dPY#^^$A(({if?b#}i*85cE9S`fsr-0T~cgtT@H=(+jFSS2navz5F7QY>=|M4OUy zcirFoHlj{!_HBM%wTfVfCsCcAB<=HfVZlnQcAY;@NDw9UQdAAc@asEi==BkmQJ=va zx-xQC#`%MgrC;Ex!%j__jE=hdO153uR~hBX`s|$1tR<|pLRv0HZ?vAn)^xr(N|0ok z4Nr1zxc0Zc+Sq}UfEwNiX^AhTe(#UUE2;2W(R@iGPbFg4MJdD$DL6T^Y?`UJMPHr= zOjmo|INBm)h^K6w>0OZ@HlK?X1I$DC0iD>PYf?&oO8JdHKT4(ZSn3Ies1Q^FW_Tz* z)BP9313j-kkuZub#X6T@Y6ec;@J*2a`WAfK^MeGL|KK7mV!uhJZF^Z-DJ~#4fHhp_ z8MV1i!@0Iubs1@ATpx5MUlNmg2dy+P`bjSaeRg;s2r3iG${wLNCZ!>4O7Uc&p*+}y z`4OY|G>^*dv2Ja*X{Xnrj>q(^O-?a%P$BE5U!t&ssnD&IwaDyB!@}yW*StX!p+=*d z3+;W!8!%qa7CM%|!=T8ot};ST(m`>)3gUiuO0@jK6TWuPS~T8WsgoJW%Nm~(_NLurEK~{fqZanCQ&%1l zZS72}`&^*WGdx3@7u$Cz34XPSiTr5)hzR^7Xpp_hL{_5HYsdIdR>GHzLAdR-+JN_6 zfREaK(CMe!Z;sZ>A4cA4Y6xzsoHgCxdBPK6mqpy>4Dvo%8IvvUo%ezzT=A=UJP$7R ztfHRKdB&tsLc$KQLJWV2ZlsnwLish_7V z8Z`NZ_S@~}RjIG@={?IG`qYWLxzye~-dUx(W8RI~U1~m98;gbUxIOFM0ZusH{!Wxp zvyPE?#yNP1Gi=j~V^E*2)9h)ZbFFdSIDsxRA?Syrz zSEau;%c?AtTKa|X%*HxXWO&EqXjBuBp7WE_v4GlzPT6U*wGxM4iM^< z1dEQA3gOrYQm)Z`-vyl$`4S=-3Cq?L%@o$X}c z)GLlT<=L;EN2$yihJ|qpbIs0H7G&WH7pJ@)XNM*Q#yby4Xq{qQMnamN;18sIAEBBk zAGjs|`#VMB8Q?gX1)_-^D}3F#*C!y8*dO&L%jRCl)b~nlDU>$1#^YElQxEq=Zu3x{ zLK07`3^z5}I)tQ(sN7HY*Bzu(U!T$rNWtLpZQ!i}3n||E$76}LWd7;b3%0Z5fsVl{ zApFcQ;5c5#L-%CCm+g=a#COq-av_tFZOe7h_jTO63Im#4c`G)M%_^}K@t+sDd-cx$g>h3cRwaVLOuBUZK za^|Xn+u|lsqkKJq?HBP8w|oQ+Iao^_f#bsPg!-&Mp_8M2>g>QaKPJl9^W|D%xknJ!d zgB}DMbl=_G`?_Ts9O*D10-0fo7MPhHZl!{Y4%&l6Dh`bG?3xdWxYUx#U`o=xG`5ZD zq#HBif%h!CDq4a`(@GV;#A|yqRF(O<-YN#4$chA>Q?Vp%l?TB+ca*EZibg~mYn2q? z!eSI^o}vUKDI0HJFv`B%x{ygI!`_yoWcFs0kXEB&@tzA{*!R$3|lkTa*F5Gia{tVBZvH@ zokdCn-Jjh(gI;W0wK=d| zfX;wBRlS!M1a2N)0H=X~W4|Qoahy^&{uH0nd%v!*<@R*;yfr#173ykpvKvW3z-j7L ztBpIG=P^+=^zD;a(6O}ug_nQ8Azqox@n^Ilv_FN?bH8q>+m`t5GQLhpNz{#4e40OE zKibiJZ?^66*fN1&sIy*ENJbx}WG$o2ncb{NKxT4`{MtBGve+=mm~9x=xWe2-zGuJ9 zGKmsb`(Rk6Bp}}Pj7VW*bcpjuwyH(%aT^J@tw9yRMWLXSgowbpiykfiEQTPKP@s83 zBJB8sib~K#zbcQJagJqd3vq-;h7+?GZB#?w9=11v^5cGLMej z;9eWgRkmd$n^Y-t46bU#FU5`rI7vZ=zx>NBeYH*8q)Ko1BNkODcw$-deBZhCmyJ(D zg6>bP3eBBpSxS-5G8twNd!?=mNd*=K33|a|#KPx_Q6?jw(^G7nrc(q5u>N0WPJi~3eL4tuE!bAIR0 zDtQ{|d{f=!Hs~io(tEjWEU5TJ zNkDJ1q-T&MEt@8`UjDBzQko!duU|KqL!4jTcT1Rmx}pJXh?10Lmhj3uq+N}bZ!de- z6-O9v(aMKZb~2mq<*6AvU3T=TdYe*nr}Q=x5MZz>VTgE-wsobe%~FcW9I|$Aj~31S z!&KHoBc$vm|d5%t9J`-i*t>|)Z~MnAVdup^trBu7%S#3ul+R?aVuRNJ?qQReCGb_#1QP_NJ(e^MRn&61sv!@DgkHK`ZP`FQ~i{ifRAo1K3oVj)gt;q(J^mhS>!MvKkP* z=68L@w04T$P@Y{Ohge*92HVADdG*3<7}#TN_=@)~9N&5#eW2&U8h007ad7JE-c@=0 za~7&_Nc1gxw2~SmYCuWNRN@V#1h9gl-rcOjsL6Ykt2#h{N9o|n2)LkI2$Gwz$A@~j_&pYU z#KMc}t;k(Wv+1u8>riWZLKerBKha@M;M>Ue^Ws3aXUly>DBRx6$Jn4i`paYiO)H_v zVe0&oUNxJq63d5!_ln=TeG4C6J+X0irwd!%wk3o#l(bV%-$v*x1G`mQ$I9pL*i~Ou z6M24pOO$E%Tf-^6K3=Q2C_jNO^!BuL1r=fBn9$h`uAWC!DQvyX8MS^b9~?s2Q?Dj| z-V=%IaPc%@q7cqPbB6?whHzJ=1V7_RFcz9``8EWHwtjxxcJX9%Ci9K^598WE7Prjr?RF1K=UrfC*I#zzVr9MNE zAD?s8RFkFpO6Df@QqUaNty>)#*%#^twKhrwsb&5Bd^LLkW{9ZUBgQAOua{mJ^v3jF zFbgM|n6W#zc!cw6)?LsDXc)UAW*APFdzoXn>`oT!h1L^(wQi6*+nN#$t+hRP%J;oV zk}H#}TVvv&Jad3hj^Eju_vg-6AgnyR?$e^ZR5C9f6gr%nFeKP^ zic+zPHdGAYXAAD(Bk5j=G(H*dAIq-XZ+2(;=_rZd~Z=n(H8`C+Ta88JByTFG!<#yJhGscKKfNZR`}>)W5}DM5560bTwj2IL@y~LxP>t#Skf!P zxbE=`$K-nwP(c3Vm(q@2RbWmgzOA+MvgVogUNeaX*W#LckIvG>B~mUZ`ZaDKD!VDJ zKGH~!M|ljxVl_(fm13@E_O~=K{J8X5sz1j`8GT{+!37jS(HZ-Nc`2EgezN;Ctw*VB zoA?CHQ@pH~tkX@jT12izL|K||#lvb}Yo&?y>2pW-8XJUbsTx~{vvE*O)UoWTo2*Ce zVvJK_Z%;DwcA#b6y$$wVNS%cWHHKfwyxhA}u+=c*56N{;ju;Cqu}RkBQn5e0$SNtz z@09vXnGK5$Hm^CDDz6a-F`&j_rwwO#6K#MZSukjOWbEb z5JzTCg{U3a6rD#e(TpdW z{?ao%!E4s!hMmD!4$|22)*6mere5(XuyC+Ta7c;xRE~wPUz(!hW8YqAHk!$sBia1WuFzE^D(L!T&30K z-LKPrUK|)6a_U+M`BknKZ2kI6h}Cpbw}in9k6-0K-zM3XVxtg?#u^DgWKM1PkIB-d zSWvQQt?!1A&z~Pea*IXg5|72aRPTh%{d$e_Nsk@~HW2nc1vMyo&2r)`Rwv6ex+u4U z4_hBtk?D~#F|H(|7TrQ5H~V00Kel2M9`C`D>Fu_(TIpyFdtc6*xqBsr1*e;DPMDSN z)IDE)KWB5|;#Hq&7r0g~S%xW1KV}QnC;jwsrNDim?T*H4iL$u>a9d0VF3I%S@0t23 z(AsTa$KAkF;+EwSA2xG;ir1bLMCWVux+dy5Q;X@m-4LXClI!HF)toCi_Mef*mh+tyK6$2*G}8I&JDl;I3W<=hs1JZFlfo$@zxlZ4Y4LuhPRiboPUG@UQv{Don~Gr`4hq zXq@67V0oMbB2@0%4t5Z-VRLOG7bjNiZ`Ymb5bnrtEkxZ^QXv96OA8VG%ySd@Rj6fI z6Wc^+d3hM|Ic|;F;Q9!?g)Fjvrs+e@SuAHII((X8CJWIO@!1b|^Zmd+zwy_V^iS-5 z2ZlJ*?MI5wrkw}BqE*UoE?{5VaoTR8SNjQ(U~G z$w*Cd$v|5$T}ud+C_o*?}byIIquw?1u`u={n(!1o+k3kZXI`&zsz!Q{NVdhcQuZZq|SrpYWdHN%;)#D z*9URhVzN0n@rO^W)u{Y030}PEiFKP5?QHlyY5|W!IJwq!k+Qb!9U@YiB4V4L?-o2-q`&y0|q7TL$SCAj&$ zIe~c({8_MHKLQLYSG}`m#~_O5pCVM;o04yH;IdW|6Gyp#?u@^R=t~PsqweZ{xguZf zX~|ej=_Q@4uT}ii#E2eW?gPE~U6gR!`X2+e^rgNjT&Rn2`*Y?#7J@@`I=vL9DWNY5?J|gPl2#39BRNye{B(7w$JPw_B6z-| zB=pib_(;Ks2t43)aoV4Yh4D`O>e89@_hq=JVmr$H6kDFEuXbulBeyh zjua?BUJ3q^3Sxb?z`!wu4i0$iWXv)YKSq9N@muhAP;hW;Ue^#0<>hhf1GlC@umfX> zF)nWln6R_7EHxiExhvOYKBBULGuLbO-nIeVtaX_J+}ZMRx(sTASYD8>$F`gR52jD< zO2dtt#wO3?DZ>%Xlj3!qSAQ`^x&efIw)AhaxF`*=(cqmVp>FAqj$!0My1@(&hm&k_4wI23A@DZ6YEVavr?v*qq)Ht z&cqsw$x_NmiOuA&w2Wcc8O)L5_=D;bV2Q+Tfy;g1mU*bsk-ejbqmlCB`}2?fJN+NE z=hKWu#L3(5gdMEObTnR>G7Wpk+zI-RY7Ocu5! zUsG+rA9%GDS9dt&d%PhTD^i$oQ?6$r8eN>{+PEdeRj&8rnFNJ{P3!6L$%##CL=lg1 z35#yd1G0!v_lfU>k+)1n)PKLu@>8S>Br37O-LtCt?E+*|H3tV*#oyZVS-q3(s7O~y zEU!egF>&)bqrHNg1d@TNQbi%uml`qTxU%0h&W>f>dzJBR^DZ{yoBi-R0H0+Og>&>| zK0~PgIYzm-uB>mVw#$Yh%phOf>gsav+2!FXGw+Y~3@X>V@oA5N@E$&`F?IThs)!=q z;YNP_(`-{3;;fHE)h7+ugtR7`kJVUhvM_^60C1wkbt9s=v~8z7CQbc71eyKK#b%;> z3_4O}{J@m1mZ;?U>E_S(=!;X?mx1zI+LK?nG?2PQW>lzWRJ9ISLy{ynfNYhe?l+XX zhZUmX?o*?aJ<4=q*FZ1O->9Ddy8kb{=l=)Z6GemgXxSLDI#pS0sP_*no_rHNZcQk` z0+uqGUXb<4)2HUfo=~d)z~2D~n8Ay3Y{DhUe?s`=n-+$I=^q(8cSC`UtY!ekBIyf)qW=L6^ay|08OM?+ z73jPFd${xUGP*(|of6@LNW6Mc%pFR6;T8N?^dJ~y71l^Ge zJZzh(4=fEj@w}^?z%Kr?EyWh#*@6pjghyB(aM1Em=e>4gNO0YD3n`CYF4-;+D?==Fcq3at7;~uB4<+#W9O% zS9w^!60~H!&SVq@jvPIo0Es}>BcBa`z1m+06acz0542kqav^?xaY02%X(gR`cC>Zn z^z{4C->jZnDX*;b!)6YA`xNS**d_*AT0dREZx)=otMX!XK<+R7nF9m391>wI=6GhY zC%~xExZ?ms0U2fi9(d@shw~u#bYGEO>S)>vFkb8c-oOR>O8xH-cc`m;*64sef%ohh zu3qxOcc3cW}GB(t+BIpjG1{cYQA zTxtZ`o(oEG{bf`*o^GCIEMU1iS`wX=|7JvnCV)vDF2?Dzyzbs60jE;Hs+lq6U)tU# z3N%pat{}qtR|9fDzYBvlE)V|dw-cBi?WOo5-q*8+U6e}s6;I;Cw&~weW5+FxeT>z1 zdqfqh^e@8xHM!V3h=K3mHgErln3ZR*ph}>OcIVCB`Hz`)J&rawzz_zd0-FC>{*S={ z1pey^W8h{7(Jn^$;QqVh?^j{tiVqPy6@^h9od1C3a!@W_^OLz$^M8PFDW|R}thhbo z{|~zWuqhoN?K$Tt@&7Dm0!BJs!NC~+pVk7HVb5s*($H<-0-iq*d4C5j?eNy;9B-tfB+N1&vTM2T3W(M`v#ycKbp$ydoqqq zSFwNdt~3b{@wz!v>|awlTj3!B!0G)W5w`z^4LsUZAyQa-@Ll%0D&^62!xzv|GGK`z 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

7-v9^Ob7xy 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

gF+@x#p%3<4eMoDDCC?R_?-z)dBz7SO!HR3Wp} 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 literal 0 HcmV?d00001 diff --git a/src/PHP.php b/src/PHP.php index dee3413..3f2517f 100644 --- a/src/PHP.php +++ b/src/PHP.php @@ -202,41 +202,36 @@ public static function init(null|array|Box|BasicInitConfig $args = null): BasicI return $config; } - private static $_cached_current_url = null; - /** - * @param $refresh + * * * @return ?UrlObject */ - static function currentUrl($refresh = false) { + static function currentUrl() { if (static::isCLI() && !defined('CURRENT_URL_PRETEND_NOT_CLI')) { return null; } - if (!static::$_cached_current_url || $refresh) { - $info = static::info(); - $serv = $info->server_var; - $protocol = empty($serv['HTTPS']) || $serv['HTTPS']?'https':'http'; - $host = $serv['SERVER_NAME'] ?? null; - if (empty($host)) { - $host = $serv['HTTP_HOST'] ?? null; - } - $server_port = $serv['SERVER_PORT'] ?? null - ?intval($serv['SERVER_PORT']) - :null; + $info = static::info(); + $serv = $info->server_var; + $protocol = empty($serv['HTTPS']) || $serv['HTTPS']?'https':'http'; + $host = $serv['SERVER_NAME'] ?? null; + if (empty($host)) { + $host = $serv['HTTP_HOST'] ?? null; + } - $uri = $serv['REQUEST_URI'] ?? null; + $server_port = $serv['SERVER_PORT'] ?? null + ?intval($serv['SERVER_PORT']) + :null; - static::$_cached_current_url = static::url( - host: $host, - path: $uri, - protocol: $protocol, - port: $server_port - ); - } + $uri = $serv['REQUEST_URI'] ?? null; - return static::$_cached_current_url; + return static::url( + host: $host, + path: $uri, + protocol: $protocol, + port: $server_port + ); } public static function metaMagicSpell(string|object $ref, $spell, ...$args) { diff --git a/src/models/UrlObject.php b/src/models/UrlObject.php index 77d26bf..2530c9f 100644 --- a/src/models/UrlObject.php +++ b/src/models/UrlObject.php @@ -16,6 +16,7 @@ use function explode; use function is_array; use function is_null; +use function is_numeric; use function is_string; use function preg_match; use function preg_replace; @@ -47,6 +48,22 @@ class UrlObject extends SimpleObject { use ForOutputsTrait; use RedefinableComponentTrait; + + /** + * CS operation "prepend", will cause to add elements to the left of the group + */ + const CS_OP_PREPEND = 'prepend'; + + /** + * CS operation "append", will cause to add elements to the right of the group + */ + const CS_OP_APPEND = 'append'; + + /** + * CS operation "replace", will cause to replace all the elements of the group + */ + const CS_OP_REPLACE = 'replace'; + static string $default_processor = HttpProtocolProcessor::class; static array $processors = [ @@ -199,32 +216,88 @@ function csSharpy(?string $sharpy): self { * but allows chaining due to native functions/methods nature. * * @param null|Box|array|string $path - * @param bool $extend + * @param string $operation * * @return $this */ - #[Shortcut('$this->sharpy')] - function csPath(null|Box|array|string $path, $extend = true): self { - $this->path = $path; + #[Shortcut('$this->path')] + function csPath(null|Box|array|string $path, string $operation = self::CS_OP_REPLACE): self { + $path_new = static::preProcessPathData($path); + $path_old = $this->path ?: PHP::box()->pathAlike(); + + $res = match ($operation) { + static::CS_OP_REPLACE => $path_new, + static::CS_OP_PREPEND => static::addPathElements($path_new, $path_old), + static::CS_OP_APPEND => static::addPathElements($path_old, $path_new), + }; + + $this->path = $res; return $this; } + private static function addPathElements($to, $from) { + foreach ($from as $k => $val) { + if (is_numeric($k)) { + $to[] = $val; + } else { + $to[$k] = $val; + } + } + return $to; + } + /** - * Chain set method for PArams + * Chained Setting of Path + * + * CS - in this context stands for `Chained Setting` * - * This method can be used the same way as property, + * This naming is done for compatibility reasons, due to possibility of `set` prefixed + * methods might collide with other frameworks "getter/setter" functionality, and cause + * unexpected behaviour. + * + * This method can be used the same way as the property, * but allows chaining due to native functions/methods nature. * - * @param \spaf\simputils\models\Box|array|null $params - * @param bool $extend Extend (by default) or Replace + * @param Box|array|null $params + * @param string $operation * * @return $this */ - function setParams(null|Box|array $params, $extend = true): self { + #[Shortcut('$this->params')] + function csParams(null|Box|array $params, string $operation = self::CS_OP_REPLACE): self { $this->params = $params; return $this; } + /** + * @param UrlCompatible|string|Box|array|null $host + * @param Box|array|string|null $path + * @param Box|array|null $params + * @param string|null $protocol + * @param string|null $processor + * @param string|null $port + * @param string|null $user + * @param string|null $pass + * @param mixed ...$data + * + * @return $this + */ + function update( + null|UrlCompatible|string|Box|array $host = null, + null|Box|array|string $path = null, + null|Box|array $params = null, + ?string $protocol = null, + ?string $processor = null, + ?string $port = null, + ?string $user = null, + ?string $pass = null, + mixed ...$data, + ): static { + // MARK Proceed here! + + return $this; + } + #[Property] protected ?string $_processor = null; @@ -250,17 +323,23 @@ function setParams(null|Box|array $params, $extend = true): self { #[Property] protected ?Box $_params = null; - #[Property('path')] - protected function setPathProperty($val) { + static function preProcessPathData($val) { + $res = null; if ($val instanceof Box) { $val->pathAlike(); - $this->_path = $val; + $res = $val; } else if (is_array($val)) { - $this->_path = PHP::box($val)->pathAlike(); + $res = PHP::box($val)->pathAlike(); } else if (is_string($val)) { $val = Str::removeStarting($val, '/'); - $this->_path = PHP::box(explode('/', $val))->pathAlike(); + $res = PHP::box(explode('/', $val))->pathAlike(); } + return $res; + } + + #[Property('path')] + protected function setPathProperty($val) { + $this->_path = static::preProcessPathData($val); } #[Property] From 065f9734c6c27dbd2acca8066afc5d780aad00bd Mon Sep 17 00:00:00 2001 From: Ivan Ponomarev Date: Mon, 28 Aug 2023 11:47:02 +0200 Subject: [PATCH 3/3] #130 / Urls full refactoring Awaiting #164 #165 unfinished --- src/PHP.php | 2 +- src/basic.php | 2 +- src/generic/BasicProtocolProcessor.php | 46 +-- src/generic/BasicSchemeProcessor.php | 70 ++++ src/models/UrlObject.php | 299 +++--------------- src/models/files/apps/DotEnvProcessor.php | 12 +- .../urls/processors/FileSchemeProcessor.php | 35 ++ .../urls/processors/HttpProtocolProcessor.php | 212 +------------ .../urls/processors/HttpSchemeProcessor.php | 215 +++++++++++++ tests/general/NewFeatures202206Test.php | 4 +- tests/general/UrlsTest.php | 144 ++++++++- tests/general/bugs/UrlCommonIssuesTest.php | 2 +- 12 files changed, 538 insertions(+), 505 deletions(-) create mode 100644 src/generic/BasicSchemeProcessor.php create mode 100644 src/models/urls/processors/FileSchemeProcessor.php create mode 100644 src/models/urls/processors/HttpSchemeProcessor.php diff --git a/src/PHP.php b/src/PHP.php index 894b3e9..958f553 100644 --- a/src/PHP.php +++ b/src/PHP.php @@ -921,7 +921,7 @@ public static function isCLI(): bool { static function url( null|UrlCompatible|string|Box|array $host = null, null|Box|array|string $path = null, - null|Box|array $params = null, + null|Box|array|string $params = null, ?string $protocol = null, ?string $processor = null, ?string $port = null, diff --git a/src/basic.php b/src/basic.php index c13bfff..9000f27 100644 --- a/src/basic.php +++ b/src/basic.php @@ -780,7 +780,7 @@ function ic(?string $name = null): null|InitConfig|BasicInitConfig { function url( null|UrlCompatible|string|Box|array $host = null, null|Box|array|string $path = null, - null|Box|array $params = null, + null|Box|array|string $params = null, ?string $protocol = null, ?string $processor = null, ?string $port = null, diff --git a/src/generic/BasicProtocolProcessor.php b/src/generic/BasicProtocolProcessor.php index e54fba5..12ad0e0 100644 --- a/src/generic/BasicProtocolProcessor.php +++ b/src/generic/BasicProtocolProcessor.php @@ -2,48 +2,16 @@ namespace spaf\simputils\generic; -use spaf\simputils\interfaces\UrlCompatible; -use spaf\simputils\models\Box; -use spaf\simputils\models\UrlObject; +use spaf\simputils\attributes\markers\Deprecated; /** * @property-read ?string $protocol + * @deprecated */ -abstract class BasicProtocolProcessor extends SimpleObject { +#[Deprecated( + 'Wrong naming "protocol" instead of commonly used "scheme"', + '\spaf\simputils\generic\BasicSchemeProcessor' +)] +abstract class BasicProtocolProcessor extends BasicSchemeProcessor { - static ?string $default_protocol = null; - - /** - * Returns array of supported protocol names - * - * @return Box|string[]|array|null - */ - abstract static function supportedProtocols(); - - abstract static function parse(UrlCompatible|string $value); - - abstract static function generateForSystem(UrlObject $url): string; - abstract static function generateForUser(UrlObject $url): string; - abstract static function generateRelative(UrlObject $url): string; - - // FIX -// -// #[Property(type: 'get')] -// protected ?string $_protocol = null; -// -// function __construct(string $protocol) { -// $this->_protocol = $protocol; -// } -// -// abstract function parse(UrlCompatible|string|Box|array $value, bool $is_preparsed = false, $data = null); -// -// abstract function generateForSystem($host, $path, $params, $data): string; -// -// abstract function generateForUser($host, $path, $params, $data): string; -// -// abstract function generateRelative($host, $path, $params, $data): string; - -// function __toString(): string { -// return PHP::objToNaiveString($this, ['protocol' => $this->_protocol]);//@codeCoverageIgnore -// } } diff --git a/src/generic/BasicSchemeProcessor.php b/src/generic/BasicSchemeProcessor.php new file mode 100644 index 0000000..e973b47 --- /dev/null +++ b/src/generic/BasicSchemeProcessor.php @@ -0,0 +1,70 @@ +_protocol = $protocol; +// } +// +// abstract function parse(UrlCompatible|string|Box|array $value, bool $is_preparsed = false, $data = null); +// +// abstract function generateForSystem($host, $path, $params, $data): string; +// +// abstract function generateForUser($host, $path, $params, $data): string; +// +// abstract function generateRelative($host, $path, $params, $data): string; + +// function __toString(): string { +// return PHP::objToNaiveString($this, ['protocol' => $this->_protocol]);//@codeCoverageIgnore +// } +} diff --git a/src/models/UrlObject.php b/src/models/UrlObject.php index b9b62e0..bffd886 100644 --- a/src/models/UrlObject.php +++ b/src/models/UrlObject.php @@ -8,7 +8,7 @@ use spaf\simputils\exceptions\NotImplementedYet; use spaf\simputils\generic\SimpleObject; use spaf\simputils\interfaces\UrlCompatible; -use spaf\simputils\models\urls\processors\HttpProtocolProcessor; +use spaf\simputils\models\urls\processors\HttpSchemeProcessor; use spaf\simputils\PHP; use spaf\simputils\Str; use spaf\simputils\traits\ForOutputsTrait; @@ -21,6 +21,7 @@ use function preg_match; use function preg_replace; use function spaf\simputils\basic\ic; +use function spaf\simputils\basic\pd; /** * @@ -64,11 +65,11 @@ class UrlObject extends SimpleObject { */ const CS_OP_REPLACE = 'replace'; - static string $default_processor = HttpProtocolProcessor::class; + static string $default_processor = HttpSchemeProcessor::class; static array $processors = [ - 'http' => HttpProtocolProcessor::class, - 'https' => HttpProtocolProcessor::class, + 'http' => HttpSchemeProcessor::class, + 'https' => HttpSchemeProcessor::class, ]; /** @@ -265,7 +266,7 @@ private static function addPathElements($to, $from) { */ #[Shortcut('$this->params')] function csParams(null|Box|array $params, string $operation = self::CS_OP_REPLACE): self { - $this->params = $params; + $this->params = $this->preProcessParamsData($params); return $this; } @@ -301,8 +302,10 @@ function update( #[Property] protected ?string $_processor = null; +// FIX After implementing multiple properties (and arrayed names) #[Property] - protected ?string $_protocol = null; + #[Property('protocol')] + protected ?string $_scheme = null; #[Property] protected ?string $_user = null; @@ -337,6 +340,26 @@ static function preProcessPathData($val) { return $res; } + protected function preProcessParamsData($val) { + $res = null; + if ($val instanceof Box) { + $val->paramsAlike(); + $res = $val; + } else if (is_array($val)) { + $res = PHP::box($val)->paramsAlike(); + } else if (is_string($val)) { + // FIX Proceed here with params data parsing +// pd('FIX', $val); + $proc = $this->_processor; + /** @var HttpSchemeProcessor $proc */ + $r = $proc::parse($val, part: HttpSchemeProcessor::PART_PARAMS); + pd('FIX RES', $r); +// $val = Str::removeStarting($val, '/'); +// $res = PHP::box(explode('/', $val))->pathAlike(); + } + return $res; + } + #[Property('path')] protected function setPathProperty($val) { $this->_path = static::preProcessPathData($val); @@ -413,7 +436,7 @@ function isSimilar( function __construct( null|UrlCompatible|string|Box|array $host = null, null|Box|array|string $path = null, - null|Box|array $params = null, + null|Box|array|string $params = null, ?string $protocol = null, ?string $processor = null, ?string $port = null, @@ -432,7 +455,7 @@ function __construct( $parsed = $this->parseHost($host); - $this->_protocol = $protocol ?: $parsed->get('protocol'); + $this->_scheme = $protocol ?: $parsed->get('protocol'); unset($parsed['protocol']); $this->_user = $user ?: $parsed->get('user'); @@ -466,7 +489,7 @@ function __construct( if (PHP::isArrayCompatible($path)) { $pre = PHP::box($path)->pathAlike(); } else if (is_string($path)) { - /** @var \spaf\simputils\models\Box $_def_pre */ + /** @var Box $_def_pre */ $_def_pre = $this->_processor::parse($path, part: 'path'); $ll = $_def_pre->get('path'); if ($ll) { @@ -520,11 +543,16 @@ function __construct( unset($parsed['path']); if ($params) { + + if (Str::is($params)) { + $params = $this->preProcessParamsData($params); + } + $pre_data = $pre = null; if (PHP::isArrayCompatible($params)) { $pre = PHP::box($params)->paramsAlike(); } else if (is_string($path)) { - /** @var \spaf\simputils\models\Box $_def_pre */ + /** @var Box $_def_pre */ $_def_pre = $this->_processor::parse($params, part: 'params'); $pre = PHP::box($_def_pre->get('params'))->paramsAlike(); unset($_def_pre['path']); @@ -592,257 +620,6 @@ protected function getRelative(): string { return $this->_processor::generateRelative($this); } -// -// static ?string $default_host = null; -// static ?string $default_protocol = 'https'; -// -// static Box|array $processors = [ -// 'http' => HttpProtocolProcessor::class, -// 'https' => HttpProtocolProcessor::class, -// ]; -// -// #[Property(type: 'get')] -// protected UrlCompatible|string|Box|array|null $_orig = null; -// -// #[Property(type: 'get')] -// protected UrlCompatible|string|null $_host = null; -// -// #[Property] -// protected Box|array|string|null $_path = null; -// -// #[Property] -// protected Box|array|null $_params = null; -// -// #[Property] -// protected Box|array|null $_data = null; -// -// #[Property('port')] -// protected function getPort(): ?int { -// return $this->_processor->getPort($this); -// } -// -// #[Property('port')] -// protected function setPort(?int $val) { -// $this->_processor->setPort($this, $val); -// } -// -// #[Extract(false)] -// #[DebugHide] -// protected ?BasicProtocolProcessor $_processor = null; -// -// #[Property('protocol')] -// protected function getProtocol(): string { -// return $this->processor->protocol; -// } -// -// #[Property('processor')] -// protected function getProcessor(): BasicProtocolProcessor { -// return $this->_processor; -// } -// -// #[Property('processor')] -// protected function setProcessor(BasicProtocolProcessor $val) { -// $this->_processor = $val; // @codeCoverageIgnore -// } -// -// /** -// * -// * Some info: -// * * Host can be "condensed" string containing all arguments - should be parsed -// * * Host can be everything without protocol - should be parsed -// * * Host can be just portion of URL - should be parsed -// * * Path can be path portion (array, string) + params if assoc indexes -// * * Params can contain only "get" encoded arguments -// * * Protocol just a string -// * * Processor should not be explicitly specified -// * * Data provided to the processors to incorporate/use in URLs -// * -// * All the above should be incremental, and no info should be lost. So if the params -// * can be in all 3 ($host, $path and $params) all of them have to be aggregated! -// * -// * -// * @param UrlCompatible|string|Box|array|null $host Host -// * @param Box|array|string|null $path Path -// * @param Box|array|null $params Params -// * @param string|null $protocol Protocol -// * @param string|null $processor Processor object -// * @param mixed ...$data Additional data -// */ -// function __construct( -// UrlCompatible|string|Box|array $host = null, -// Box|array|string $path = null, -// Box|array $params = null, -// string $protocol = null, -// string $processor = null, -// mixed ...$data, -// ) { -// $this->_host = null; -// $this->_path = PHP::box(); -// $this->_params = PHP::box(); -// $this->_data = PHP::box(); -// $this->_orig = $host; -// -// $this->parseHost($host, $protocol, $processor); -// $this->addPath($path); -// $this->addParams($params); -// $this->addData($data); -// $this->_path->pathAlike(); -// } -// -// protected function parseHost( -// UrlCompatible|string|Box|array $host = null, -// ?string $protocol = null, -// ?string $processor = null -// ) { -// if (!empty($host)) { -// if (is_string($host)) { -// $m = []; -// $orig_host = $host; -// preg_match('#^([a-zA-Z]?[a-zA-Z0-9_-]*):(.*)$#S', $host, $m); -// if ($m) { -// $protocol = $protocol ?? $m[1] ?? null; -// $host = $m[2] ?? null; -// } -// -// // Str::startsWith($host, '//') || (!Str::startsWith($host, 'http://') && !Str::startsWith($host, 'https://')) -// $tt = bx(static::$processors); -// if (!$tt->containsKey($protocol) || bx(['http', 'https'])->containsValue($protocol)) { -// // NOTE We need to check whether it's a valid http url without protocol part, -// // or it's something else. -// $processor_class = static::$processors['http'] ?? null; -// if (empty($processor_class)) { -// throw new Exception('HTTP Protocol does not have a proper Processor Class specified for it!'); -// } -// /** @var HttpProtocolProcessor $http_processor */ -// $protocol = empty($protocol) && $processor_class::isValid($host, $protocol)?'http':$protocol; -// -// [$protocol, $user, $pass, $host, $port] = $processor_class::preParsing($orig_host, $protocol); -// $this->data['user'] = $user; -// $this->data['pass'] = $pass; -// $this->data['port'] = $port; -// } -// -//// pd($protocol); -// // FIX Parse here! -// -// } else if (is_array($host) || $host instanceof Box) { -// $host = PHP::box($host); -// -// $this->_path->mergeFrom($host->only_numeric); -// $this->_params->mergeFrom($host->only_assoc); -// $host = null; -// } -// } -// -// $protocol = $protocol?:static::$default_protocol; -// if (is_null($protocol)) { -// $protocol = 'https'; -// } -// $this->_processor = $processor ?: static::chooseProcessor($protocol); -// -// if (!empty($host)) { -// [ -// $this->_host, -// $this->_path, -// $this->_params, -// $this->_data -// ] = $this->_processor->parse($host, true, $this->data); -// } -// } -// -// function addPath(Box|array|string|null $path) { -// if (!empty($path)) { -// $proc = $this->processor; -// -// if (is_string($path)) { -// [$_, $_path, $_params, $_data] = $proc->parse($path, false); -// if (!empty($_path)) { -// $this->_path->mergeFrom($_path); -// } -// if (!empty($_params)) { -// $this->_params->mergeFrom($_params); -// } -// if (!empty($_data)) { -// $this->_data->mergeFrom($_data); -// } -// } else { -// // NOTE Path can contain params as well. Difference is in indexes -// $path = PHP::box($path); -// -// $this->_path->mergeFrom($path->only_numeric); -// $this->_params->mergeFrom($path->only_assoc); -// } -// } -// -// // TODO Re-implement properly without creating a new object every single time -// $r = PHP::box()->pathAlike(); -// foreach ($this->_path as $item) { -// $sub = preg_replace('#\s+#S', '', $item); -// if (!empty($sub)) { -// $r[] = $sub; -// } -// } -// $this->_path = $r; -// } -// -// function addParams(Box|array|null $params) { -// if (!empty($params)) { -// $this->_params->mergeFrom($params); -// } -// -// $r = PHP::box(); -// foreach ($this->_params as $k => $v) { -// $sub = preg_replace('#\s+#S', '', $k); -// if (!empty($sub)) { -// $r[$sub] = $v; -// } -// } -// $this->_params = $r; -// } -// -// function addData(Box|array|null $data) { -// if (!empty($data)) { -// $this->_data->mergeFrom($data); -// } -// } -// -// protected static function chooseProcessor(string $protocol): BasicProtocolProcessor { -// $protocols = PHP::box(static::$processors); -// if (!$protocols->containsKey($protocol)) { -// throw new ProtocolProcessorIsUndefined( -// "No processor defined for this protocol: {$protocol}" -// ); -// } -// -// $class = $protocols[$protocol]; -// return new $class($protocol); -// } -// -// #[Property('for_system')] -// protected function getForSystem(): string { -// $host = $this->_host ?? static::$default_host ?? 'localhost'; -// return $this->_processor->generateForSystem( -// $host, $this->_path, $this->_params, $this->_data -// ); -// } -// -// #[Property('for_user')] -// protected function getForUser(): string { -// $host = $this->_host ?? static::$default_host ?? 'localhost'; -// return $this->_processor->generateForUser( -// $host, $this->_path, $this->_params, $this->_data -// ); -// } -// -// #[Property('relative')] -// protected function getRelative(): string { -// $host = $this->_host ?? static::$default_host ?? 'localhost'; -// return $this->_processor->generateRelative( -// $host, $this->_path, $this->_params, $this->_data -// ); -// } - - function setFromData($data): static { $this->__construct($data['for_system']); return $this; diff --git a/src/models/files/apps/DotEnvProcessor.php b/src/models/files/apps/DotEnvProcessor.php index 5a3176e..7bd5ad5 100644 --- a/src/models/files/apps/DotEnvProcessor.php +++ b/src/models/files/apps/DotEnvProcessor.php @@ -7,6 +7,10 @@ use spaf\simputils\generic\BasicResource; use spaf\simputils\models\files\apps\settings\DotEnvSettings; use spaf\simputils\special\dotenv\ExtTypeHint; +use spaf\simputils\Str; +use function spaf\simputils\basic\pr; +use function str_replace; +use function trim; /** * DotEnv data processor @@ -47,10 +51,16 @@ public function getContent(mixed $fd, ?BasicResource $file = null): ?array { $lines = explode("\n", $content); $res = []; foreach ($lines as $line) { + if (Str::contains($line, "\r")) { + pr("Line contains \"\\r\" symbol, trimmed."); + } + + $line = trim($line); + if (empty($line)) { continue; } - $line = trim($line); + if ($line[0] === '#') { // TODO Comment-extension processing must happen here! diff --git a/src/models/urls/processors/FileSchemeProcessor.php b/src/models/urls/processors/FileSchemeProcessor.php new file mode 100644 index 0000000..4fe7993 --- /dev/null +++ b/src/models/urls/processors/FileSchemeProcessor.php @@ -0,0 +1,35 @@ + static::$initial_parser_regexp_path, - static::PART_PARAMS => static::$initial_parser_regexp_params, - default => static::$initial_parser_regexp_full, - }; - - $m = []; - // NOTE Parsing URL - preg_match($regexp, $value, $m); - - if ($part === static::PART_FULL) { - if (!$protocol) { - $protocol = static::$default_protocol; - } - $creds = $m[1] ?? null; - $host = $m[2] ?: static::$default_host; - $port = $m[3] ?: static::$default_port; - $path = $m[4] ?? null; - $params = $m[5] ?? null; - $sharpy = $m[6] ?? null; - } else if ($part === static::PART_PATH) { - $path = $m[1] ?? null; - $params = $m[2] ?? null; - $sharpy = $m[3] ?? null; -// } else if ($part === static::PART_PARAMS) { -// pd($m, $value); -// $path = $m[1] ?? null; -// $params = $m[2] ?? null; -// $sharpy = $m[3] ?? null; - } - - if ($path) { - $path_new = PHP::box()->pathAlike(); - foreach (explode('/', $path) as $key => $val) { - if ($val) { - $path_new[$key] = $val; - } - } - $path = $path_new; - } - - - if ($creds) { - $c = []; - preg_match('#^([\w\d_-]*)(?::(.*))?#S', $creds, $c); - $user = $c[1] ?? null; - $pass = $c[2] ?? null; - } - - if ($params) { - $params_pre_parsed = PHP::box(explode('&', $params)); - } - - $params_res = PHP::box()->apply( - separator: '&', - joined_to_str: true, - stretcher: true - ); - if ($params_pre_parsed) { - foreach ($params_pre_parsed as $item) { - if ($item && Str::contains($item, '=')) { - [$key, $val] = explode('=', $item); - if ($key) { - $val = Str::contains($val, '%')?urldecode($val):$val; - - $params_res[$key] = $val; - } - } - } - $params = $params_res; - } - - if ($host && !static::$do_not_lower) { - $host = Str::lower($host); - } - - $r = PHP::box([ - 'protocol' => $protocol, - 'user' => $user, - 'pass' => $pass, - 'host' => $host, - 'port' => $port, - 'path' => $path, - 'params' => $params, - 'sharpy' => $sharpy, - ]); - return $r; - } - - private static function _stringify($url, bool $obfuscate, bool $relative) { - $cred = ''; - if ($url->user) { - $cred = "{$url->user}"; - if ($url->password) { - if ($obfuscate) { - $cred .= ":".DebugHide::$default_placeholder; - } else { - $cred .= ":{$url->password}"; - } - } - $cred .= '@'; - } - - $port = ''; - if ($url->port && $url->port !== 80 && $url->port !== 443 ) { - $port = ":{$url->port}"; - } - - $path = preg_replace('#/+#S', '/', "/{$url->path}"); - - $params = ''; - if ($url->params && count($url->params) > 0) { - $params = $url->params->clone(); - foreach ($params as $key => $val) { - $params[$key] = urlencode($val); - } - $params = "?{$params}"; - } - - $sharpy = ''; - if (!empty($url->data['sharpy']) && $url->data['sharpy']) { - $sharpy = "#{$url->data['sharpy']}"; - } - - $rel_part = "{$path}{$params}{$sharpy}"; - if ($relative) { - return $rel_part; - } - - return "{$url->protocol}://{$cred}{$url->host}{$port}{$rel_part}"; - } - - static function generateForSystem(UrlObject $url): string { - return static::_stringify($url, false, false); - } - - static function generateForUser(UrlObject $url): string { - return static::_stringify($url, true, false); - } - - static function generateRelative(UrlObject $url): string { - return static::_stringify($url, true, true); - } } diff --git a/src/models/urls/processors/HttpSchemeProcessor.php b/src/models/urls/processors/HttpSchemeProcessor.php new file mode 100644 index 0000000..6dcf144 --- /dev/null +++ b/src/models/urls/processors/HttpSchemeProcessor.php @@ -0,0 +1,215 @@ + static::$initial_parser_regexp_path, + static::PART_PARAMS => static::$initial_parser_regexp_params, + default => static::$initial_parser_regexp_full, + }; + + $m = []; + // NOTE Parsing URL + preg_match($regexp, $value, $m); + + if ($part === static::PART_FULL) { + if (!$protocol) { + $protocol = static::$default_protocol; + } + $creds = $m[1] ?? null; + $host = $m[2] ?: static::$default_host; + $port = $m[3] ?: static::$default_port; + $path = $m[4] ?? null; + $params = $m[5] ?? null; + $sharpy = $m[6] ?? null; + } else if ($part === static::PART_PATH) { + $path = $m[1] ?? null; + $params = $m[2] ?? null; + $sharpy = $m[3] ?? null; + } else if ($part === static::PART_PARAMS) { + pd('PART', $regexp, $m, $value); + $path = $m[1] ?? null; + $params = $m[2] ?? null; + $sharpy = $m[3] ?? null; + } + + if ($path) { + $path_new = PHP::box()->pathAlike(); + foreach (explode('/', $path) as $key => $val) { + if ($val) { + $path_new[$key] = $val; + } + } + $path = $path_new; + } + + + if ($creds) { + $c = []; + preg_match('#^([\w\d_-]*)(?::(.*))?#S', $creds, $c); + $user = $c[1] ?? null; + $pass = $c[2] ?? null; + } + + if ($params) { + $params_pre_parsed = PHP::box(explode('&', $params)); + } + + $params_res = PHP::box()->apply( + separator: '&', + joined_to_str: true, + stretcher: true + ); + if ($params_pre_parsed) { + foreach ($params_pre_parsed as $item) { + if ($item && Str::contains($item, '=')) { + [$key, $val] = explode('=', $item); + if ($key) { + $val = Str::contains($val, '%')?urldecode($val):$val; + + $params_res[$key] = $val; + } + } + } + $params = $params_res; + } + + if ($host && !static::$do_not_lower) { + $host = Str::lower($host); + } + + $r = PHP::box([ + 'protocol' => $protocol, + 'user' => $user, + 'pass' => $pass, + 'host' => $host, + 'port' => $port, + 'path' => $path, + 'params' => $params, + 'sharpy' => $sharpy, + ]); + return $r; + } + + private static function _stringify($url, bool $obfuscate, bool $relative) { + $cred = ''; + if ($url->user) { + $cred = "{$url->user}"; + if ($url->password) { + if ($obfuscate) { + $cred .= ":".DebugHide::$default_placeholder; + } else { + $cred .= ":{$url->password}"; + } + } + $cred .= '@'; + } + + $port = ''; + if ($url->port && $url->port !== 80 && $url->port !== 443 ) { + $port = ":{$url->port}"; + } + + $path = preg_replace('#/+#S', '/', "/{$url->path}"); + + $params = ''; + if ($url->params && count($url->params) > 0) { + $params = $url->params->clone(); + foreach ($params as $key => $val) { + $params[$key] = is_null($val) + ?'' + :urlencode($val); + } + $params = "?{$params}"; + } + + $sharpy = ''; + if (!empty($url->data['sharpy']) && $url->data['sharpy']) { + $sharpy = "#{$url->data['sharpy']}"; + } + + $rel_part = "{$path}{$params}{$sharpy}"; + if ($relative) { + return $rel_part; + } + + return "{$url->protocol}://{$cred}{$url->host}{$port}{$rel_part}"; + } + + static function generateForSystem(UrlObject $url): string { + return static::_stringify($url, false, false); + } + + static function generateForUser(UrlObject $url): string { + return static::_stringify($url, true, false); + } + + static function generateRelative(UrlObject $url): string { + return static::_stringify($url, true, true); + } + +} diff --git a/tests/general/NewFeatures202206Test.php b/tests/general/NewFeatures202206Test.php index 7062c4a..625d848 100644 --- a/tests/general/NewFeatures202206Test.php +++ b/tests/general/NewFeatures202206Test.php @@ -229,7 +229,7 @@ public function ipRangesToCheck() { /** * @covers \spaf\simputils\models\UrlObject * @covers \spaf\simputils\basic\url - * @covers \spaf\simputils\models\urls\processors\HttpProtocolProcessor + * @covers \spaf\simputils\models\urls\processors\HttpSchemeProcessor * * @uses \spaf\simputils\Boolean::from * @uses \spaf\simputils\basic\bx @@ -440,7 +440,7 @@ public function datetimePeriodToCheck() { * @uses \spaf\simputils\models\Box * @uses \spaf\simputils\models\IPv4 * @uses \spaf\simputils\models\UrlObject - * @uses \spaf\simputils\models\urls\processors\HttpProtocolProcessor + * @uses \spaf\simputils\models\urls\processors\HttpSchemeProcessor * @uses \spaf\simputils\traits\PropertiesTrait::__set * @uses \spaf\simputils\traits\PropertiesTrait::_simpUtilsGetValidator * diff --git a/tests/general/UrlsTest.php b/tests/general/UrlsTest.php index 21703b0..50de07a 100644 --- a/tests/general/UrlsTest.php +++ b/tests/general/UrlsTest.php @@ -9,7 +9,7 @@ /** * @covers \spaf\simputils\models\UrlObject - * @covers \spaf\simputils\models\urls\processors\HttpProtocolProcessor + * @covers \spaf\simputils\models\urls\processors\HttpSchemeProcessor * @covers \spaf\simputils\basic\url * @covers \spaf\simputils\generic\BasicProtocolProcessor * @@ -220,6 +220,35 @@ function dataProviderBasicsStrOnly() { // 'data' => ['sharpy' => 'ggogg'], ] ], + [ + 'http://dd:gg@localhost/p1/p2/p3?empty_param=', + [ + 'protocol' => 'http', + 'host' => 'localhost', + 'port' => 80, + 'path' => ['p1', 'p2', 'p3'], + 'user' => 'dd', + 'password' => 'gg', + 'params' => [ + 'empty_param' => null, + ], + ] + ], + [ + 'http://dd:gg@localhost/p1/p2/p3?empty_param=&another-non-empty-param=test', + [ + 'protocol' => 'http', + 'host' => 'localhost', + 'port' => 80, + 'path' => ['p1', 'p2', 'p3'], + 'user' => 'dd', + 'password' => 'gg', + 'params' => [ + 'empty_param' => null, + 'another-non-empty-param' => 'test', + ], + ] + ], [ 'https://dd:@localhost/p4/p5/p6?d1=v1&d2=v2&d3=v3', [ @@ -520,4 +549,117 @@ function testBasicsExtension() { // pd($url); } + function dataProviderAdvancedExtension() { + return [ + [ + url('/path1/path2'), + 'https://localhost/path1/path2' + ], + [ + url('my-domain'), + 'https://my-domain/' + ], + [ + url('http://my-domain'), + 'http://my-domain/' + ], + [ + url('http://my-domain/path1/path2'), + 'http://my-domain/path1/path2' + ], + [ + url('http://my-domain/path1/path2/'), + 'http://my-domain/path1/path2' + ], + [ + url('http://my-domain/path1/path2?arg1=1'), + 'http://my-domain/path1/path2?arg1=1' + ], + [ + url('http://my-domain/path1/path2?arg0=&arg1=1&arg2=2'), + 'http://my-domain/path1/path2?arg0=&arg1=1&arg2=2' + ], + [ + url($host = 'http://my-domain/path1/path2?arg0=&arg1=1&arg2=2#sch'), + 'http://my-domain/path1/path2?arg0=&arg1=1&arg2=2#sch' + ], + [ // NOTE Normally in UNIX systems first slash should make path absolute. + // So the new path value must replace the old one, and not add up + url($host, '/path-1/path0'), + 'http://my-domain/path-1/path0?arg0=&arg1=1&arg2=2#sch' + ], + [ + url($host, 'path3/path4'), + 'http://my-domain/path1/path2/path3/path4?arg0=&arg1=1&arg2=2#sch' + ], + [ // NOTE Normally in UNIX systems first slash should make path absolute. + // So the new path value must replace the old one, and not add up + url($host, ['/path-2/path-1', 'path0']), + 'http://my-domain/path-2/path-1/path0?arg0=&arg1=1&arg2=2#sch' + ], + [ + url($host, ['path3/path4', 'path5']), + 'http://my-domain/path1/path2/path3/path4?arg0=&arg1=1&arg2=2#sch' + ], + [ + url($host, ['path3', 'path4', 'arg0' => 0, 'arg1' => 10, 'arg3' => 30]), + 'http://my-domain/path1/path2/path3/path4?arg0=0&arg1=10&arg2=2&arg3=30#sch' + ], + [ + url($host, ['arg0' => 0, 'arg1' => 10, 'arg3' => 30]), + 'http://my-domain/path1/path2?arg0=0&arg1=10&arg2=2&arg3=30#sch' + ], + [ + url($host, ['path3', 'path4', 'arg0' => 'new', '#' => 'new']), + 'http://my-domain/path1/path2/path3/path4?arg0=new&arg1=1&arg2=2#new' + ], + [ + url($host, ['#' => 'new']), + 'http://my-domain/path1/path2?arg0=&arg1=1&arg2=2#new' + ], + + [ + url( + $host, + $path = ['path3', 'path4', 'arg0' => 0, 'arg1' => 10, 'arg3' => 30, '#' => 'sharpy'], + [ + 'arg0' => 'new-0', + 'arg4' => 'new-4', + ] + ), + 'http://my-domain/path1/path2/path3/path4?arg0=new-0&arg1=10&arg2=2&arg3=30&arg4=new-4#sharpy' + ], + [ + url( + $host, + $path, + [ + 'arg0' => 'new-0', + 'arg4' => 'new-4', + '#' => 'replaced-sharpy' + ] + ), + 'http://my-domain/path1/path2/path3/path4?arg0=new-0&arg1=10&arg2=2&arg3=30&arg4=new-4#replaced-sharpy' + ], + [ + url( + $host, + $path, + 'arg0=new-0&arg4=new-4#replaced-sharpy' + ), + 'http://my-domain/path1/path2/path3/path4?arg0=new-0&arg1=10&arg2=2&arg3=30&arg4=new-4#replaced-sharpy' + ], + ]; + } + + /** + * @param $url + * @param $expected + * + * @dataProvider dataProviderAdvancedExtension + * @return void + */ + function testStatusQuo202308($url, $expected) { + $this->assertEquals($expected, "{$url}"); + } } diff --git a/tests/general/bugs/UrlCommonIssuesTest.php b/tests/general/bugs/UrlCommonIssuesTest.php index 42d975d..93bb7e3 100644 --- a/tests/general/bugs/UrlCommonIssuesTest.php +++ b/tests/general/bugs/UrlCommonIssuesTest.php @@ -16,7 +16,7 @@ * @uses \spaf\simputils\components\normalizers\BooleanNormalizer * @uses \spaf\simputils\components\normalizers\StringNormalizer * @uses \spaf\simputils\models\Box - * @uses \spaf\simputils\models\urls\processors\HttpProtocolProcessor + * @uses \spaf\simputils\models\urls\processors\HttpSchemeProcessor * @uses \spaf\simputils\special\CodeBlocksCacheIndex * @uses \spaf\simputils\traits\PropertiesTrait */