PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB` sh-3ll

HOME


sh-3ll 1.0
DIR:/home/ami/www/wp-content/plugins/duplicator/installer/dup-installer/classes/
Upload File :
Current File : /home/ami/www/wp-content/plugins/duplicator/installer/dup-installer/classes/class.engine.php
<?php

/**
 * Walks every table in db that then walks every row and column replacing searches with replaces
 * large tables are split into 50k row blocks to save on memory.
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2 Full Documentation
 *
 * @package SC\DUPX\UpdateEngine
 *
 */

defined('ABSPATH') || defined('DUPXABSPATH') || exit;

use Duplicator\Installer\Utils\Log\Log;
use Duplicator\Installer\Utils\Log\LogHandler;
use Duplicator\Installer\Core\Params\PrmMng;
use Duplicator\Libs\Snap\SnapDB;

class DUPX_UpdateEngine
{
    const SR_PRORITY_HIGH                 = 5;
    const SR_PRORITY_DEFAULT              = 10;
    const SR_PRORITY_LOW                  = 20;
    const SR_PRORITY_NETWORK_SUBSITE_HIGH = 4;
    const SR_PRORITY_NETWORK_SUBSITE      = 5;
    const SR_PRORITY_GENERIC_SUBST        = 10;
    const SR_PRORITY_GENERIC_SUBST_P1     = 10;
    const SR_PRORITY_GENERIC_SUBST_P2     = 11;
    const SR_PRORITY_GENERIC_SUBST_P3     = 12;
    const SR_PRORITY_GENERIC_SUBST_P4     = 13;
    const SR_PRORITY_CUSTOM               = 20;
    const SERIALIZE_OPEN_STR_REGEX        = '/^(s:\d+:")/';
    const SERIALIZE_OPEN_SUBSTR_LEN       = 25;
    const SERIALIZE_CLOSE_STR_REGEX       = '/^";}*(?:"|a:|s:|S:|b:|d:|i:|o:|O:|C:|r:|R:|N;|$)/';
    const SERIALIZE_CLOSE_SUBSTR_LEN      = 50;
    const SERIALIZE_CLOSE_STR             = '";';
    const SERIALIZE_CLOSE_STR_LEN         = 2;

    private static $report = null;

    /**
     *  Used to report on all log errors into the installer-txt.log
     *
     * @return string Writes the results of the update engine tables to the log
     */
    public static function logErrors()
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();

        if (!empty($s3Funcs->report['errsql'])) {
            Log::info("--------------------------------------");
            Log::info("DATA-REPLACE ERRORS (MySQL)");
            foreach ($s3Funcs->report['errsql'] as $error) {
                Log::info($error);
            }
            Log::info("");
        }
        if (!empty($s3Funcs->report['errser'])) {
            Log::info("--------------------------------------");
            Log::info("DATA-REPLACE ERRORS (Serialization):");
            foreach ($s3Funcs->report['errser'] as $error) {
                Log::info($error);
            }
            Log::info("");
        }
        if (!empty($s3Funcs->report['errkey'])) {
            Log::info("--------------------------------------");
            Log::info("DATA-REPLACE ERRORS (Key):");
            Log::info('Use SQL: SELECT @row := @row + 1 as row, t.* FROM some_table t, (SELECT @row := 0) r');
            foreach ($s3Funcs->report['errkey'] as $error) {
                Log::info($error);
            }
        }
    }

    /**
     *  Used to report on all log stats into the installer-txt.log
     *
     * @return string Writes the results of the update engine tables to the log
     */
    public static function logStats()
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();
        Log::resetIndent();

        if (!empty($s3Funcs->report) && is_array($s3Funcs->report)) {
            $stats  = "--------------------------------------\n";
            $stats .= sprintf("SCANNED:\tTables:%d \t|\t Rows:%d \t|\t Cells:%d \n", $s3Funcs->report['scan_tables'], $s3Funcs->report['scan_rows'], $s3Funcs->report['scan_cells']);
            $stats .= sprintf("UPDATED:\tTables:%d \t|\t Rows:%d \t|\t Cells:%d \n", $s3Funcs->report['updt_tables'], $s3Funcs->report['updt_rows'], $s3Funcs->report['updt_cells']);
            $stats .= sprintf("ERRORS:\t\t%d \nRUNTIME:\t%f sec", $s3Funcs->report['err_all'], $s3Funcs->report['time']);
            Log::info($stats);
        }
    }

    /**
     * Returns only the text type columns of a table ignoring all numeric types
     *
     * @param obj $conn A valid database link handle
     * @param string $table A valid table name
     *
     * @return array All the column names of a table
     */
    private static function getTextColumns($table)
    {
        $dbh = DUPX_S3_Funcs::getInstance()->getDbConnection();

        $type_where  = '';
        $type_where .= "type LIKE '%char%' OR ";
        $type_where .= "type LIKE '%text' OR ";
        $type_where .= "type LIKE '%blob' ";
        $sql         = "SHOW COLUMNS FROM `" . mysqli_real_escape_string($dbh, $table) . "` WHERE {$type_where}";

        $result = DUPX_DB::mysqli_query($dbh, $sql);
        if (!$result || mysqli_num_rows($result) === 0) {
            return null;
        }

        $fields = array();
        while ($row    = mysqli_fetch_assoc($result)) {
            $fields[] = $row['Field'];
        }

        //Return Primary which is needed for index lookup.  LIKE '%PRIMARY%' is less accurate with lookup
        //$result = mysqli_query($dbh, "SHOW INDEX FROM `{$table}` WHERE KEY_NAME LIKE '%PRIMARY%'");
        $result = DUPX_DB::mysqli_query($dbh, "SHOW INDEX FROM `" . mysqli_real_escape_string($dbh, $table) . "`");
        if (mysqli_num_rows($result) > 0) {
            while ($row = mysqli_fetch_assoc($result)) {
                $fields[] = $row['Column_name'];
            }
        }

        return (count($fields) > 0) ? array_unique($fields) : null;
    }

    public static function set_sql_column_safe(&$str)
    {
        $str = "`$str`";
    }

    public static function loadInit()
    {
        Log::info('ENGINE LOAD INIT', Log::LV_DEBUG);
        $s3Funcs                          = DUPX_S3_Funcs::getInstance();
        $s3Funcs->report['profile_start'] = DUPX_U::getMicrotime();

        $dbh = $s3Funcs->getDbConnection();
        @mysqli_autocommit($dbh, false);
    }

    /**
     * Begins the processing for replace logic
     *
     * @param array $tables The tables we want to look at
     *
     * @return array Collection of information gathered during the run.
     */
    public static function load($tables = array())
    {
        self::loadInit();

        if (is_array($tables)) {
            foreach ($tables as $table) {
                self::evaluateTalbe($table);
            }
        }

        self::commitAndSave();
        return self::loadEnd();
    }

    public static function commitAndSave()
    {
        Log::info('ENGINE COMMIT AND SAVE', Log::LV_DEBUG);

        $dbh = DUPX_S3_Funcs::getInstance()->getDbConnection();

        @mysqli_commit($dbh);
        @mysqli_autocommit($dbh, true);

        DUPX_NOTICE_MANAGER::getInstance()->saveNotices();
    }

    public static function loadEnd()
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();
        Log::info('ENGINE LOAD END', Log::LV_DEBUG);

        $s3Funcs->report['profile_end'] = DUPX_U::getMicrotime();
        $s3Funcs->report['time']        = DUPX_U::elapsedTime($s3Funcs->report['profile_end'], $s3Funcs->report['profile_start']);
        $s3Funcs->report['errsql_sum']  = empty($s3Funcs->report['errsql']) ? 0 : count($s3Funcs->report['errsql']);
        $s3Funcs->report['errser_sum']  = empty($s3Funcs->report['errser']) ? 0 : count($s3Funcs->report['errser']);
        $s3Funcs->report['errkey_sum']  = empty($s3Funcs->report['errkey']) ? 0 : count($s3Funcs->report['errkey']);
        $s3Funcs->report['err_all']     = $s3Funcs->report['errsql_sum'] + $s3Funcs->report['errser_sum'] + $s3Funcs->report['errkey_sum'];

        return $s3Funcs->report;
    }

    public static function getTableRowParamsDefault($table = '')
    {
        return array(
            'table'         => $table,
            'updated'       => false,
            'row_count'     => 0,
            'columns'       => array(),
            'colList'       => '*',
            'colMsg'        => 'every column',
            'columnsSRList' => array(),
            'pages'         => 0,
            'page_size'     => 0,
            'page'          => 0,
            'current_row'   => 0
        );
    }

    private static function getTableRowsParams($table)
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();
        $dbh     = $s3Funcs->getDbConnection();

        $rowsParams = self::getTableRowParamsDefault($table);

        // Count the number of rows we have in the table if large we'll split into blocks
        $rowsParams['row_count'] = DUPX_DB::mysqli_query($dbh, "SELECT COUNT(*) FROM `" . mysqli_real_escape_string($dbh, $rowsParams['table']) . "`");
        if (!$rowsParams['row_count']) {
            return null;
        }
        $rows_result = mysqli_fetch_array($rowsParams['row_count']);
        @mysqli_free_result($rowsParams['row_count']);
        $rowsParams['row_count'] = $rows_result[0];
        if ($rowsParams['row_count'] == 0) {
            $rowsParams['colMsg'] = 'no columns  ';
            self::logEvaluateTable($rowsParams);
            return null;
        }

        // Get a list of columns in this table
        $sql    = 'DESCRIBE ' . mysqli_real_escape_string($dbh, $rowsParams['table']);
        $fields = DUPX_DB::mysqli_query($dbh, $sql);
        if (!$fields) {
            return null;
        }
        while ($column = mysqli_fetch_array($fields)) {
            $rowsParams['columns'][$column['Field']] = $column['Key'] == 'PRI' ? true : false;
        }

        $rowsParams['page_size']  = $GLOBALS['DATABASE_PAGE_SIZE'];
        $rowsParams['pages']      = ceil($rowsParams['row_count'] / $rowsParams['page_size']);
        $rowsParams['lastOffset'] = 0;

        // Grab the columns of the table.  Only grab text based columns because
        // they are the only data types that should allow any type of search/replace logic
        if (!PrmMng::getInstance()->getValue(PrmMng::PARAM_FULL_SEARCH)) {
            $rowsParams['colList'] = self::getTextColumns($rowsParams['table']);
            if ($rowsParams['colList'] != null && is_array($rowsParams['colList'])) {
                array_walk($rowsParams['colList'], array(__CLASS__, 'set_sql_column_safe'));
                $rowsParams['colList'] = implode(',', $rowsParams['colList']);
            }
            $rowsParams['colMsg'] = (empty($rowsParams['colList'])) ? 'every column' : 'text columns';
        }

        if (empty($rowsParams['colList'])) {
            $rowsParams['colMsg'] = 'no columns  ';
        }

        self::logEvaluateTable($rowsParams);

        if (empty($rowsParams['colList'])) {
            return null;
        } else {
            // PREPARE SEARCH AN REPLACE LISF FOR TABLES
            $rowsParams['columnsSRList'] = self::getColumnsSearchReplaceList($rowsParams['table'], $rowsParams['columns']);
            return $rowsParams;
        }
    }

    public static function logEvaluateTable($rowsParams)
    {
        Log::resetIndent();
        $log  = "\n" . 'EVALUATE TABLE: ' . str_pad(Log::v2str($rowsParams['table']), 50, '_', STR_PAD_RIGHT);
        $log .= '[ROWS:' . str_pad($rowsParams['row_count'], 6, " ", STR_PAD_LEFT) . ']';
        $log .= '[PG:' . str_pad($rowsParams['pages'], 4, " ", STR_PAD_LEFT) . ']';
        $log .= '[SCAN:' . $rowsParams['colMsg'] . ']';
        if (Log::isLevel(Log::LV_DETAILED)) {
            $log .= '[COLS: ' . $rowsParams['colList'] . ']';
        }
        Log::info($log);
        Log::incIndent();
    }

    public static function evaluateTalbe($table)
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();

        // init table params if isn't initialized
        if (!self::initTableParams($table)) {
            return false;
        }

        //Paged Records
        $pages = $s3Funcs->cTableParams['pages'];
        for ($page = 0; $page < $pages; $page++) {
            self::evaluateTableRows($table, $page);
        }

        if ($s3Funcs->cTableParams['updated']) {
            $s3Funcs->report['updt_tables']++;
        }
    }

    public static function evaluateTableRows($table, $page)
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();

        // init table params if isn't initialized
        if (!self::initTableParams($table)) {
            return false;
        }

        $s3Funcs->cTableParams['page'] = $page;
        if ($s3Funcs->cTableParams['page'] >= $s3Funcs->cTableParams['pages']) {
            Log::info('ENGINE EXIT PAGE:' . Log::v2str($table) . ' PAGES:' . $s3Funcs->cTableParams['pages'], Log::LV_DEBUG);
            return false;
        }

        return self::evaluatePagedRows($s3Funcs->cTableParams);
    }

    public static function initTableParams($table)
    {
        $s3Funcs = DUPX_S3_Funcs::getInstance();

        if (is_null($s3Funcs->cTableParams) || $s3Funcs->cTableParams['table'] !== $table) {
            Log::resetIndent();
            Log::info("\n" . 'ENGINE INIT TABLE PARAMS ' . Log::v2str($table), Log::LV_DETAILED);
            if (!DUPX_DB_Functions::getInstance()->tablesExist($table)) {
                Log::info('ENGINE TABLE DOESN\'T EXIST IN THE DATABASE', Log::LV_DEBUG);
                $longMsg = <<<MSG
The table could not be found in the database.
If the "Skip Database Extraction" option was chosen in step 2, make sure you insert the database manually before continuing to step 3.
                   
Also, verify the database that was inserted contains the urls and paths of the original site before step 3 for proper migration.
MSG;
                DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice(array(
                    'shortMsg' => 'Table ' . $table . ' doesn\'t exist in the database',
                    'level'    => DUPX_NOTICE_ITEM::HARD_WARNING,
                    'longMsg'  => $longMsg,
                    'sections' => 'search_replace'
                ));
                return false;
            }

            $s3Funcs->report['scan_tables']++;

            if (($s3Funcs->cTableParams = self::getTableRowsParams($table)) === null) {
                Log::info('ENGINE TABLE PARAMS EMPTY', Log::LV_DEBUG);
                return false;
            }
        }

        return true;
    }

    /**
     * evaluate rows with pagination
     *
     * @param array $rowsParams
     *
     * @return boolean // if true table is modified and updated
     */
    private static function evaluatePagedRows(&$rowsParams)
    {
        $nManager = DUPX_NOTICE_MANAGER::getInstance();
        $s3Funcs  = DUPX_S3_Funcs::getInstance();
        $dbh      = $s3Funcs->getDbConnection();
        $start    = $rowsParams['page'] * $rowsParams['page_size'];
        $end      = $start + $rowsParams['page_size'] - 1;

        if (Log::isLevel(Log::LV_DETAILED)) {
            $scan_count = min($rowsParams['row_count'], $end);
            Log::info('ENGINE EV TABLE ' . str_pad(Log::v2str($rowsParams['table']), 50, '_', STR_PAD_RIGHT) .
                '[PAGE:' . str_pad($rowsParams['page'], 4, " ", STR_PAD_LEFT) . ']' .
                '[START:' . str_pad($start, 6, " ", STR_PAD_LEFT) .
                '[OFFSET:' . str_pad(Log::v2str($rowsParams['lastOffset']), 8, " ", STR_PAD_LEFT) . ']' .
                '[OF:' . str_pad($scan_count, 6, " ", STR_PAD_LEFT) . ']', Log::LV_DETAILED);
        }

        $data = SnapDB::selectUsingPrimaryKeyAsOffset(
            $dbh,
            "SELECT " . $rowsParams['colList'] . " FROM `" . $rowsParams['table'] . "` WHERE 1",
            $rowsParams['table'],
            $rowsParams['lastOffset'],
            $rowsParams['page_size'],
            $rowsParams['lastOffset'],
            array('DUPX_DB', 'query_log_callback')
        );

        if ($data === false) {
            $errMsg                      = mysqli_error($dbh);
            $s3Funcs->report['errsql'][] = $errMsg;
            $nManager->addFinalReportNotice(array(
                'shortMsg' => 'DATA-REPLACE ERRORS: MySQL',
                'level'    => DUPX_NOTICE_ITEM::SOFT_WARNING,
                'longMsg'  => $errMsg,
                'sections' => 'search_replace'
            ));
            return false;
        } else {
            //Loops every row
            while ($row = mysqli_fetch_assoc($data)) {
                self::evaluateRow($rowsParams, $row);
            }
            @mysqli_free_result($data);

            return $rowsParams['updated'];
        }
    }

    /**
     * Return max serialize len
     *
     * @return int
     */
    private static function getMaxSerializeLen()
    {
        static $maxLen = null;
        if (is_null($maxLen)) {
            $maxLen = PrmMng::getInstance()->getValue(PrmMng::PARAM_MAX_SERIALIZE_CHECK) * 1000000;
        }
        return $maxLen;
    }

    /**
     * evaluate single row columns
     *
     * @param array $rowsParams
     * @param array $row
     *
     * @return boolean true if row is modified and updated
     */
    private static function evaluateRow(&$rowsParams, $row)
    {
        $nManager             = DUPX_NOTICE_MANAGER::getInstance();
        $s3Funcs              = DUPX_S3_Funcs::getInstance();
        $dbh                  = $s3Funcs->getDbConnection();
        $maxSerializeLenCheck = self::getMaxSerializeLen();

        $s3Funcs->report['scan_rows']++;
        $rowsParams['current_row']++;

        $upd_col    = array();
        $upd_sql    = array();
        $where_sql  = array();
        $upd        = false;
        $serial_err = false;
        $is_unkeyed = !in_array(true, $rowsParams['columns']);

        $rowErrors = array();

        //Loops every cell
        foreach ($rowsParams['columns'] as $column => $primary_key) {
            $s3Funcs->report['scan_cells']++;
            if (!isset($row[$column])) {
                continue;
            }

            $safe_column     = '`' . mysqli_real_escape_string($dbh, $column) . '`';
            $edited_data     = $originalData    = $row[$column];
            $base64converted = false;
            $txt_found       = false;

            //Unkeyed table code
            //Added this here to add all columns to $where_sql
            //The if statement with $txt_found would skip additional columns -TG
            if ($is_unkeyed && !empty($originalData)) {
                $where_sql[] = $safe_column . ' = "' . mysqli_real_escape_string($dbh, $originalData) . '"';
            }

            //Only replacing string values
            if (!empty($row[$column]) && !is_numeric($row[$column]) && $primary_key != 1) {
                // get search and reaplace list for column
                $tColList        = &$rowsParams['columnsSRList'][$column]['list'];
                $tColSearchList  = &$rowsParams['columnsSRList'][$column]['sList'];
                $tColreplaceList = &$rowsParams['columnsSRList'][$column]['rList'];
                $tColExactMatch  = $rowsParams['columnsSRList'][$column]['exactMatch'];

                // skip empty search col
                if (empty($tColSearchList)) {
                    continue;
                }

                // Search strings in data
                foreach ($tColList as $item) {
                    if (preg_match($item['search'], $edited_data)) {
                        $txt_found = true;
                        break;
                    }
                }

                if (!$txt_found) {
                    //if not found decetc Base 64
                    if (($decoded = DUPX_U::is_base64($row[$column])) !== false) {
                        $edited_data     = $decoded;
                        $base64converted = true;

                        // Search strings in data decoded
                        foreach ($tColList as $item) {
                            if (preg_match($item['search'], $edited_data)) {
                                $txt_found = true;
                                break;
                            }
                        }
                    }

                    //Skip table cell if match not found
                    if (!$txt_found) {
                        continue;
                    }
                }

                // 0 no limit
                if ($maxSerializeLenCheck > 0 && self::is_serialized_string($edited_data) && strlen($edited_data) > $maxSerializeLenCheck) {
                    $serial_err          = true;
                    $substrData          = substr($edited_data, 0, Log::isLevel(Log::LV_DEBUG) ? 20000 : 1000);
                    $rowErrors[$column]  = 'ENGINE: serialize data too big to convert; data len:' . strlen($edited_data) . ' Max size:' . $maxSerializeLenCheck;
                    $rowErrors[$column] .= "\n\tDATA: " . $substrData;
                    continue;
                }

                //Replace logic - level 1: simple check on any string or serlized strings
                if ($tColExactMatch) {
                    // if is exact match search and replace the itentical string
                    if (($rIndex = array_search($edited_data, $tColSearchList)) !== false) {
                        Log::info("ColExactMatch " . $column . ' search:' . $edited_data . ' replace:' . $tColreplaceList[$rIndex] . ' index:' . $rIndex, Log::LV_DEBUG);
                        $edited_data = $tColreplaceList[$rIndex];
                    }
                    continue;
                }

                // SEARCH AND REPLACE
                $edited_data = self::searchAndReplaceItems($tColSearchList, $tColreplaceList, $edited_data);

                // Replace logic - level 2: repair serialized strings that have become broken
                // check value without unserialize it
                if (self::is_serialized_string($edited_data)) {
                    $serial_check = self::fixSerializedAndCheck($edited_data);
                    if ($serial_check['fixed']) {
                        $edited_data = $serial_check['data'];
                    } else {
                        $substrData = substr($edited_data, 0, Log::isLevel(Log::LV_DEBUG) ? 20000 : 1000);
                        $message    = 'ENGINE: serialize data serial check error' .
                            "\n\tDATA: " . $substrData;

                        Log::info($message);
                        $serial_err         = true;
                        $rowErrors[$column] = $message;
                    }
                }
            }

            //Base 64 encode
            if ($base64converted) {
                $edited_data = base64_encode($edited_data);
            }

            //Change was made
            if ($serial_err == false && $edited_data != $originalData) {
                $s3Funcs->report['updt_cells']++;
                $upd_col[] = $safe_column;
                $upd_sql[] = $safe_column . ' = "' . mysqli_real_escape_string($dbh, $edited_data) . '"';
                $upd       = true;
            }

            if ($primary_key) {
                $where_sql[] = $safe_column . ' = "' . mysqli_real_escape_string($dbh, $originalData) . '"';
            }
        }

        foreach ($rowErrors as $errCol => $msgCol) {
            $longMsg                     = $msgCol . "\n\tTABLE:" . $rowsParams['table'] . ' COLUMN: ' . $errCol . ' WHERE: ' . implode(' AND ', array_filter($where_sql));
            $s3Funcs->report['errser'][] = $longMsg;
            $nManager->addFinalReportNotice(array(
                'shortMsg'    => 'DATA-REPLACE ERROR: Serialization',
                'level'       => DUPX_NOTICE_ITEM::SOFT_WARNING,
                'longMsg'     => $longMsg,
                'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
                'sections'    => 'search_replace'
            ));
        }

        //PERFORM ROW UPDATE
        if ($upd && !empty($where_sql)) {
            $sql    = "UPDATE `{$rowsParams['table']}` SET " . implode(', ', $upd_sql) . ' WHERE ' . implode(' AND ', array_filter($where_sql));
            $result = DUPX_DB::mysqli_query($dbh, $sql);

            if ($result) {
                $s3Funcs->report['updt_rows']++;
                $rowsParams['updated'] = true;
            } else {
                $errMsg                      = mysqli_error($dbh) . "\n\tTABLE:" . $rowsParams['table'] . ' WHERE: ' . implode(' AND ', array_filter($where_sql));
                $s3Funcs->report['errsql'][] = 'DB ERROR: ' . $errMsg . (Log::isLevel(Log::LV_DETAILED) ? "\nSQL: [{$sql}]\n" : '');
                $nManager->addFinalReportNotice(array(
                    'shortMsg'    => 'DATA-REPLACE ERRORS: MySQL',
                    'level'       => DUPX_NOTICE_ITEM::SOFT_WARNING,
                    'longMsg'     => $errMsg,
                    'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
                    'sections'    => 'search_replace'
                ));
            }
        } elseif ($upd) {
            $errMsg                      = sprintf("Row [%s] on Table [%s] requires a manual update.", $rowsParams['current_row'], $rowsParams['table']);
            $s3Funcs->report['errkey'][] = $errMsg;

            $nManager->addFinalReportNotice(array(
                'shortMsg' => 'DATA-REPLACE ERROR: Key',
                'level'    => DUPX_NOTICE_ITEM::SOFT_WARNING,
                'longMsg'  => $errMsg,
                'sections' => 'search_replace'
            ));
        }

        return $rowsParams['updated'];
    }

    private static function getColumnsSearchReplaceList($table, $columns)
    {
        // PREPARE SEARCH AN REPLACE LISF FOR TABLES
        $srManager   = DUPX_S_R_MANAGER::getInstance();
        $searchList  = array();
        $replaceList = array();
        $list        = $srManager->getSearchReplaceList($table);
        foreach ($list as $item) {
            $searchList[]  = $item['search'];
            $replaceList[] = $item['replace'];
        }

        $columnsSRList = array();
        foreach ($columns as $column => $primary_key) {
            if (($cScope = self::getSearchReplaceCustomScope($table, $column)) === false) {
                // if don't have custom scope get normal search and reaplce table list
                $columnsSRList[$column] = array(
                    'list'       => &$list,
                    'sList'      => &$searchList,
                    'rList'      => &$replaceList,
                    'exactMatch' => false
                );
            } else {
                // if column have custom scope overvrite default table search/replace list
                $columnsSRList[$column] = array(
                    'list'       => $srManager->getSearchReplaceList($cScope, true, false),
                    'sList'      => array(),
                    'rList'      => array(),
                    'exactMatch' => self::isExactMatch($table, $column)
                );
                foreach ($columnsSRList[$column]['list'] as $item) {
                    $columnsSRList[$column]['sList'][] = $item['search'];
                    $columnsSRList[$column]['rList'][] = $item['replace'];
                }
            }
        }

        return $columnsSRList;
    }

    /**
     * searches and replaces strings without deserializing
     * recursion for arrays
     *
     * @param array $search
     * @param array $replace
     * @param mixed $data
     *
     * @return mixed
     */
    public static function searchAndReplaceItems($search, $replace, $data)
    {
        if (empty($data) || is_numeric($data) || is_bool($data) || is_callable($data)) {
            /* do nothing */
        } elseif (is_string($data)) {
            foreach ($search as $index => $cs) {
                //  Multiple replace string. If the string is serialized will fixed with fixSerialString
                $data = preg_replace($cs, $replace[$index], $data);
            }
        } elseif (is_array($data)) {
            $_tmp = array();
            foreach ($data as $key => $value) {
                // prevent recursion overhead
                if (empty($value) || is_numeric($value) || is_bool($value) || is_callable($value) || is_object($data)) {
                    $_tmp[$key] = $value;
                } else {
                    $_tmp[$key] = self::searchAndReplaceItems($search, $replace, $value, false);
                }
            }

            $data = $_tmp;
            unset($_tmp);
        } elseif (is_object($data)) {
            // it can never be an object type
            Log::info("OBJECT DATA IMPOSSIBLE\n");
        }
        return $data;
    }

    /**
     * FROM WORDPRESS
     * Check value to find if it was serialized.
     *
     * If $data is not an string, then returned value will always be false.
     * Serialized data is always a string.
     *
     * @since 2.0.5
     *
     * @param string $data   Value to check to see if was serialized.
     * @param bool   $strict Optional. Whether to be strict about the end of the string. Default true.
     * @return bool False if not serialized and true if it was.
     */
    public static function is_serialized_string($data, $strict = true)
    {
        // if it isn't a string, it isn't serialized.
        if (!is_string($data)) {
            return false;
        }
        $data = trim($data);
        if ('N;' == $data) {
            return true;
        }
        if (strlen($data) < 4) {
            return false;
        }
        if (':' !== $data[1]) {
            return false;
        }
        if ($strict) {
            $lastc = substr($data, -1);
            if (';' !== $lastc && '}' !== $lastc) {
                return false;
            }
        } else {
            $semicolon = strpos($data, ';');
            $brace     = strpos($data, '}');
            // Either ; or } must exist.
            if (false === $semicolon && false === $brace) {
                return false;
            }
            // But neither must be in the first X characters.
            if (false !== $semicolon && $semicolon < 3) {
                return false;
            }
            if (false !== $brace && $brace < 4) {
                return false;
            }
        }
        $token = $data[0];
        switch ($token) {
            case 's':
                if ($strict) {
                    if ('"' !== substr($data, -2, 1)) {
                        return false;
                    }
                } elseif (false === strpos($data, '"')) {
                    return false;
                }
            // or else fall through
            case 'a':
            case 'O':
                return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
            case 'b':
            case 'i':
            case 'd':
                $end = $strict ? '$' : '';
                return (bool) preg_match("/^{$token}:[0-9.E-]+;$end/", $data);
        }
        return false;
    }

    /**
     * Test if a string in properly serialized
     *
     * @param string $data Any string type
     *
     * @todo The serialized string fix handles layers recursively in case there is a serialization of a serialized string.
     *       As an example if an array is serialized that contains to its turn the list of serialized strings.
     *       For this reason in order to make a working test in all the cases we should recursively test all the values of the serialized object.
     *       Note: since the fix works well I'm fine with running a superficial test like this given the implementation effort to improve it.
     *
     * @return bool Is the string a serialized string
     */
    public static function unserializeTest($data)
    {
        if (!is_string($data)) {
            return false;
        } elseif ($data === 'b:0;') {
            return true;
        } else {
            try {
                LogHandler::setMode(LogHandler::MODE_OFF);
                $unserialize_ret = @unserialize($data);
                LogHandler::setMode();
                return ($unserialize_ret !== false);
            } catch (Exception $e) {
                Log::info("Unserialize exception: " . $e->getMessage());
                //DEBUG ONLY:
                Log::info("Serialized data\n" . $data, Log::LV_DEBUG);
                return false;
            }
        }
    }
    /**
     * custom columns list
     * if the table / column pair exists in this array then the search scope will be overwritten with that contained in the array
     *
     * @var array
     */
    private static $customScopes = array(
        'signups' => array(
            'domain' => array(
                'scope' => 'domain_host',
                'exact' => true
            ),
            'path'   => array(
                'scope' => 'domain_path',
                'exact' => true
            )
        ),
        'site'    => array(
            'domain' => array(
                'scope' => 'domain_host',
                'exact' => true
            ),
            'path'   => array(
                'scope' => 'domain_path',
                'exact' => true
            )
        )
    );

    /**
     *
     * @param string $table
     * @param string $column
     * @return boolean|string  false if custom scope not found or return custom scoper for table/column
     */
    private static function getSearchReplaceCustomScope($table, $column)
    {
        $tablePrefix = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);
        if (strpos($table, $tablePrefix) !== 0) {
            return false;
        }

        $table_key = substr($table, strlen($tablePrefix));

        if (!array_key_exists($table_key, self::$customScopes)) {
            return false;
        }

        if (!array_key_exists($column, self::$customScopes[$table_key])) {
            return false;
        }

        return self::$customScopes[$table_key][$column]['scope'];
    }

    /**
     *
     * @param string $table
     * @param string $column
     * @return boolean if true search a exact match in column if false search as LIKE
     */
    private static function isExactMatch($table, $column)
    {
        $tablePrefix = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);

        if (strpos($table, $tablePrefix) !== 0) {
            return false;
        }

        $table_key = substr($table, strlen($tablePrefix));

        if (!array_key_exists($table_key, self::$customScopes)) {
            return false;
        }

        if (!array_key_exists($column, self::$customScopes[$table_key])) {
            return false;
        }

        return self::$customScopes[$table_key][$column]['exact'];
    }

    /**
     *  Fixes the string length of a string object that has been serialized but the length is broken
     *
     * @param string $data The string object to recalculate the size on.
     *
     * @return string  A serialized string that fixes and string length types
     */
    private static function fixSerializedAndCheck($data)
    {
        $result = array(
            'data'  => null,
            'fixed' => false,
        );

        $serialized_fixed = self::recursiveFixSerialString($data);
        if (self::unserializeTest($serialized_fixed)) {
            $result['data']  = $serialized_fixed;
            $result['fixed'] = true;
        } else {
            $result['fixed'] = false;
        }

        return $result;
    }

    /**
     *  Fixes the string length of a string object that has been serialized but the length is broken
     *  Work on nested serialized string recursively.
     *
     *  @param string $data The string ojbect to recalculate the size on.
     *
     *  @return string  A serialized string that fixes and string length types
     */
    private static function recursiveFixSerialString($data)
    {

        if (!self::is_serialized_string($data)) {
            return $data;
        }

        $result  = '';
        $matches = null;

        $openLevel     = 0;
        $openContent   = '';
        $openContentL2 = '';

        // parse every char
        for ($i = 0; $i < strlen($data); $i++) {
            $cChar = $data[$i];

            $addChar = true;

            if ($cChar == 's') {
                // test if is a open string
                if (preg_match(self::SERIALIZE_OPEN_STR_REGEX, substr($data, $i, self::SERIALIZE_OPEN_SUBSTR_LEN), $matches)) {
                    if ($openLevel > 1) {
                        $openContentL2 .= $matches[0];
                    }

                    $addChar = false;

                    $openLevel++;

                    $i += strlen($matches[0]) - 1;
                }
            } elseif ($openLevel > 0 && $cChar == '"') {
                // test if is a close string
                if (preg_match(self::SERIALIZE_CLOSE_STR_REGEX, substr($data, $i, self::SERIALIZE_CLOSE_SUBSTR_LEN))) {
                    $addChar = false;

                    switch ($openLevel) {
                        case 1:
                            // level 1
                            // flush string content
                            $result     .= 's:' . strlen($openContent) . ':"' . $openContent . '";';
                            $openContent = '';
                            break;
                        case 2:
                            // level 2
                            // fix serial string level2
                            $sublevelstr = self::recursiveFixSerialString($openContentL2);
                            // flush content on level 1
                            $openContent  .= 's:' . strlen($sublevelstr) . ':"' . $sublevelstr . '";';
                            $openContentL2 = '';
                            break;
                        default:
                            // level > 2
                            // keep writing at level 2; it will be corrected with recursion
                            $openContentL2 .= self::SERIALIZE_CLOSE_STR;
                            break;
                    }

                    $openLevel--;
                    $i += self::SERIALIZE_CLOSE_STR_LEN - 1;
                }
            }

            if ($addChar) {
                switch ($openLevel) {
                    case 0:
                        // level 0
                        // add char on result
                        $result .= $cChar;

                        break;
                    case 1:
                        // level 1
                        // add char on content level1
                        $openContent .= $cChar;

                        break;
                    default:
                        // level > 1
                        // add char on content level2
                        $openContentL2 .= $cChar;

                        break;
                }
            }
        }

        return $result;
    }

    /**
     *
     * @param object $dbh
     * @param string $table
     * @param string $column
     * @param string $oldPrefix
     * @param string $newPrefix
     *
     * @return boolean
     */
    public static function updateTablePrefix($dbh, $table, $column, $oldPrefix, $newPrefix)
    {
        if ($oldPrefix === $newPrefix) {
            return true;
        }

        Log::info('UPDATE KEYS PREFIX ON ' . $table . ' COLUMN ' . $column . ' FROM ' . $oldPrefix . ' TO ' . $newPrefix);

        $lenOldPrefix          = strlen($oldPrefix) + 1;
        $regexOldTablePrefix   = mysqli_real_escape_string($dbh, SnapDB::quoteRegex($oldPrefix));
        $escapedNewTablePrefix = mysqli_real_escape_string($dbh, $newPrefix);

        // remove old prefix values if exists
        $sql = 'DELETE FROM ' . $table . ' WHERE ' . $column . ' IN ('
            . 'SELECT CONCAT(\'' . $escapedNewTablePrefix . '\',SUBSTRING(' . $column . ' , ' . $lenOldPrefix . ')) '
            . 'FROM (SELECT * FROM ' . $table . ' WHERE `' . $column . '` REGEXP \'^' . $regexOldTablePrefix . '\') selectTableAlias'
            . ')';

        if (DUPX_DB::mysqli_query($dbh, $sql) === false) {
            $nManager = DUPX_NOTICE_MANAGER::getInstance();
            $s3Funcs  = DUPX_S3_Funcs::getInstance();
            $errMsg   = 'Query error on table prefix user meta ' . "\n\t" . mysqli_error($dbh);

            $s3Funcs->report['errsql'][] = 'DB ERROR: ' . $errMsg . (Log::isLevel(Log::LV_DETAILED) ? "\nSQL: [{$sql}]\n" : '');
            $nManager->addFinalReportNotice(array(
                'shortMsg'    => 'UPDATE PREFIX TABLE ERROR',
                'level'       => DUPX_NOTICE_ITEM::CRITICAL,
                'longMsg'     => $errMsg,
                'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
                'sections'    => 'search_replace'
            ));
            return false;
        }

        /// rename table prefix value
        $sql = 'UPDATE ' . $table . ' SET ' . $column . ' = CONCAT(\'' . $escapedNewTablePrefix . '\',SUBSTRING(' . $column . ' , ' . $lenOldPrefix . ')) WHERE `' . $column . '` REGEXP \'^' . $regexOldTablePrefix . '\'';
        if (DUPX_DB::mysqli_query($dbh, $sql) === false) {
            $nManager = DUPX_NOTICE_MANAGER::getInstance();
            $s3Funcs  = DUPX_S3_Funcs::getInstance();
            $errMsg   = 'Query error on table prefix user meta ' . "\n\t" . mysqli_error($dbh);

            $s3Funcs->report['errsql'][] = 'DB ERROR: ' . $errMsg . (Log::isLevel(Log::LV_DETAILED) ? "\nSQL: [{$sql}]\n" : '');
            $nManager->addFinalReportNotice(array(
                'shortMsg'    => 'UPDATE PREFIX TABLE ERROR',
                'level'       => DUPX_NOTICE_ITEM::CRITICAL,
                'longMsg'     => $errMsg,
                'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
                'sections'    => 'search_replace'
            ));
            return false;
        }
        return true;
    }

    public static function updateTablePrefixKeys()
    {
        if (!DUPX_ArchiveConfig::getInstance()->isTablePrefixChanged()) {
            return true;
        }

        Log::info("\nUPDATE PREFIX KEY TABLES");

        $nManager      = DUPX_NOTICE_MANAGER::getInstance();
        $s3Funcs       = DUPX_S3_Funcs::getInstance();
        $paramsManager = PrmMng::getInstance();
        $archiveConfig = DUPX_ArchiveConfig::getInstance();

        $dbh = $s3Funcs->getDbConnection();

        $oldPrefix    = DUPX_ArchiveConfig::getInstance()->wp_tableprefix;
        $newPrefix    = $paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);
        $optionsTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getOptionsTableName());

        $tables = DUPX_DB_Tables::getInstance()->getNewTablesNames();
        if (!is_array($tables)) {
            $nManager->addFinalReportNotice(array(
                'shortMsg'    => 'CAN\'T FIND ANY TABLE IN DATABASE',
                'level'       => DUPX_NOTICE_ITEM::CRITICAL,
                'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
                'sections'    => 'search_replace'
            ));
            return false;
        }

        if (in_array($optionsTable, $tables)) {
            Log::info('UPDATE PREFIX IN TABLE ' . Log::v2str($optionsTable) . ' FROM ' . $oldPrefix . ' TO ' . $newPrefix);
            self::updateTablePrefix($dbh, $optionsTable, 'option_name', $oldPrefix, $newPrefix);
        } else {
            Log::info('CAN\'T UPDATE PREFIX IN TABLE ' . Log::v2str($optionsTable) . ' BACAUSE TABLE NOT IN LIST', Log::LV_DETAILED);
        }

        $usermetaTable = DUPX_DB_Functions::getUserMetaTableName();
        if (in_array($usermetaTable, $tables)) {
            Log::info('UPDATE PREFIX IN TABLE ' . Log::v2str($usermetaTable) . ' FROM ' . $oldPrefix . ' TO ' . $newPrefix);
            self::updateTablePrefix($dbh, $usermetaTable, 'meta_key', $oldPrefix, $newPrefix);
        } else {
            Log::info('CAN\'T UPDATE PREFIX IN TABLE ' . Log::v2str($usermetaTable) . ' BACAUSE TABLE NOT IN LIST', Log::LV_DETAILED);
        }

        return true;
    }
}